---
name: cse-linear-jsp
description: Manage Top-100 Joint Success Plans in Linear (one project per account under the FY27 initiative) including Salesforce account sync. Use when the user asks to sync JSP seeds to Linear, sync Salesforce into Linear, update a JSP project, move JSP stages, add JSP milestones, or list JSP status.
argument-hint: "[sync | sf-sync | list | get <account> | status <account> <stage> | milestone <account>]"
---

# CSE Linear JSP

Manage the Top-100 Joint Success Plan motion in Linear: one Linear **project per
top-100 account** under the initiative **Top 100 — Joint Success Plans (FY27)**
(workspace `postman`, team `POS`). Project content is the account's JSP seed;
Salesforce account data syncs into a managed block; selected outcome milestones
become Linear project milestones. The Jira CSE board stays the internal system
of record — Linear is the external/customer-facing JSP tracker.

## Compact MCP routing

This workflow is CLI-primary. Follow the shared [compact MCP routing contract](../../shared/compact-mcp-routing.md). Interactive facade tools are `cse_capabilities`, `cse_read`, `cse_apply`, `context_assemble`, and `cse_session_info`; named operations are capability ids. For MCP fallback reads, call `cse_read` with the capability id. Every MCP mutation goes through `cse_apply` twice: dry-run first, then the identical capability and arguments with `execute:true`, justification, and the returned `preview_digest`. The `get_salesforce_account` capability remains available directly on `/mcp/full` for the Salesforce fallback below. Never route Linear CRUD through MCP. Call `context_assemble` and `cse_session_info` directly when context or session diagnosis is required.
## Auth

The helper resolves a Linear personal API key automatically, in this exact
order:

1. Non-empty `process.env.LINEAR_API_KEY`.
2. macOS keychain service `cse-linear-api-key`.
3. A safely parsed `~/.zshrc` assignment: `export LINEAR_API_KEY=...` or
   `LINEAR_API_KEY=...`. The helper reads matching lines only; it never sources
   or executes `.zshrc`.

When the key exists only in `.zshrc`, use it for the current helper run. Never
print it. If all three sources are missing, follow this agent UX exactly:

1. Ask the operator for a Linear personal API key from
   linear.app → Settings → API.
2. Append `export LINEAR_API_KEY='<key>'` to `~/.zshrc`, creating the file if
   needed. Do not echo the value to output or put it in a tracked file.
3. Optionally also store it with
   `security add-generic-password -a "$USER" -s cse-linear-api-key -w '<key>' -U`.
4. Re-run the helper command.

Linear auth belongs only to this Linear workflow; do not add it to MCP config.

## Helper

Use the bundled helper as the primary interface for Linear and Salesforce
(Node ≥ 18, zero deps):

```sh
node "$SKILL_DIR/scripts/linear-jsp.mjs" <command> [args]
```

| Command | Purpose |
|---|---|
| `whoami` | Verify key: viewer + org + JSP initiative resolution |
| `list` | All JSP projects under the initiative (stage, name, url) |
| `get <account>` | One project incl. full content + milestones |
| `sync <seed-dir> [--account <name> --confirm-overwrite] [--dry-run]` | Create/update seed-stage projects; live-stage content requires explicit account selection and overwrite confirmation |
| `sf-sync --target-org <alias-or-username> [--account <name>] [--confirm-sf-id <id>] [--allow-partial]` | Verify the pinned SF org, pull account data, and write the managed block + description; unresolved/drift exits nonzero unless partial success is explicitly allowed |
| `customers` | Create a Linear Customer per mapped account (externalId = SF AccountId). Requires the Customers feature enabled on the workspace — currently plan-gated off; command no-ops with errors until it is turned on |
| `update <account> --file <md>` | Replace a project's content from a markdown file |
| `status <account> <stage>` | Move a project between JSP stages |
| `milestone <account> <name> [--date YYYY-MM-DD] [--desc <text>]` | Add a project milestone |

`<account>` matches case-insensitively against the project name with the
`JSP — ` prefix stripped.

The helper's sync path is only the operator's authenticated `sf` CLI and requires
an explicit `--target-org` alias/username (or `SF_TARGET_ORG`); it verifies the
resolved org identity before querying. If CLI sync is unavailable, MCP
`get_salesforce_account` with arguments `{"account_id":"<crm_id>"}` may be used only for separate,
read-only inspection. MCP-fetched records are not accepted as helper sync input.

### Human prose

Linear-facing text stays plain ASCII for decorative typography: no middle dots,
em/en dashes, smart quotes, or fancy bullets. Missing commercial values render
as `ARR not recorded` / `n/a` (not an em dash). Salesforce Ids must be exactly
15 or 18 alphanumeric characters. Automation that heals existing Linear content
lives in the linear-sync repo (`jsp-humanize-backfill`); this skill only emits
clean text on write.

## JSP stages (workspace project statuses)

Seed → Discovery → Milestones Agreed → In Flight → Value Delivered, plus
Dormant for paused accounts. Move projects with `status <account> <stage>`.

## Salesforce sync mechanics

- Account resolution: a unique exact name match may pin automatically. LIKE
  results are printed as candidates and never saved or synced until the operator
  selects one with `--account <name> --confirm-sf-id <id>`; ARR ordering is display
  order only. Resolved `AccountId`s pin into `~/.cse-tools/linear-sf-map.json` so
  later runs are deterministic. Fix wrong matches by editing the map's
  `aliases` (seed name → SF account name) and deleting the bad `accounts` row.
- The SF block is a `## Salesforce Snapshot` section marked by an invisible
  zero-width sentinel on the heading and bounded by the end of its table
  (`scripts/lib/managed-blocks.mjs`), replaced in place on re-sync — human
  edits outside the section are never touched. Legacy `<!-- /sf-snapshot -->`
  tails and `<!-- SF-SYNC:BEGIN/END -->` fences are migrated away on the next
  sync.
- A daily LaunchAgent (`com.postman.cse.linear-sf-sync`) re-runs `sf-sync` at
  07:00 local with a configured target org; logs land in `~/.cse-tools/logs/`.
- Unresolved or drift-flagged accounts preserve the last-known-good managed block
  and description, are reported stale, and make the run fail. `--allow-partial`
  permits other accounts to succeed but does not clear stale data. This helper has
  no snapshot-clearing path; clearing content and description requires a separate
  explicit operator edit.

## Workflow rules

- **Seeds are authoritative only in Seed (or legacy Planned).** Blanket sync skips
  every project beyond Seed. Updating live project content requires both
  `sync --account <name>` and `--confirm-overwrite`; either alone is refused.
- **Milestones follow the JSP spec:** 5-7 outcome milestones per account, each
  with the value delivered — not task lists. Author milestone descriptions per
  [`prose-voice.md`](../../shared/prose-voice.md).
- **Never** echo the API key into logs, MCP manifests, or committed files.
- Linear rate limit ~1,500 req/h; the helper backs off on 429, so full-100
  runs are safe but take a couple of minutes.
