@mgiles/perk 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extension/adapters/planAdapterPlannotator.ts +64 -1
- package/extension/doors/address.ts +3 -3
- package/extension/doors/commitCompact.ts +163 -0
- package/extension/doors/learn.ts +219 -23
- package/extension/doors/prReview.ts +189 -18
- package/extension/doors/prReviewDynamic.ts +249 -0
- package/extension/doors/submit.ts +4 -3
- package/extension/factories/gistAuthor.ts +94 -0
- package/extension/factories/gistDraft.ts +265 -0
- package/extension/factories/gistSave.ts +251 -0
- package/extension/factories/objectivePlan.ts +3 -2
- package/extension/factories/planMode.ts +8 -5
- package/extension/factories/planReview.ts +233 -12
- package/extension/index.ts +26 -0
- package/extension/substrate/config.ts +8 -4
- package/extension/substrate/git.ts +38 -0
- package/extension/substrate/terminalLaunch.ts +1 -1
- package/extension/substrate/toolGating.ts +44 -3
- package/extension/substrate/unifiedDiff.ts +224 -0
- package/extension/waves/learnWave.ts +155 -0
- package/extension/waves/memoryAdapter.ts +126 -0
- package/extension/waves/prReviewDynamicWave.ts +466 -0
- package/extension/waves/prReviewWave.ts +229 -0
- package/extension/waves/reportWave.ts +449 -0
- package/extension/waves/rpcAdapter.ts +201 -0
- package/package.json +7 -1
- package/prompts/_fixtures/live.yaml +22 -11
- package/prompts/commit-and-compact.md +7 -0
- package/prompts/common/output-schemas/objective-explorer.md +36 -0
- package/prompts/common/output-schemas/review-classifier.md +47 -0
- package/prompts/contexts/adapters/plannotator-objective.md +8 -1
- package/prompts/contexts/adapters/plannotator-plan.md +6 -1
- package/prompts/contexts/gist-authoring.md +22 -0
- package/prompts/stages/address/action.md +15 -4
- package/prompts/stages/address/preview.md +14 -3
- package/prompts/stages/conflict-resolution.md +1 -1
- package/prompts/stages/gist-author/seed.md +10 -0
- package/prompts/stages/gist-save.md +9 -0
- package/prompts/stages/learn-orchestrate.md +7 -5
- package/prompts/stages/objective-plan/guidance.md +12 -1
- package/prompts/stages/objective-plan/seed.md +12 -1
- package/prompts/stages/pr-review-browser/active.md +11 -3
- package/prompts/stages/pr-review-browser/foreign.md +11 -3
- package/prompts/stages/pr-review-dynamic.md +7 -0
- package/prompts/stages/pr-review-terminal/active.md +11 -3
- package/prompts/stages/pr-review-terminal/foreign.md +11 -3
- package/prompts/stages/pr-review.md +7 -6
- package/shared/bindings.yaml +6 -0
- package/shared/contracts.md +221 -45
- package/shared/registry.yaml +31 -1
package/shared/contracts.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# perk cross-plane contracts
|
|
2
2
|
|
|
3
3
|
The language-neutral contracts both planes obey, authored once here and bundled into each
|
|
4
|
-
build artifact. This document holds the numbered **prose contract sections** (`§8.1`–`§8.
|
|
4
|
+
build artifact. This document holds the numbered **prose contract sections** (`§8.1`–`§8.41`,
|
|
5
5
|
non-contiguous: `§8.8` is skipped and `§8.6a` exists; no parser): the Python CLI (`perk`)
|
|
6
6
|
and the TS extension (`@mgiles/perk`) each implement one side, against the exact names/paths/
|
|
7
7
|
fields pinned in each section. `perk doctor` verifies conformance. The numbering convention:
|
|
@@ -336,7 +336,7 @@ end of the section).
|
|
|
336
336
|
| `active_plan_ref` | object \| null | the provider-agnostic plan ref (§8.4); null during early `plan` |
|
|
337
337
|
| `active_objective` | string \| null | the active objective id (`/objective <id>` sets it, `/objective clear` nulls it) |
|
|
338
338
|
| `last_review_batch` | object \| null | the last processed review batch: `{ pr, counts:{actionable,informational,praise,question}, resolved_thread_ids:[…], at:ISO }` |
|
|
339
|
-
| `last_pr_review` | object \| null | the last `/pr-review` outcome posted via the warm `post_pr_review` tool: `{ pr, verdict, angles, comment_count, mode, at:ISO }`; best-effort tier (the PR review is the canonical record) |
|
|
339
|
+
| `last_pr_review` | object \| null | the last `/pr-review` (or the experimental `/pr-review-dynamic`) outcome posted via the shared warm `post_pr_review` tool: `{ pr, verdict, angles, comment_count, mode, at:ISO }`; best-effort tier (the PR review is the canonical record) |
|
|
340
340
|
| `last_review` | object \| null | the last review-door outcome posted via the warm `submit_pr_review` tool: `{ pr, event, comment_count, mode, at:ISO }`; best-effort tier (the submitted PR review is the canonical record) |
|
|
341
341
|
| `session_artifacts` | object \| null | per-name session-artifact provenance pointers `{run_id, name, path, digest, at}` (§8.1); appends carry the **whole merged map** (per-field LWW); strict-append tier |
|
|
342
342
|
| `objective_node_claim` | object \| null | the objective node this session has claimed `planning` (`{ objective, node }`); written by the warm `objective_node` tool on a successful `planning` transition, cleared on a successful non-planning transition for the same node and after a successful node-linked plan save; best-effort tier (cheaply reconstructable; loud-but-non-fatal) |
|
|
@@ -386,9 +386,12 @@ swallowed**: a failed advance shows a visible `⚠ … NOT advanced — re-run /
|
|
|
386
386
|
**Tool gating.** The `mode` field **structurally gates tools** — enforcement, not prompting. When
|
|
387
387
|
`mode == "read-only"` the interior (`extension/substrate/toolGating.ts`): (1) restricts the
|
|
388
388
|
active tool set to `READ_ONLY_TOOLS` (`read`/`grep`/`find`/`ls`/`bash` + `ask_user_question` +
|
|
389
|
-
`plan_review` + the `plan_draft`/`objective_draft` session-data carve-outs + `objective_node`
|
|
389
|
+
`plan_review` + the `plan_draft`/`objective_draft`/`gist_draft` session-data carve-outs + `objective_node`
|
|
390
390
|
(delegates a bounded node transition to the canonical Python plane) + the **`web` seam**
|
|
391
|
-
providers' research tools, the read-only Linear tools,
|
|
391
|
+
providers' research tools, the read-only Linear tools, the pi-fff search family (both mode
|
|
392
|
+
name-sets — `fffind`/`ffgrep`/`fff-multi-grep` + override's `multi_grep`; the override names
|
|
393
|
+
`find`/`grep` are already present — local search belongs in read-only exploration, and FFF's
|
|
394
|
+
frecency state lives under `~/.pi/agent/fff/`, outside the worktree), and the pi-subagents delegation family
|
|
392
395
|
(`subagent`/`wait` + the parent supervisor pair — the gated objective-plan explorer spawn must be
|
|
393
396
|
reachable; **accepted no-backstop posture**: spawned children are unscoped by design (§8.40
|
|
394
397
|
adopt-never-impersonates) — the explorer's agent def is write-blocked by its `tools` frontmatter,
|
|
@@ -470,10 +473,10 @@ session-lifecycle gates + the warm `/implement` handoff (`extension/doors/lifecy
|
|
|
470
473
|
adapter (`extension/factories/planMode.ts`, `extension/adapters/todoAdapterJuicesharp.ts`; §8.10
|
|
471
474
|
owns the provider seams); in-process read-only child sessions
|
|
472
475
|
(`extension/worker/readOnlySession.ts`); the read-only CI executor
|
|
473
|
-
(`extension/doors/ciExecutor.ts`); the spawned delegation seam + `/address` + `/pr-review` + `/pr-review-terminal` + `/pr-review-browser`
|
|
474
|
-
(`extension/doors/address.ts` / `prReview.ts` / `prReviewTerminal.ts` /
|
|
476
|
+
(`extension/doors/ciExecutor.ts`); the spawned delegation seam + `/address` + `/pr-review` + `/pr-review-dynamic` + `/pr-review-terminal` + `/pr-review-browser`
|
|
477
|
+
(`extension/doors/address.ts` / `prReview.ts` / `prReviewDynamic.ts` / `prReviewTerminal.ts` /
|
|
475
478
|
`prReviewBrowser.ts` / `submitPrReview.ts` / `hunkHandoff.ts` / `plannotatorHandoff.ts`, `agents/*.md`, `skills/perk-address/` /
|
|
476
|
-
`perk-pr-review/` / `perk-pr-review-terminal/` / `perk-pr-review-browser/`; the gateway op shapes stay in §8.4); the conflict-resolution drive
|
|
479
|
+
`perk-pr-review/` / `perk-pr-review-dynamic/` / `perk-pr-review-terminal/` / `perk-pr-review-browser/`; the gateway op shapes stay in §8.4); the conflict-resolution drive
|
|
477
480
|
(`extension/doors/submit.ts`; the probe contract stays in §8.4).
|
|
478
481
|
|
|
479
482
|
|
|
@@ -687,6 +690,11 @@ add_pr_reaction{ pr_number } -> void
|
|
|
687
690
|
# failure (mutations raise; nothing review-shaped is lost).
|
|
688
691
|
```
|
|
689
692
|
|
|
693
|
+
The experimental `/pr-review-dynamic` door shares `post_pr_review`/`review-post` and the clean
|
|
694
|
+
guard unchanged — angle selection is delegated to a fresh `perk.review-angle-selector` lane and
|
|
695
|
+
normalized in module-rendered code (`extension/waves/prReviewDynamicWave.ts`); the baseline
|
|
696
|
+
`/pr-review` stays canonical.
|
|
697
|
+
|
|
690
698
|
### PR-review toolbox ops (checkout / cleanup / review-submit)
|
|
691
699
|
|
|
692
700
|
The two human-in-the-loop review doors (`/pr-review-terminal`, `/pr-review-browser`) review a
|
|
@@ -797,7 +805,7 @@ waves to `POST <url>/api/external-annotations`
|
|
|
797
805
|
side: LEFT→"old" / RIGHT-or-omitted→"new", text: "[severity/confidence] …"}]}`; batches are
|
|
798
806
|
atomic; 201 returns `{ids}` — captured for cleanup). The wave cadence: on
|
|
799
807
|
`/pr-review-browser` a wave is pushed per ARRIVING fenced-JSON batch inside the streaming
|
|
800
|
-
`
|
|
808
|
+
`subagent_wait({timeoutMs})` loop (a `path`+`line` ledger dedupes — a pushed anchor is never re-pushed —
|
|
801
809
|
and the discipline is hold-and-accumulate: a refused POST before any door failure notice means
|
|
802
810
|
"not up yet", retried on the next wait-loop return, never a degrade).
|
|
803
811
|
`line: null` findings ARE pushed on this surface (path → `scope: "file"`, none →
|
|
@@ -882,19 +890,28 @@ pieces; a neutral re-home is a deferred residual).
|
|
|
882
890
|
`prompts/stages/pr-review-terminal/foreign.md` (the untrusted-foreign-code posture, the triage
|
|
883
891
|
loop, the posting contract, and the `perk pr review cleanup` step).
|
|
884
892
|
- **The streaming fan-out (foreign + active; guidance-driven — no door plumbing):** the guidance
|
|
885
|
-
spawns the 2–3 reviewers as ONE async `subagent` call
|
|
886
|
-
`async: true
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
893
|
+
spawns the 2–3 reviewers as ONE async `subagent` call in `workflowScript` mode (top-level
|
|
894
|
+
`async: true` + `context: "fresh"` — workflow-level defaults flowing to every lane; the script
|
|
895
|
+
is a single all-settled `runs.all` with one item per angle — a stable angle-slug `key`,
|
|
896
|
+
`phase`/`label` trace metadata, and a task naming the angle, the PR number, and the worktree
|
|
897
|
+
path ONLY — never the surface handle; a failed lane resolves `{key, ok: false, error}` without
|
|
898
|
+
sinking its siblings; the script returns the mapped per-lane outputs so the full reports
|
|
899
|
+
persist in the run's `status.json`), then loops `subagent_wait({ timeoutMs })` while the run
|
|
900
|
+
is active. The why: progress updates never wake `subagent_wait` and never enter pi-subagents'
|
|
901
|
+
`pending` map — delivery is an injected (now `triggerTurn`-bearing) message when a tool call
|
|
902
|
+
returns — so the timed wait loop IS the streaming cadence and the parent holds its turn open
|
|
903
|
+
(an ended turn degrades streaming to churny per-batch wake-ups instead of a held relay). Each
|
|
904
|
+
arriving fenced-JSON batch is pushed into hunk incrementally with **`path`+`line` dedupe** (an
|
|
892
905
|
in-conversation ledger; a pushed anchor is never re-pushed; hold-and-accumulate until the
|
|
893
|
-
handshake connects). On the
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
906
|
+
handshake connects). On completion — the workflow notification carries only a truncated
|
|
907
|
+
return preview, never the reports — the parent retrieves the full reports via
|
|
908
|
+
`subagent({action: "status", id})` (the per-lane step lines + the `Dir:` line) → `read`
|
|
909
|
+
`<Dir>/status.json` → `workflow.value`, reconciles from the fenced-JSON **completion
|
|
910
|
+
reports** (union + dedupe — the source of truth for triage and posting; streamed batches were
|
|
911
|
+
provisional; an `ok: false` lane is reported honestly to the human — angle + error, never
|
|
912
|
+
papered over), pushes any not-yet-pushed remainder, and — when the handshake never connected
|
|
913
|
+
— applies the unchanged check-in posture (ask, wait, degrade only on the human's explicit
|
|
914
|
+
choice).
|
|
898
915
|
- **Active mode (no PR arg):** the shared active-PR resolution ladder — `perk pr url --json` →
|
|
899
916
|
`resolveReviewTarget` with the plan-ref's pinned base. A resolved PR → the same flow re-homed
|
|
900
917
|
to the human's own worktree (`active.md`: no checkout and **no cleanup step**; the children
|
|
@@ -1023,8 +1040,9 @@ core), imported by this door and `/pr-review-terminal`'s active mode.
|
|
|
1023
1040
|
`prompts/stages/pr-review-browser/foreign.md` (the untrusted-foreign-code posture, the
|
|
1024
1041
|
`perk pr review cleanup` step).
|
|
1025
1042
|
- **The streaming fan-out (foreign + active; guidance-driven — no door plumbing):** the 2–3
|
|
1026
|
-
adversarial reviewers spawn as ONE async `subagent` call
|
|
1027
|
-
|
|
1043
|
+
adversarial reviewers spawn as ONE async `subagent` call in `workflowScript` mode and the
|
|
1044
|
+
parent holds the
|
|
1045
|
+
`subagent_wait({timeoutMs})` streaming loop, exactly as on `/pr-review-terminal` — but each arriving
|
|
1028
1046
|
fenced-JSON batch is pushed as ONE atomic wave to `POST <url>/api/external-annotations` (the
|
|
1029
1047
|
ledger dedupe + hold-and-accumulate discipline in the findings-stream block above). Children
|
|
1030
1048
|
never receive the surface handle — not the URL, not the port. Once the fan-out turn ends the
|
|
@@ -1169,11 +1187,12 @@ carries `base` from the `plan-header` so resume paths recover the pinned value.
|
|
|
1169
1187
|
|
|
1170
1188
|
**Label taxonomy (minimal):** `perk:plan` (green `1f883d`), `perk:learn` (purple `8250df`),
|
|
1171
1189
|
`perk:objective` (indigo `5319e7`, description "perk objective issue"), `perk:objective-node`
|
|
1172
|
-
(indigo `5319e7`, on Linear project-backed roadmap node-issues),
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1190
|
+
(indigo `5319e7`, on Linear project-backed roadmap node-issues), `perk:gist` (yellow `fbca04`,
|
|
1191
|
+
description "perk gist issue (a rough statement of intent)" — §8.41), and `perk:consolidated`
|
|
1192
|
+
(gray `6e7781`, description "perk learn issue consolidated into docs/learned"), each **lazily
|
|
1193
|
+
created** by its gateway create-op on first use. Query by a **single** label — GitHub label
|
|
1194
|
+
filters are AND-semantics. (On Linear, `perk init` / `doctor --fix` proactively ensure the six
|
|
1195
|
+
`perk:*` labels at **workspace** scope — §8.21.)
|
|
1177
1196
|
|
|
1178
1197
|
**The `pending-learn` semaphore.** An existence-only `cache.markers` file
|
|
1179
1198
|
(`.perk/workflow/markers/pending-learn`, name shared as `PENDING_LEARN` in both planes): **`land`
|
|
@@ -1317,13 +1336,16 @@ second `--fix` at `fixed == []`).
|
|
|
1317
1336
|
**Groups.** `environment` (tools; required tools missing = `fail`; optional tools (e.g. ast-grep)
|
|
1318
1337
|
missing = `warn`) · `github` (auth/access; non-fatal `warn`) ·
|
|
1319
1338
|
`linear` (verify-gated Linear readiness — auth/team/labels; present only when the committed
|
|
1320
|
-
`[issues] backend` is `"linear"`; warn-level, the github D3 mirror; `--fix` ensures the
|
|
1339
|
+
`[issues] backend` is `"linear"`; warn-level, the github D3 mirror; `--fix` ensures the six perk
|
|
1321
1340
|
labels — §8.21) · `runner` (remote-runner prereqs; report-only, non-fatal — §8.16) ·
|
|
1322
1341
|
`package` (settings wiring + perk-package ref reconcile + the `extension-install` install-ownership
|
|
1323
1342
|
check + the `required-perk-version` managed check over the committed `.perk/required-perk-version`
|
|
1324
1343
|
pin + the report-only `cli-version` CLI-vs-repo-pin warning (warn, never fail) + the report-only
|
|
1325
1344
|
`resource-overrides` probe over pi resource overrides that touch perk's own resources (warn, never
|
|
1326
|
-
fail, no `--fix` arm — §8.6a)
|
|
1345
|
+
fail, no `--fix` arm — §8.6a) + the report-only `subagent-compat` pi-subagents surface probe
|
|
1346
|
+
(installed version + source-file markers for the orchestration surfaces perk's guidance assumes;
|
|
1347
|
+
`info` when not installed; warn on divergence, never fail; no `--fix` arm — the package stays
|
|
1348
|
+
unpinned);
|
|
1327
1349
|
`--fix` also migrates a former git-clone consumer forward by removing the orphaned clone — §8.6a) ·
|
|
1328
1350
|
`repository` (gitignore/agents blocks + config present/valid) ·
|
|
1329
1351
|
`registry` (the registry self-check) · `skills` (the skills-CLI manifest fragment + the
|
|
@@ -1528,6 +1550,7 @@ perk's workflow skills are prompt-hidden; `transclude` exists for the user-bindi
|
|
|
1528
1550
|
| trigger | skill | mode |
|
|
1529
1551
|
|---|---|---|
|
|
1530
1552
|
| `stage:plan` | `perk-plan` | `nudge` |
|
|
1553
|
+
| `stage:gist-author` | `perk-gist-author` | `nudge` |
|
|
1531
1554
|
| `stage:objective-author` | `perk-objective-author` | `nudge` |
|
|
1532
1555
|
| `stage:objective-plan` | `perk-objective-plan` | `nudge` |
|
|
1533
1556
|
| `stage:implement` | `perk-implement` | `nudge` |
|
|
@@ -1538,6 +1561,7 @@ perk's workflow skills are prompt-hidden; `transclude` exists for the user-bindi
|
|
|
1538
1561
|
| `command:learn-docs` | `perk-learn-docs` | `nudge` |
|
|
1539
1562
|
| `command:learn-code` | `perk-learn-code` | `nudge` |
|
|
1540
1563
|
| `command:pr-review` | `perk-pr-review` | `nudge` |
|
|
1564
|
+
| `command:pr-review-dynamic` | `perk-pr-review-dynamic` | `nudge` |
|
|
1541
1565
|
| `command:pr-review-terminal` | `perk-pr-review-terminal` | `nudge` |
|
|
1542
1566
|
| `command:pr-review-browser` | `perk-pr-review-browser` | `nudge` |
|
|
1543
1567
|
| `command:skills-create` | `perk-skill-author` | `nudge` |
|
|
@@ -1847,7 +1871,11 @@ test on both planes).
|
|
|
1847
1871
|
|
|
1848
1872
|
**`perk init` two-directional settings wiring:** provider wiring composes on top of the static
|
|
1849
1873
|
`_desired_packages` (perk + `BORROWED_PACKAGES`: `npm:@tombell/pi-diff`,
|
|
1850
|
-
`npm:pi-subagents`) layer within the same `_converge_settings` body —
|
|
1874
|
+
`npm:pi-subagents`, `npm:@ff-labs/pi-fff`) layer within the same `_converge_settings` body —
|
|
1875
|
+
perk launches inject the env default `PI_FFF_MODE=override` at **both spawn sites** (local
|
|
1876
|
+
`_exec_pi`, remote `_spawn_worker`) with operator env winning by merge order, so stage sessions
|
|
1877
|
+
get FFF as `find`/`grep` while warm/bare sessions keep pi-fff's additive default mode
|
|
1878
|
+
(`fffind`/`ffgrep`) — `npm:pi-web-access` is **no
|
|
1851
1879
|
longer borrowed** (#529): it is the `web` seam's `default: true` provider, converged via the
|
|
1852
1880
|
provider path (see the web status note in contracts-history.md §8.10), so a default repo still installs it but deselecting `web`
|
|
1853
1881
|
removes it like any provider package —
|
|
@@ -1934,7 +1962,8 @@ compaction drift; `doctor --fix` reconverges). **Committed-only read** (a `local
|
|
|
1934
1962
|
table touches nothing; removing it leaves the written keys to clean up by hand (perk cannot prove
|
|
1935
1963
|
ownership of a bare settings key). Relatedly, `[models.subagents]` values are **blessed** to carry
|
|
1936
1964
|
the same `:thinking` suffix (and pi-subagents' `inherit` sentinel — child inherits the parent
|
|
1937
|
-
session's model), resolved by pi-subagents
|
|
1965
|
+
session's model), resolved by pi-subagents from the workflow-level `model` the guidance passes
|
|
1966
|
+
on the `subagent` workflowScript call; doctor's
|
|
1938
1967
|
warn-level `models` check flags suspicious suffixes (alphabetic-only last-colon segment outside
|
|
1939
1968
|
the vocabulary) across `[models].default`, `[models.subagents]` values, and
|
|
1940
1969
|
`[models.stages.<id>].model`. Resulting precedence — cold launch: explicit `perk <stage>
|
|
@@ -1965,6 +1994,19 @@ pi-subagents' `applyBuiltinOverrides` consults **before** the bulk flag and whic
|
|
|
1965
1994
|
never touches; a **user-global** `~/.pi/agent/settings.json` re-enable does **not** work (the
|
|
1966
1995
|
project bulk-disable is checked before user-scope overrides).
|
|
1967
1996
|
|
|
1997
|
+
**`tuiMode` seed (init-owned):** perk seeds `"tuiMode": "fullscreen"` into `.pi/settings.json`
|
|
1998
|
+
**write-when-absent** — the third convergence shape beside write-when-present
|
|
1999
|
+
(`[compaction]`/`[models]`) and constant-enforced (`subagents.disableBuiltins`): the key is
|
|
2000
|
+
written once, only when absent, and never overwrites a present key (any value — presence, not
|
|
2001
|
+
value, is the guard). Python-plane-only (pi consumes `settings.json` itself; the extension never
|
|
2002
|
+
reads it), composed inside `_converge_settings`
|
|
2003
|
+
(`perk/convergence/init/settings.py::_converge_tui_mode`) so it rides the `settings-wiring`
|
|
2004
|
+
`ManagedConvergence`. **Excluded** from the desired/observed managed-state settings portions: a
|
|
2005
|
+
seeded default is user-ownable after the seed — including it would misclassify an opt-out as
|
|
2006
|
+
`locally-modified`. The opt-out is committing any `tuiMode` value (e.g. `"regular"`), which
|
|
2007
|
+
survives init/doctor; a user-global `/settings` change does **not** durably override the project
|
|
2008
|
+
key (pi merges project settings over global).
|
|
2009
|
+
|
|
1968
2010
|
> **Interactive save discipline (as of Node 2.5 the present + `/plan-save` flow is
|
|
1969
2011
|
> FALLBACK-ONLY on every interactive path — perk-plan included):** the prior
|
|
1970
2012
|
> `PLAN_AUTHORING_CONTEXT` ending ("disable plan mode (/plan off), then call the plan_save
|
|
@@ -2114,13 +2156,18 @@ stream's terminal `run_finished` event (§8.12) — the same frozen object, carr
|
|
|
2114
2156
|
channel.
|
|
2115
2157
|
|
|
2116
2158
|
> **Open dependency (carried risk).** The `address` drive's seeded prompt instructs the model to
|
|
2117
|
-
>
|
|
2118
|
-
>
|
|
2119
|
-
>
|
|
2120
|
-
>
|
|
2121
|
-
>
|
|
2122
|
-
>
|
|
2123
|
-
>
|
|
2159
|
+
> run `perk.review-classifier` via ONE foreground `workflowScript` call on the borrowed
|
|
2160
|
+
> `pi-subagents` `subagent` tool (direct `{agent, task}` execution was removed upstream at 0.43).
|
|
2161
|
+
> `pi-subagents` now loads in the worker from the managed settings `packages` list (Gap 4 above).
|
|
2162
|
+
> The worker's address prompt now also injects the configured classifier model when
|
|
2163
|
+
> `[models.subagents] review-classifier` is set in the worktree's `.perk/config.toml` (#196), as a
|
|
2164
|
+
> workflow-level `model` default on that one call, byte-identical to `_address_prompt`'s parity
|
|
2165
|
+
> twin. The seeded classify call also passes a top-level `outputSchema` (rendered from the shared
|
|
2166
|
+
> template's `prompts/common/output-schemas/review-classifier.md` include) — the classifier's
|
|
2167
|
+
> report is engine-validated structured output read from the projection's `report`
|
|
2168
|
+
> (`structuredOutput`), so `ok: true` ⟺ a schema-valid report is present. The
|
|
2169
|
+
> **subagent-under-worker live smoke** stays an open dependency **deferred to the Phase-3
|
|
2170
|
+
> `doctor workflow`**; Node 1.2 does not prove it.
|
|
2124
2171
|
|
|
2125
2172
|
## §8.12 · The structured run-event stream (Node 1.3)
|
|
2126
2173
|
|
|
@@ -3009,8 +3056,9 @@ init/doctor probe — report-shaped, never raises; phases short-circuit auth →
|
|
|
3009
3056
|
remediation "export LINEAR_API_KEY (create a personal API key at linear.app Settings →
|
|
3010
3057
|
Security & access), or set [linear] api_key in .perk/local.toml".
|
|
3011
3058
|
- `linear-team` — ok: `team <key> found`; failure: warn with the error detail.
|
|
3012
|
-
- `linear-labels` — all
|
|
3013
|
-
`perk:objective`, `perk:objective-node`): ok; otherwise warn listing the missing
|
|
3059
|
+
- `linear-labels` — all six perk labels present (`perk:plan`, `perk:learn`, `perk:consolidated`,
|
|
3060
|
+
`perk:objective`, `perk:objective-node`, `perk:gist`): ok; otherwise warn listing the missing
|
|
3061
|
+
names,
|
|
3014
3062
|
remediation "run `perk init` or `perk doctor --fix`". perk's labels are created
|
|
3015
3063
|
**workspace-scoped** (no `teamId` on create — Linear's cross-team-label guidance; the lookup is
|
|
3016
3064
|
unscoped, so a pre-existing team-scoped label still counts).
|
|
@@ -3031,14 +3079,14 @@ safely auto-create them).
|
|
|
3031
3079
|
|
|
3032
3080
|
**The `--fix` label repair gesture** (`_fix_linear_labels`, verify-gated like the skills sync —
|
|
3033
3081
|
network I/O, so never a `ManagedConvergence`): when `fix` AND `verify` AND linear is selected AND
|
|
3034
|
-
key + team are available, `check_readiness(..., ensure_labels=True)` ensures the
|
|
3082
|
+
key + team are available, `check_readiness(..., ensure_labels=True)` ensures the six labels;
|
|
3035
3083
|
created names land on `fixed` (`Linear: created label perk:plan`), failures on `fix_errors`.
|
|
3036
3084
|
Lookup-first idempotency: a converged workspace reports nothing (the doctor idempotency rule).
|
|
3037
3085
|
|
|
3038
3086
|
**The init readiness step** (`perk/convergence/init/__init__.py::_linear_readiness`, verify-gated, non-fatal — the
|
|
3039
3087
|
GitHub D3 mirror: file convergence already succeeded). Only when `verify` AND the committed
|
|
3040
3088
|
backend is `"linear"`: missing key/team degrade to an errored `LinearReport`; otherwise the probe
|
|
3041
|
-
runs with `ensure_labels=True` (init converges the
|
|
3089
|
+
runs with `ensure_labels=True` (init converges the six perk labels upfront; the lazy write-time
|
|
3042
3090
|
`ensure_label` calls remain the safety net). Created labels are reported through the
|
|
3043
3091
|
`LinearReport` (the `--json` `linear` key, §8.5; the human `✓ Linear: <user>, team <key>` line) —
|
|
3044
3092
|
**never** appended to `InitReport.changes`, which stays a pure filesystem-delta list.
|
|
@@ -3213,6 +3261,34 @@ one-stop current shape.
|
|
|
3213
3261
|
| `plannotator-plan` | `PLAN_ADAPTER_PLANNOTATOR_CONTEXT` | browser bridge | present + `/plan-save` |
|
|
3214
3262
|
| `tombell-plan` | `PLAN_ADAPTER_TOMBELL_CONTEXT` (conditioned injection, Node 2.6) | first-party in-TUI review | present + `/plan-save` (incl. tombell's own interactive `/plan` `setActiveTools` restriction arm) |
|
|
3215
3263
|
|
|
3264
|
+
- **Plannotator "Direct Edits" (browser edits of the reviewed document).** Plannotator's
|
|
3265
|
+
plan-review browser lets the reviewer edit the reviewed document directly; the edits arrive as
|
|
3266
|
+
PROSE inside the existing `feedback` string, never a new envelope field — a `# Direct Edits`
|
|
3267
|
+
section (heading + preamble + a ```` ```diff ```` fence containing a jsdiff
|
|
3268
|
+
`createTwoFilesPatch(…, { context: 3 })` patch, `trimEnd()`'d, against the exact bytes perk
|
|
3269
|
+
submitted), composed FIRST, with non-sentinel annotation feedback following after
|
|
3270
|
+
`\n\n---\n\n` (format pin: plannotator `packages/editor/directEdits.ts`
|
|
3271
|
+
`buildDirectEditsSection`/`composeFeedbackWithDirectEdits` @ v0.26.1 — prose, not a pinned
|
|
3272
|
+
API). perk handles it asymmetrically per arm:
|
|
3273
|
+
- **Plan arm, APPROVE:** mechanical apply — strict extraction (`extractDirectEdits`,
|
|
3274
|
+
`extension/adapters/planAdapterPlannotator.ts`) → strict clean-apply (`applyUnifiedDiff`,
|
|
3275
|
+
`extension/substrate/unifiedDiff.ts`, a vendored zero-runtime-dep applier; null on any
|
|
3276
|
+
anomaly) → `writePlanDraft` write-back (reviewed bytes == artifact bytes == saved bytes) →
|
|
3277
|
+
save the EDITED bytes with `details.edited: true` and the annotation remainder as the only
|
|
3278
|
+
surviving feedback. The **fail-open ladder**: no section → today's path byte-stable; a
|
|
3279
|
+
heading that cannot be parsed / applied / written back → the verbatim save plus a loud
|
|
3280
|
+
warning in the approved text and `details.direct_edits_applied: false` (the diff stays in
|
|
3281
|
+
the surfaced feedback for a manual follow-up). Worst-case upstream format drift degrades to
|
|
3282
|
+
exactly the pre-Direct-Edits behavior.
|
|
3283
|
+
- **Objective arm, APPROVE with a Direct Edits section:** NO save — the save seam re-reads the
|
|
3284
|
+
STRUCTURED draft, so rendered-markdown edits (roadmap-table rows included) cannot be folded
|
|
3285
|
+
back mechanically. The arm returns a NON-terminating revise round (`details.status:
|
|
3286
|
+
"revise"`, `reason: "direct_edits"`, gate untouched): the model folds the diff into
|
|
3287
|
+
`objective_draft`, then calls `plan_review` again to confirm. perk never saves an objective
|
|
3288
|
+
the reviewer explicitly edited away from.
|
|
3289
|
+
- **DENY (both arms):** model-mediated — the feedback (diff included) passes through verbatim
|
|
3290
|
+
for the `plan_draft`/`objective_draft` rewrite.
|
|
3291
|
+
|
|
3216
3292
|
- **Link/`consumed_learn` recovery carriers.** Approval-triggered saves carry **no model params**;
|
|
3217
3293
|
the **cold** `handoff_extra` carrier (→ §8.2) and the **warm** `objective_node_claim` carrier
|
|
3218
3294
|
(→ §8.3) recover `objective_id`/`node_id` with identical semantics — fill both-or-neither,
|
|
@@ -4510,6 +4586,30 @@ evidence --render --json`) and **also writes `<bundle_dir>/manifest.json`** —
|
|
|
4510
4586
|
children can `read` the manifest (they cannot read the door's stdout). Written unconditionally on
|
|
4511
4587
|
a materialized bundle, deterministic (no wall-clock); no write on a skip.
|
|
4512
4588
|
|
|
4589
|
+
**The analyst wave (the report-wave module).** The multi-angle analyst fan-out runs through the
|
|
4590
|
+
Perk-owned report-wave module (`extension/waves/reportWave.ts`) via the flow-scoped
|
|
4591
|
+
**`run_learn_wave`** tool (`extension/doors/learn.ts` — non-terminating; the parent continues to
|
|
4592
|
+
reconcile): the module renders the tested `workflowScript`, spawns it async over the pi-subagents
|
|
4593
|
+
v1 extension RPC (`mission: false`, `context: "fresh"`), blocks under the module-owned timeout,
|
|
4594
|
+
and reads the durable `status.json` aggregate — the wave mechanics are CODE, never model-authored
|
|
4595
|
+
prompt mechanics. Analyst reports are **engine-validated structured output** against the TS-owned
|
|
4596
|
+
`LEARN_ANALYST_REPORT_SCHEMA` (`extension/waves/learnWave.ts` — closed shape, all-required,
|
|
4597
|
+
`target` required-nullable, deliberately NO verdict↔candidates conditional: the parent derives
|
|
4598
|
+
the real verdict from `candidates[]`, so salvaging an inconsistent report beats failing its
|
|
4599
|
+
lane), replacing fenced-JSON scraping — covered angle ⟺ ok lane ⟺ schema-valid report.
|
|
4600
|
+
Completeness is the module's **`best-effort`** policy as tested implementation: a lane-level
|
|
4601
|
+
failure is an explicitly-reported **skipped angle** (never a failed pass, no retry); a
|
|
4602
|
+
**wave-level** failure is a loud tool soft-failure (`error_type` = the wave failure reason) —
|
|
4603
|
+
never a silent fallback to model-authored scripts — and the guidance routes the parent to a
|
|
4604
|
+
single-context analysis of the bundle instead. The **angle policy is tool-enforced**
|
|
4605
|
+
(`angleSelectionError`): 2–4 angles, no duplicates, only the four known slugs
|
|
4606
|
+
(`session-deviations` / `plan-vs-implementation` / `existing-docs` / `validation-risk`), and
|
|
4607
|
+
`session-deviations` always included; violations are `bad_input`. The tool takes the
|
|
4608
|
+
guidance-rendered `bundle_dir` (the model relays it verbatim — the same trust plane as the task
|
|
4609
|
+
text), derives `manifest.json` itself (`bad_input` when absent), and resolves the analyst model
|
|
4610
|
+
from `[models.subagents] learn-analyst` at execute time (the wave's workflow-level `model`
|
|
4611
|
+
default). The manifest write rule above and the DECISION vocabulary are unchanged.
|
|
4612
|
+
|
|
4513
4613
|
**The `learn` tool's classification params.** The warm `learn` tool carries `decision` (a
|
|
4514
4614
|
JSON-schema enum of the five captured tokens) + `target` (string), threaded to `perk learn
|
|
4515
4615
|
capture --decision/--target`. The tool-boundary decode mirrors the `summary` strictness: a
|
|
@@ -4815,10 +4915,13 @@ stage (a forked implement session is an implement session); **adopt never impers
|
|
|
4815
4915
|
subagent children stay unscoped — their fresh branch carries no stage). Stage-borrowing cold
|
|
4816
4916
|
doors land on real stage ids (`plan from`/`plan replan`/`learn docs`/`learn code` borrow `plan`;
|
|
4817
4917
|
`objective replan`/`objective author --from` borrow `objective-author`; `skills create/refine`
|
|
4818
|
-
borrow `save`), so the per-stage sets cover every borrower.
|
|
4918
|
+
borrow `save`), so the per-stage sets cover every borrower. The gist stages (`gist-author`,
|
|
4919
|
+
`gist-save` — §8.41) each carry `ask_user_question` + `gist_draft` + `gist_save` + the research
|
|
4920
|
+
families (the objective-author shape; `plan_review` governs via the gate-ON set). **Scoped universe:
|
|
4819
4921
|
`PERK_TOOLS ∪ BORROWED_TOOLS`** — perk's own name-keyed census plus the enumerated
|
|
4820
4922
|
borrowed-package census (the web-provider union, pi-mono-linear's 25 tools, pi-subagents'
|
|
4821
|
-
delegation four,
|
|
4923
|
+
delegation four, pi-fff's search names — both mode name-sets, `fffind`/`ffgrep`/`fff-multi-grep`
|
|
4924
|
+
plus override's `multi_grep` — `todo`, `plannotator_submit_plan`); builtins and un-enumerated foreign names
|
|
4822
4925
|
pass through untouched (fail-open — enumeration is diet-completeness, not correctness).
|
|
4823
4926
|
|
|
4824
4927
|
**The borrowed census posture.** Static names, inert when absent (the `READ_ONLY_TOOLS`
|
|
@@ -4833,7 +4936,7 @@ appear in `BORROWED_TOOLS`; hygiene-tested). Foreign packages that run their own
|
|
|
4833
4936
|
`setActiveTools` (plannotator's phase machinery, @tombell/pi-plan's plan mode) win between
|
|
4834
4937
|
perk's rebuild points (the fail-open direction), and a mid-session rebuild re-installs perk's
|
|
4835
4938
|
stage set over a foreign restriction — recorded interplay, not re-engineered. Stage placement:
|
|
4836
|
-
the research families (web union + Linear reads) ride EVERY stage list; delegation
|
|
4939
|
+
the research families (web union + Linear reads + FFF local search) ride EVERY stage list; delegation
|
|
4837
4940
|
(`subagent`/`wait`/the supervisor pair) and `todo` are worktree-family only among the gate-OFF
|
|
4838
4941
|
stage lists (delegation additionally rides the read-only gate — §8.3);
|
|
4839
4942
|
`LINEAR_MUTATING_TOOLS` (incl. `linear_configure_auth`, which writes `~/.pi/agent/auth.json`)
|
|
@@ -4870,3 +4973,76 @@ lever the gate's allowlist uses; `edit`/`write`/`bash` blocking remains the gate
|
|
|
4870
4973
|
config surface for the map (the §8.39 non-interference posture; fail-open on unknown ids covers
|
|
4871
4974
|
version skew). **Bare-session zero-change guarantee:** a session that never engages either
|
|
4872
4975
|
concern gets **zero `setActiveTools` calls** — bare warm sessions stay byte-identical.
|
|
4976
|
+
|
|
4977
|
+
## §8.41 · The gist tier (lightweight statements of intent)
|
|
4978
|
+
|
|
4979
|
+
A **gist** is a backend-tracked **statement of intent** — a rough, thematically
|
|
4980
|
+
problem-space-focused note of "something we would likely want to do", upstream of both plans and
|
|
4981
|
+
objectives. It is **code-informed but carries no implementation strategy** (no steps, no roadmap,
|
|
4982
|
+
no estimates); the lightness lives in the ARTIFACT and the skill guidance, not the machinery. A
|
|
4983
|
+
gist's **scope** (`plan` | `objective`) records its intended consumption tier: a storage
|
|
4984
|
+
discriminator on Linear (issue vs project), a header hint on GitHub.
|
|
4985
|
+
|
|
4986
|
+
**Registry topology (settled decision).** The two gist stages (`gist-author` → `gist-save`) form
|
|
4987
|
+
a **separate, disconnected component** — no edges into the main loop. Gists are optional; nothing
|
|
4988
|
+
routes off "which stage is initial" except the validator, which requires **at least one** initial
|
|
4989
|
+
stage (zero initials — a pure cycle — stays an error). Consumption happens via the unchanged
|
|
4990
|
+
§8.29/§8.30 in-place adoption doors (`perk plan from <gist>` /
|
|
4991
|
+
`perk objective author --from <gist>`), never via stage edges: a gist is consumable because it is
|
|
4992
|
+
an ordinary OPEN backend object without plan/objective metadata.
|
|
4993
|
+
|
|
4994
|
+
**Metadata.** The `perk:gist` label (yellow `fbca04`, description "perk gist issue (a rough
|
|
4995
|
+
statement of intent)"), lazily created by its create-op on first use — the sixth `perk:*` label.
|
|
4996
|
+
The `gist-header` metadata block carries `run_id`, `created`, and `scope` (`plan` | `objective`;
|
|
4997
|
+
lenient read — an unknown stored scope parses to `None`). Per-backend storage:
|
|
4998
|
+
|
|
4999
|
+
- **GitHub** — an issue: the html-style `gist-header` block rendered into the body above the
|
|
5000
|
+
prose.
|
|
5001
|
+
- **Linear, scope `plan`** — an issue: clean transcoded prose body + a `gist-header` **native
|
|
5002
|
+
attachment** (kind `gist-header`, URL `https://perk.invalid/gist/<run_id>`, title
|
|
5003
|
+
"Perk gist", subtitle = scope; the §8.24 attachment-metadata posture).
|
|
5004
|
+
- **Linear, scope `objective`** — a **project**: the overview carries an inline-code
|
|
5005
|
+
`gist-header` block above the transcoded prose. No milestones, no node-issues, no metadata
|
|
5006
|
+
sentinel — deliberately light; the overview block IS the identity (projects have no
|
|
5007
|
+
attachments).
|
|
5008
|
+
|
|
5009
|
+
**The `IssueBackend` gist trio** (docstring contracts mirroring the learn trio):
|
|
5010
|
+
`find_gist_issue{run_id}` (label + header-key scoped — cannot return a plan/learn issue),
|
|
5011
|
+
`create_gist_issue{title, body, run_id, scope, dry_run}` (idempotent via the finder; stamps
|
|
5012
|
+
`scope` into the header), and `list_gist_issues{} -> GistSummary[]{id, title, url, body, scope,
|
|
5013
|
+
adopted}` — every OPEN `perk:gist` issue; raises on infra failure, never masks as empty. The
|
|
5014
|
+
`ObjectiveStore` grows the project-tier pair in the no-op-return family:
|
|
5015
|
+
`create_gist_source{title, prose, run_id, dry_run} -> ObjectiveRef | None` (`None` = "no project
|
|
5016
|
+
surface" — the CLI falls back to the issue tier; the Linear project store creates/finds the gist
|
|
5017
|
+
project) and `list_gist_sources{} -> GistSummary[]` (`()` outside the project store).
|
|
5018
|
+
|
|
5019
|
+
**Adopted detection.** A gist whose stored metadata ALSO carries the adopting tier's metadata
|
|
5020
|
+
(distinct keys — adoption stamps additively beside the `gist-header`, no collision) is
|
|
5021
|
+
**adopted**: on GitHub the `plan-header`/`objective-header` block joins the body; on a Linear
|
|
5022
|
+
issue the signal is a `plan-header` attachment; on a Linear project it is the **Reconcilable
|
|
5023
|
+
region** the adoption composer writes into the overview (the objective headers ride the
|
|
5024
|
+
sentinel's attachments, never an overview block; the original gist overview — with its
|
|
5025
|
+
`gist-header` — survives verbatim in the Immutable archive note, keeping the project scannable
|
|
5026
|
+
as a gist). `perk gist list` default **hides** adopted gists (the "what's still unconsumed" backlog
|
|
5027
|
+
view); `--all` shows everything with an adopted marker. No gist-specific consumption
|
|
5028
|
+
bookkeeping: in-place adoption means the gist *becomes* the plan/objective and inherits its
|
|
5029
|
+
lifecycle.
|
|
5030
|
+
|
|
5031
|
+
**The save worker (`perk gist create --json`).** Options `--body <path>` (required), `--title`,
|
|
5032
|
+
`--scope [plan|objective]`, `--run-id`, `--dry-run`. Scope resolution: explicit `--scope` > the
|
|
5033
|
+
`gist_scope` **handoff** key (a declared `Handoff` field, stashed by `perk gist author --scope`
|
|
5034
|
+
— the `adopt_from` handoff pattern; best-effort recovery, never blocks a save) > `"plan"`. Scope
|
|
5035
|
+
`objective` routes to `ObjectiveStore.create_gist_source` first, falling back to the issue tier
|
|
5036
|
+
on a `None` return; scope `plan` goes to the issue backend directly. Envelope:
|
|
5037
|
+
`{"success": true, "error_type": null, "gist": {"id", "url", "existed"}, "scope", "dry_run"}` —
|
|
5038
|
+
opaque string ids (§8.21). Human output prints the created/found line plus a consumption hint
|
|
5039
|
+
(`perk plan from <id>` / `perk objective author --from <id>`).
|
|
5040
|
+
|
|
5041
|
+
**The warm flow** is the full review-first mirror of plan/objective authoring: the `gist_draft`
|
|
5042
|
+
tool (the third draft-file tool — §8.1's carve-out family; artifact `gist-draft.json`, shape
|
|
5043
|
+
`{schema_version: 1, title?, scope?, prose}`) keeps the draft current; `plan_review` in a
|
|
5044
|
+
`gist-author` session reviews the **rendered markdown** (title + scope line + prose — never raw
|
|
5045
|
+
JSON), VIEW-ONLY first-party (the objective-arm shape; deny+feedback is the change channel);
|
|
5046
|
+
APPROVED auto-saves via `gistApprovalSave` → the `gist_save` tool / `perk gist create`;
|
|
5047
|
+
`/gist-save` is the manual failsafe. No draft → soft-skip `reason: "no_gist_draft"`. No session
|
|
5048
|
+
linkage after save — nothing consumes a gist in-session.
|
package/shared/registry.yaml
CHANGED
|
@@ -27,6 +27,7 @@ state_keys:
|
|
|
27
27
|
- comments # discussion comments
|
|
28
28
|
- review-threads # review threads (distinct API from comments)
|
|
29
29
|
- learn # the perk:learn knowledge-capture issue (P2.T8b)
|
|
30
|
+
- gist # the perk:gist statement-of-intent issue (contracts.md §8.41)
|
|
30
31
|
# Tier 2 · .perk/workflow/ — the local cache (see contracts.md §8.1).
|
|
31
32
|
cache:
|
|
32
33
|
- plan # materialized plan body cache
|
|
@@ -41,7 +42,10 @@ state_keys:
|
|
|
41
42
|
|
|
42
43
|
# ---------------------------------------------------------------------------
|
|
43
44
|
# Stages — the loop (Q5): objective-author -> objective-save -> objective-plan -> plan ->
|
|
44
|
-
# save -> implement -> submit -> address -> land -> learn
|
|
45
|
+
# save -> implement -> submit -> address -> land -> learn — plus the OPTIONAL gist
|
|
46
|
+
# component (gist-author -> gist-save), deliberately disconnected from the loop:
|
|
47
|
+
# a saved gist is consumed by the in-place adoption doors (`perk plan from`,
|
|
48
|
+
# `perk objective author --from`), not by stage edges (contracts.md §8.41).
|
|
45
49
|
#
|
|
46
50
|
# Every field is locked EXCEPT `requires`/`reads`/`writes` (empty until handlers
|
|
47
51
|
# land). `run_id` is uniform AND shape-locked by the validator: `perk/substrate/registry.py`
|
|
@@ -59,6 +63,32 @@ state_keys:
|
|
|
59
63
|
# local-only by design — review, merge, and judgment capture stay where a human is.
|
|
60
64
|
# ---------------------------------------------------------------------------
|
|
61
65
|
stages:
|
|
66
|
+
- id: gist-author
|
|
67
|
+
summary: Draft a gist — a rough statement of intent (read-only authoring).
|
|
68
|
+
mode: read-only
|
|
69
|
+
worktree: none # interactive authoring, like plan — no branch, no gist yet
|
|
70
|
+
doors: { warm: true, cold_local: true, cold_remote: false } # interactive local authoring
|
|
71
|
+
run_id: { warm: keep, cold_local: mint, cold_remote: mint }
|
|
72
|
+
command: gist author # dedicated seeded cold door (perk gist author)
|
|
73
|
+
requires: [] # nothing exists yet — this stage CREATES the gist
|
|
74
|
+
reads: []
|
|
75
|
+
writes: [session.workflow-state, cache.session-data] # the read-only `mode` append; gist_draft writes the gist-draft artifact + its session_artifacts pointer
|
|
76
|
+
predecessors: [] # an initial of the disconnected gist component
|
|
77
|
+
successors: [gist-save]
|
|
78
|
+
|
|
79
|
+
- id: gist-save
|
|
80
|
+
summary: Persist the drafted gist to the issue backend; the read-only -> read-write boundary.
|
|
81
|
+
mode: read-write
|
|
82
|
+
worktree: none # still no branch; save writes the gist issue/project
|
|
83
|
+
doors: { warm: true, cold_local: true, cold_remote: false }
|
|
84
|
+
run_id: { warm: keep, cold_local: mint, cold_remote: mint }
|
|
85
|
+
command: gist save # the gist_save terminating tool + /gist-save command
|
|
86
|
+
requires: []
|
|
87
|
+
reads: []
|
|
88
|
+
writes: [github.gist, session.workflow-state] # create the gist issue/project
|
|
89
|
+
predecessors: [gist-author]
|
|
90
|
+
successors: [] # a terminal — consumption happens via the adoption doors, not edges
|
|
91
|
+
|
|
62
92
|
- id: objective-author
|
|
63
93
|
summary: Draft a new objective + roadmap (read-only authoring).
|
|
64
94
|
mode: read-only
|