@appchy/jarvis 0.1.36 → 0.1.39

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.
Files changed (108) hide show
  1. package/bin/config-change.mjs +1 -1
  2. package/bin/jarvis.mjs +8 -12
  3. package/bin/pre-tool-use.mjs +2 -2
  4. package/bin/session-start.mjs +1 -1
  5. package/bin/stop.mjs +1 -1
  6. package/bin/user-prompt-submit.mjs +1 -1
  7. package/dist/bin.js +10104 -37174
  8. package/dist/bin.js.map +1 -1
  9. package/dist/data/backends.mjs +469 -0
  10. package/dist/data/chunk-7REP35VA.mjs +462 -0
  11. package/dist/data/chunk-AKQQC5IT.mjs +1224 -0
  12. package/dist/data/chunk-AYOJSS2F.mjs +72 -0
  13. package/dist/data/chunk-RRJ6KKYL.mjs +42 -0
  14. package/dist/data/chunk-YWSWQEJN.mjs +138 -0
  15. package/dist/data/embedders.mjs +80 -0
  16. package/dist/data/finders.mjs +35 -0
  17. package/dist/data/index.mjs +137 -0
  18. package/dist/data/linkers.mjs +83 -0
  19. package/dist/data/mcp.mjs +2624 -0
  20. package/dist/data/persistences.mjs +8 -0
  21. package/dist/data/rerankers.mjs +52 -0
  22. package/dist/data/stores.mjs +38 -0
  23. package/dist/hooks/config-change.js +7 -29
  24. package/dist/hooks/config-change.js.map +1 -1
  25. package/dist/hooks/pre-tool-use.js +129 -32
  26. package/dist/hooks/pre-tool-use.js.map +1 -1
  27. package/dist/hooks/session-start.js +72 -37
  28. package/dist/hooks/session-start.js.map +1 -1
  29. package/dist/hooks/stop.js +140 -32
  30. package/dist/hooks/stop.js.map +1 -1
  31. package/dist/hooks/user-prompt-submit.js +7 -29
  32. package/dist/hooks/user-prompt-submit.js.map +1 -1
  33. package/harness/assets/architecture-template.md +53 -0
  34. package/harness/assets/design-template.md +89 -0
  35. package/harness/assets/domains/architecture.md +43 -0
  36. package/harness/assets/domains/commercial.md +42 -0
  37. package/harness/assets/domains/design.md +42 -0
  38. package/harness/assets/domains/legal.md +43 -0
  39. package/harness/assets/domains/operations.md +39 -0
  40. package/harness/assets/domains/product.md +43 -0
  41. package/harness/assets/domains/quality.md +43 -0
  42. package/harness/assets/domains/security.md +44 -0
  43. package/harness/assets/domains/support.md +45 -0
  44. package/harness/assets/epic-template.md +85 -0
  45. package/harness/assets/feature-template.md +86 -0
  46. package/harness/assets/handoff-template.md +26 -0
  47. package/harness/assets/task-template.md +38 -0
  48. package/harness/assets/version-template.md +43 -0
  49. package/harness/harness/__init__.py +7 -0
  50. package/harness/harness/align.py +582 -0
  51. package/harness/harness/architecture.py +254 -0
  52. package/harness/harness/autonomy.py +374 -0
  53. package/harness/harness/branches.py +408 -0
  54. package/harness/harness/config.py +1482 -0
  55. package/harness/harness/coverage.py +199 -0
  56. package/harness/harness/epic.py +220 -0
  57. package/harness/harness/events.py +153 -0
  58. package/harness/harness/extend.py +99 -0
  59. package/harness/harness/frontmatter.py +218 -0
  60. package/harness/harness/gate.py +591 -0
  61. package/harness/harness/generate.py +267 -0
  62. package/harness/harness/git.py +775 -0
  63. package/harness/harness/ids.py +140 -0
  64. package/harness/harness/kickoff.py +231 -0
  65. package/harness/harness/lint.py +505 -0
  66. package/harness/harness/model.py +364 -0
  67. package/harness/harness/peers.py +187 -0
  68. package/harness/harness/product.py +29 -0
  69. package/harness/harness/registry.py +382 -0
  70. package/harness/harness/report.py +227 -0
  71. package/harness/harness/safety.py +387 -0
  72. package/harness/harness/scaffold.py +129 -0
  73. package/harness/harness/shard.py +63 -0
  74. package/harness/harness/shift.py +348 -0
  75. package/harness/harness/task.py +507 -0
  76. package/harness/harness/tree.py +258 -0
  77. package/harness/harness/version.py +305 -0
  78. package/harness/harness/wrap.py +217 -0
  79. package/harness/hooks/guard.py +259 -0
  80. package/harness/presets/appchy/PRESET.md +717 -0
  81. package/harness/presets/appchy/references/artifacts.md +539 -0
  82. package/harness/presets/appchy/references/graph.md +154 -0
  83. package/harness/presets/appchy/references/operations.md +444 -0
  84. package/harness/presets/appchy/references/research.md +216 -0
  85. package/harness/schema/work.config.schema.json +401 -0
  86. package/harness/test_work.py +5002 -0
  87. package/harness/work.py +534 -0
  88. package/package.json +34 -40
  89. package/bin/config-change.dev.mjs +0 -17
  90. package/bin/jarvis-dev.mjs +0 -30
  91. package/bin/pre-tool-use.dev.mjs +0 -23
  92. package/bin/session-start.dev.mjs +0 -17
  93. package/bin/stop.dev.mjs +0 -17
  94. package/bin/user-prompt-submit.dev.mjs +0 -17
  95. package/dev/bin.js +0 -38718
  96. package/dev/bin.js.map +0 -1
  97. package/dev/env.json +0 -1
  98. package/dev/hooks/config-change.js +0 -110
  99. package/dev/hooks/config-change.js.map +0 -1
  100. package/dev/hooks/pre-tool-use.js +0 -120
  101. package/dev/hooks/pre-tool-use.js.map +0 -1
  102. package/dev/hooks/session-start.js +0 -115
  103. package/dev/hooks/session-start.js.map +0 -1
  104. package/dev/hooks/stop.js +0 -112
  105. package/dev/hooks/stop.js.map +0 -1
  106. package/dev/hooks/user-prompt-submit.js +0 -111
  107. package/dev/hooks/user-prompt-submit.js.map +0 -1
  108. package/dist/env.json +0 -1
@@ -0,0 +1,154 @@
1
+ # Planning against a graph — the worked loop
2
+
3
+ How the blast-radius step in [`SKILL.md`](../PRESET.md) actually runs when
4
+ `graph.mcp` names an engine. SKILL.md owns *what the step must achieve*, in five
5
+ ordered outcomes; this owns what each one looks like in practice, what each is
6
+ **not** for, and the two ways the step goes wrong silently.
7
+
8
+ **No engine is named here, and none should be.** The payload resolves its repo
9
+ from config and never writes one in — so the calls below are described by what
10
+ they DO, and the names in the table are illustrations of a common vocabulary,
11
+ not a contract. **Your engine's own tool descriptions are the authority**; read
12
+ them before planning, which is step 0 and is not optional.
13
+
14
+ ## Why this file exists at all
15
+
16
+ The harness used to name four graph queries: neighbours, impact, path, search.
17
+ All four are real and useful — and following them literally was often the **slow
18
+ path**, because they are the manual pieces a good engine already composes into one
19
+ call. Meanwhile the engine's own instructions said so, and a session got two
20
+ instructions for the same moment with no way to tell which to obey.
21
+
22
+ So SKILL.md now names the OUTCOME and defers the calls. That deferral only works
23
+ if the descriptions actually get read.
24
+
25
+ ## The five steps
26
+
27
+ | Step | What it must achieve | A common name |
28
+ |---|---|---|
29
+ | 1 | orient: work in flight, most-cited rules, **index freshness** | `brief` |
30
+ | 2 | a task in words → edit set + governing rules + covering tests + blast radius | `scope` |
31
+ | 3 | the **full text** of every rule step 2 surfaced | `read` |
32
+ | 4 | the plan, citing the ids you read | — |
33
+ | 5 | the diff → governance it touches that the plan never cited | `review` |
34
+
35
+ ### 1. Orient
36
+
37
+ One call for the top-down state of the repo. Call it first in a session, before
38
+ searching for anything.
39
+
40
+ **Not for**: finding a specific thing. If you know what you are looking for, you
41
+ are already past this step.
42
+
43
+ **The freshness read is the load-bearing half**, and it has three answers:
44
+
45
+ | What you see | What it means | What to do |
46
+ |---|---|---|
47
+ | fresh, built from this commit | the map describes this tree | plan against it |
48
+ | stale, or an older commit | it describes a tree that no longer exists | **rebuild**, then re-read |
49
+ | the tools are absent entirely | `graph.mcp` names a server nothing serves | treat as `null` and **say so in the plan** |
50
+
51
+ A stale index that reads as a complete map is worse than no map, because nothing
52
+ about the answer looks wrong.
53
+
54
+ ### 2. Scope
55
+
56
+ The task **in words** → what to edit, what governs it, what covers it, what
57
+ breaks. This is the fast path for **modifying** existing code, and where an engine
58
+ composes it into one call, **that call IS the step** — asking its pieces separately
59
+ is slower and no more complete.
60
+
61
+ **Not for**: building something new. Scoping needs an existing thing to scope
62
+ against; for BUILD-NEW the entry is a concept search, then the rule text, and the
63
+ plan says that is what it got.
64
+
65
+ ### 3. Read the rule text
66
+
67
+ Every decision and standard step 2 surfaced, **in full**.
68
+
69
+ **Not delegable and not skippable.** A one-line summary is not the rule — it is
70
+ the rule with the qualifier, the exception and the ordering removed, which are the
71
+ three things a plan actually trips on. It is also why a delegated grind may never
72
+ paraphrase: a cheap summary of a rule reads exactly like the rule.
73
+
74
+ **Not for**: confirming what you already believe. If you can predict what the text
75
+ says, read it anyway — the whole cost of this step is paid on the day it surprises
76
+ you.
77
+
78
+ ### 4. Draft, and cite the ids
79
+
80
+ A cited id is what stops the builder tripping a rule the planner already weighed,
81
+ and it is what makes step 5 a check rather than a re-derivation.
82
+
83
+ ### 5. Verify the edit set
84
+
85
+ The mirror of step 2, run **before committing**: given this diff, what governance
86
+ does it touch that the plan never cited? This catches a rule nobody knew applied —
87
+ a different failure from breaking one you knew about, and the only one no amount of
88
+ care at step 4 prevents.
89
+
90
+ **Not for**: correctness. It reads governance, not behaviour. Tests and
91
+ `/code-review` are the other half, and neither substitutes for the other.
92
+
93
+ ## The three direct questions, and when they earn their place
94
+
95
+ When the scope call is not enough — usually because the change is structural
96
+ rather than behavioural:
97
+
98
+ - **neighbours** of a file — its real callers and callees. Before touching
99
+ anything unfamiliar.
100
+ - **impact** — the transitive blast radius. Before a rename, a signature change or
101
+ a deletion.
102
+ - **search for a helper, then neighbours on each hit** — the **DRY / single-source
103
+ check**: is this the only implementation? It finds the inline re-implementation
104
+ grep misses, because the second copy spells everything differently.
105
+
106
+ Where the engine ranks coupling or clusters nodes, that surfaces god-node smells
107
+ for free — worth one call at a version boundary.
108
+
109
+ A **trace**-shaped call ("why does this file exist / where is this rule enforced")
110
+ earns its place when the plan touches code whose reason for existing is not
111
+ obvious from reading it, which is most code that survived a decision.
112
+
113
+ ## The two ways this goes wrong quietly
114
+
115
+ **Laundering.** You read a scope result, a delegate's map or a tool summary, and plan
116
+ off it without opening the primary source. Everything reads fine; the plan rests on
117
+ a paraphrase. The tell: *if you could have written the plan without opening a file,
118
+ you did not do step 3.*
119
+
120
+ **Overstating.** The plan's As-found implies a complete map it did not get — the
121
+ engine was absent, or stale, or answered a narrower question than the prose
122
+ suggests. SKILL.md's rule is absolute: **never write an As-found that implies a
123
+ complete map you did not get.** Grep-wide and honest beats graph-shaped prose with
124
+ nothing behind it.
125
+
126
+ Both are invisible downstream. Nothing in the harness can detect either, which is
127
+ why they are stated rather than linted.
128
+
129
+ ## What a map does NOT know
130
+
131
+ Worth holding, because a confident empty answer reads like an absence of risk:
132
+
133
+ - **What is not indexed.** Shared infrastructure deliberately claimed by no region
134
+ answers "nothing tracked connects to me" — honest, and not the same as "safe to
135
+ change".
136
+ - **Dynamic reach.** A call assembled from a string, a route resolved at runtime, a
137
+ hook fired by config — the extractor sees none of it.
138
+ - **Anything uncommitted, on a machine that is not this one.** A graph built from a
139
+ clone carries only what was committed to it.
140
+ - **Edge confidence.** Some engines mark circumstantial edges (a file sitting in a
141
+ region, a semantic match) apart from deterministic ones. Where they do, a blast
142
+ radius defaults to the precise half — widening it is a decision, not a default.
143
+
144
+ ## When your engine is missing a step
145
+
146
+ The five outcomes are the contract; the calls are not. With no scoping call, step 2
147
+ becomes search + neighbours + impact by hand, and the plan says so. With no review
148
+ call, step 5 becomes a manual re-read of the rules the sweep found — which is
149
+ exactly what `graph.mcp: null` already prescribes.
150
+
151
+ **A repo whose `graph.mcp` names a server its MCP config does not serve is in the
152
+ worst of the three states**: the harness tells every session it has a map, the
153
+ tools do not exist, and nothing says why. `work.py list` warns on this. If you see
154
+ that warning, fix the config or set `graph.mcp: null` — do not plan around it.
@@ -0,0 +1,444 @@
1
+ # Operations — detailed playbooks
2
+
3
+ The on-demand reference for every harness operation. `SKILL.md` names them;
4
+ this file walks through each. `work.py` locates `work/` automatically and
5
+ regenerates `work/README.md` after every mutating command — there is no
6
+ manual `readme` step.
7
+
8
+ ## Create a version
9
+
10
+ **First check you may.** A version is a RELEASE, so `version-new`
11
+ refuses on two counts: while **any earlier version is unreleased**, and without
12
+ a **`--outcome`** you can state in one line. Either refusal usually means the
13
+ work is an **epic**, not a version — go to *Create an epic* instead, and come
14
+ back the day the earlier cuts release.
15
+
16
+ 1. Scaffold:
17
+ ```bash
18
+ work.py version-new <v> \
19
+ --outcome "what a user can newly DO when this ships" \
20
+ --title "Human-readable title" [--target YYYY-MM-DD] [--design] [--architecture]
21
+ ```
22
+ Creates `version.md` (+ `design.md` only with `--design`,
23
+ `architecture.md` only with `--architecture`). `--design` scaffolds the
24
+ claude.ai/design **UI** brief for a UI-bearing version; `--architecture`
25
+ scaffolds the version-level **technical** brief (the cross-task "plan FROM
26
+ this" doc) for a version whose tasks share seams a single technical brief
27
+ should own. Skip either for a version that doesn't need it — they're
28
+ independent (an engine-only version might take `--architecture` alone; a
29
+ thin UI version might take `--design` alone).
30
+ 2. Fill `version.md` (Goal expanding the `outcome:`, Scope naming the epics in
31
+ the cut) together with the user. Stories live per-feature in `<feature>.md`.
32
+ 3. **Design pass (only if `--design`).** Lift the stories into `design.md`'s
33
+ **User stories**, paste it into <https://claude.ai/design>, link the repo.
34
+ Claude Design works in two passes plus cleanup (per `design.md`'s
35
+ **Workflow** section): Pass 1 wireframes + per-page `wireframes/ui.md`
36
+ behaviour spec; Pass 2 the polished prototype; then cleanup + fresh
37
+ screenshots. The bundle (`project/` + `ui.md` + screenshots) drops into
38
+ `/wireframes/`.
39
+ 4. **Architecture pass (only if `--architecture`).** Fill `architecture.md`'s
40
+ As-found / Target architecture / Phases / Risks before decomposing into
41
+ tasks — each task's own `plan.md` then plans FROM it rather than
42
+ re-deriving the cross-task design.
43
+ 5. **Pull the epics that fill it** — `work.py pull <epic> --version <v>` moves a
44
+ backlog epic in whole, tasks and all.
45
+
46
+ ## Create an epic
47
+
48
+ The normal way work starts. An epic is the coherent goal you plan together, and
49
+ it lives in the **backlog** until a cut opens for it.
50
+
51
+ 1. Scaffold:
52
+ ```bash
53
+ work.py epic-new <name> \
54
+ --title "The goal, in user terms" \
55
+ --covers <feature>/AC-03,<feature>/AC-04 # FEATURE-QUALIFIED
56
+ # --version <v> only when a cut is already open and this belongs in it
57
+ ```
58
+ Name it for the **goal**, not the mechanism — and not the same as any task
59
+ inside it, since names are globally unique across all three tiers.
60
+ 2. **Plan the whole goal, here, once** — this is the step that pays for the
61
+ tier. Fill `epic.md`'s **Plan**: the as-found (send the blast-radius/search
62
+ grind out — ONCE for the goal, not once per task), the target
63
+ architecture and shared seams, the constraining rules
64
+ (`work.py rules --task` over the union of the regions), the slices in
65
+ landing order, and the forward-compat pass. Then **Goal**, **Governance this
66
+ implies** and **Non-goals**.
67
+ 3. **Decompose into tasks** (next section) — the tasks fall out of the slices you
68
+ just settled; they do not re-open them.
69
+
70
+ ## Decompose an epic into tasks
71
+
72
+ Slice the epic into **vertical e2e tasks** — one goal per task,
73
+ shippable on its own. Source is `epic.md`'s Goal + Plan, plus
74
+ `wireframes/ui.md` for UI work.
75
+
76
+ 1. Read the source together with the `<feature>.md` of every feature the epic
77
+ `covers:`.
78
+ 2. **Propose candidate slices.** Each cuts *down* through every layer it
79
+ needs (types → db → workflow/agent → API → UI), never one layer across
80
+ with the rest stranded. Shared infrastructure folds into the task that
81
+ first consumes it, not its own task.
82
+ 3. **Test each against the shippability rule** — *if this ships and nothing
83
+ after it does, is the repo green, consistent and integrated?* If not,
84
+ merge with a sibling. Order the survivors, note dependencies.
85
+ 4. **Align with the user** via `AskUserQuestion`, each candidate with its
86
+ one-line shippability rationale.
87
+ 5. **Scaffold each agreed task** — a brief only, no plan yet, tagged into the
88
+ graph (see *Two-pass decomposition* below). `--epic` is **required**; the
89
+ version is derived from where that epic lives:
90
+ ```bash
91
+ work.py new <task> --epic <e> \
92
+ [--priority P1] [--depends a,b] \
93
+ [--owner <feature>|<domain>] [--code region-a,region-b] [--covers AC-01,AC-02]
94
+ ```
95
+ **If a slice needs 3+ `code` regions it is not a task** — the lint says so,
96
+ and it is right. Split the goal again, by goal rather than by layer.
97
+ 6. **Count the slices.** Fewer than three and the epic is the wrong unit
98
+ (`SKILL.md` §Three tiers) — either the goal is smaller than it looked, in
99
+ which case its tasks belong in an existing epic, or it was sliced too coarsely.
100
+ Say which, in the epic's §Plan, before scaffolding anything.
101
+
102
+ ## Merge thin epics back together
103
+
104
+ The inverse operation, and it comes up whenever the backlog has been fed
105
+ one-task-at-a-time. `work.py` has no `merge` — the mechanics are folder moves —
106
+ so the discipline is entirely in what happens to the prose.
107
+
108
+ 1. **Read every dissolving `epic.md` IN FULL first**, not just its Goal. A thin
109
+ epic is exactly where a fully-planned, deliberately-deferred goal hides —
110
+ banked founder calls, verified as-found, an architecture already settled — and
111
+ its own text usually says *do not re-derive this*. Deleting one silently costs
112
+ the next session everything the file was written to save.
113
+ 2. **Group by surface and audience, not by package.** The learner's runtime, the
114
+ teacher's desk, the agent-facing surfaces, the content itself, the harness.
115
+ Two tasks touching `packages/blocks` are not thereby one goal.
116
+ 3. **Move the task folders** (`mv`; the epic is derived from where a folder
117
+ sits), then **write the surviving `epic.md`**: a Goal that covers what was
118
+ merged in, a §Plan holding every settled call from the dissolved files, and
119
+ their Governance-implied and Non-goals folded in. A non-goal often survives as
120
+ a *sequencing rule* rather than an exclusion — "Hebrew waits for good English"
121
+ becomes "slice 3 runs last".
122
+ 4. **Only then delete the empty epic folders**, and check `covers:` on the
123
+ survivor: a criterion inherited from a dissolved epic may already be met by
124
+ other work, in which case the honest move is to drop it and record the new
125
+ `AC-nn` the work actually owes.
126
+ 5. Re-run `work.py list` — the floor lint should be silent, and the README table
127
+ regenerates itself.
128
+
129
+ ## Two-pass decomposition
130
+
131
+ Decomposing a version and planning a task are **two passes over the same
132
+ connected graph** — cut shallow up front, plan deep at pickup, so no task
133
+ is ever designed blind to its siblings. `SKILL.md` carries the rule; this is the
134
+ playbook.
135
+
136
+ **Pass 1 — the epic (shallow).** Runs during *Decompose an epic into tasks*
137
+ (above); the graph-connecting steps on top of the base slicing:
138
+
139
+ 1. **Story-map the goal** — lay its topology out as the vertical e2e
140
+ slices (base mechanics above), a release story-map, not a layer stack.
141
+ 2. **Tag each slice** into the graph — its `code` region(s) (`--code`), its
142
+ `owner:` feature (`--owner`), and the `AC-nn` it satisfies (`--covers`).
143
+ The tags are what wire the slice to the rest of the graph.
144
+ 3. **Cluster change-affine slices** — group the slices that share a `code`
145
+ region or a feature; a shared region is where a seam will be set and reused,
146
+ so those slices must agree on it.
147
+ 4. **Pick a walking-skeleton first slice** — thin, end-to-end, exercising the
148
+ real seams; NOT a foundation *layer* built ahead of any consumer.
149
+ 5. **Record the seams** — for each `code` region the cut touches, write its
150
+ seam + known-next constraints + consumers + foundation task into
151
+ `architecture.md`'s **Code map**. Shallow: a map of seams/decisions/links,
152
+ never a design or a code dump.
153
+
154
+ **Pass 2 — pickup (execute).** Deliberately CHEAP. The thinking was done in
155
+ Pass 1; pickup reads it and builds.
156
+
157
+ 1. **Read `epic.md` §Plan** — the as-found, the seams, the rules, this
158
+ task's slice and what it depends on. Plus the `<feature>.md` it serves.
159
+ 2. **Build.** No fresh blast-radius sweep, no re-derived architecture, no second
160
+ design document. If you are re-running the blast-radius grind for a task, the epic
161
+ plan was incomplete — fix that, don't absorb the cost silently.
162
+ 3. **When pickup DOES surface something unsettled** — an unknown the plan missed,
163
+ a seam that turns out too narrow — settle it and **write it back UP into
164
+ `epic.md`**, immediately, so every sibling inherits the answer. A task-local
165
+ `plan.md` is for a call genuinely nobody else needs; `work.py list` warns when
166
+ a task inside an epic carries one, because that is usually a gap upstairs.
167
+
168
+ ## Rescope a version or an epic
169
+
170
+ 1. Edit `version.md` (`outcome:` + Scope) or `epic.md` (Goal + Plan +
171
+ Governance this implies).
172
+ 2. If it's a UI version, update `design.md` and re-run Claude Design;
173
+ `wireframes/` rewrites. If it has an `architecture.md`, update its Target
174
+ architecture / Phases to match.
175
+ 3. Sync tasks: add new ones with `work.py new --epic <e>`; defer obsolete ones
176
+ into a backlog epic (plain `mv`). Update affected `task.md` briefs. A whole
177
+ epic moves between cuts with `work.py rehome <epic> --version <v>`.
178
+ 4. A rescope IS a direction change — run the governance sweep (SKILL.md
179
+ "Governance cuts both ways"): record the new direction (a durable rule /
180
+ `<feature>.md`), then update or re-scope-stamp everything the change
181
+ invalidates — other epics' `epic.md`, in-flight `plan.md`/`handoff.md`,
182
+ product docs. Conflicts with a standing rule go to the user as an
183
+ explicit supersede fork.
184
+
185
+ ## Create a task (one-off)
186
+
187
+ 1. Derive a kebab-case `<name>` and one-line title.
188
+ 2. Settle priority (P0/P1/P2) and its **epic** — every task has one. If no
189
+ existing epic owns the goal, create one first (*Create an epic*); a backlog
190
+ epic is cheap, a version is not.
191
+ 3. **Offer to investigate** for a bug or unknown-scope task — launch an
192
+ `Explore`/`general-purpose` agent; findings populate **Context &
193
+ findings**.
194
+ 4. Scaffold:
195
+ ```bash
196
+ work.py new <name> --epic <e> \
197
+ --priority P1 --title "Human-readable title" \
198
+ [--depends other-task]
199
+ ```
200
+ 5. Edit `task.md` (Problem, Context & findings, Acceptance criteria, Open
201
+ questions; delete the **Approach** heading unless already agreed). No length
202
+ cap — write what a cold reader needs. What is banned is a competing PLAN, not
203
+ detail: the design is settled once in `epic.md` §Plan, at pickup.
204
+
205
+ ## Pull from backlog
206
+
207
+ ```bash
208
+ work.py pull <epic> --version <v>
209
+ work.py pull <task> --version <v> --epic <e>
210
+ ```
211
+
212
+ The **name decides the tier**. An epic moves whole — the normal promotion, since
213
+ an epic is the unit a cut is filled with — and its tasks gain the `queue/`
214
+ bucket they had no need of in the backlog (a backlog task has no status). A
215
+ single task needs `--epic` to say which epic in that version it lands in.
216
+
217
+ ## List
218
+
219
+ ```bash
220
+ work.py list
221
+ ```
222
+
223
+ Prints the backlog grouped by version, plus any **lint warnings** (queue
224
+ task carrying more than `task.md`, suffix-variant files, research dir without
225
+ `00-report.md`, oversized/headerless handoff). Fix what it flags.
226
+
227
+ ## Run a shift unattended
228
+
229
+ The loop a schedule drives. Everything in it already exists as an operation below;
230
+ what `next` adds is *choosing*, and what `ask` adds is somewhere to put a question
231
+ so the run never stalls and never quietly decides for the founder.
232
+
233
+ ```bash
234
+ work.py next # ONE task, claimed, with its read order printed
235
+ # … build it, then EITHER prove it:
236
+ work.py verify --task <name>
237
+ work.py observed <name> --ac AC-01 --saw "what you actually saw"
238
+ work.py move <name> complete --delivered "…" --not-included "…"
239
+ # … OR park what is not yours to decide, and go back to `next`:
240
+ work.py ask <name> --question "…" --options "a | b" --owner founder
241
+ ```
242
+
243
+ 1. **`next` chooses; you do not.** It takes the highest-priority queued task whose
244
+ dependencies are complete, whose `tier:` is at or below `autonomy.ceiling`, and
245
+ whose `code:` regions no other live instance holds — then claims it with a lease.
246
+ Nothing eligible prints **why**, per task, so a cron log is diagnostic rather
247
+ than silent.
248
+ 2. **One instance, one task.** A second `next` from the same instance resumes what
249
+ it holds rather than taking more; three half-finished tasks is what juggling
250
+ costs, and unattended nobody notices until morning.
251
+ 3. **Read what it printed**, in that order — it is the cold-start spine below,
252
+ computed for this task.
253
+ 4. **The moment you reach a product, UX or scope call — `ask`.** Not a guess with a
254
+ note attached. The question is the deliverable at that point.
255
+ 5. **`verify` before you claim anything.** The gate refuses without a passing run
256
+ at the current commit, and it refuses *before* the folder moves.
257
+ 6. **A crashed shift frees its own work** — leases expire and are reaped on read,
258
+ so nobody is paged at 3am.
259
+
260
+ `work.py status` is the founder's morning read; `work.py digest --since <date>`
261
+ is what happened while they were away.
262
+
263
+ ## Pick up a task
264
+
265
+ ```bash
266
+ work.py move <name> in-progress
267
+ ```
268
+
269
+ Moves the folder, records the session, and scaffolds a `handoff.md` *template*
270
+ (you only fill it when handing off to a new conversation — see below). If the
271
+ task is already in-progress (fresh session, same task):
272
+
273
+ ```bash
274
+ work.py session <name>
275
+ ```
276
+
277
+ ### Read in this order
278
+
279
+ A cold session reads the spine, top to bottom:
280
+
281
+ 1. **`task.md`** — the brief: problem + acceptance criteria. The spec.
282
+ 2. **`handoff.md`** — *where things stand*, IF a prior conversation left one
283
+ (it exists only when the task was handed off across sessions; an untouched
284
+ scaffold means the last session didn't hand off — lean on `task.md` + code).
285
+ 3. **`plan.md`** — design + as-found + build (exists only if the task has
286
+ one).
287
+ 4. **The rules that constrain it** — `work.py rules --task <name>`
288
+ derives them from `task.code ∩ system.code`; read each in full where it
289
+ lives (`work.py where <id>`). A one-line summary is not the rule.
290
+ 5. **Parent `epic.md`** — the Plan every sibling task builds against, and the
291
+ governance the epic has promised to land. Then `version.md` for release
292
+ context.
293
+ 6. **`wireframes/ui.md`** — the UI sections this task touches (UI work only).
294
+ 7. **`research/00-report.md`** — the summary, if the task has research; drill
295
+ deeper only when a question demands it.
296
+
297
+ Files absent for this task (`plan.md`, `research/`, `ui.md`) simply don't
298
+ exist — skip them.
299
+
300
+ ### Check dependencies
301
+
302
+ Read `task.md` frontmatter `depends_on:` and surface any incomplete
303
+ dependency before starting — running ahead of one is the fastest way to
304
+ write code that gets thrown away.
305
+
306
+ ## Plan at pickup (analyse → align → plan)
307
+
308
+ Planning happens **now, with the code in front of you** — never pre-written
309
+ in the queue, where it rots.
310
+
311
+ 1. **`plan.md` `## As-found`** — write the current-state analysis: a raw,
312
+ factual account of how the in-scope code works today. No opinions. Kept
313
+ live for the task's footprint (≤40 lines).
314
+ 2. **Research (only when needed)** — a single `research.md`, or a `research/`
315
+ subfolder with a `00-report.md` summary. Raw external findings.
316
+ 3. **Align with the user — before writing the architecture.** Surface open
317
+ questions + a short outline first, work back and forth:
318
+ - Every question goes through `AskUserQuestion`.
319
+ - Every product / UX / architecture decision is the user's — present
320
+ alternatives, recommend one, let them choose. Tiny mechanics don't need
321
+ sign-off.
322
+ - Promote durable resolved rules to the domain or system that owns them (the
323
+ next free number, read off the tree) and cite them by id.
324
+ - **Governance conflict sweep** (SKILL.md "Governance cuts both ways"):
325
+ read in full every rule and `<feature>.md` requirement the plan touches;
326
+ a contradiction either changes the plan or goes to the user as an
327
+ explicit supersede fork — never both left standing. Cite the
328
+ constraining ids in `plan.md` so the build can't trip them; if the
329
+ plan changes direction, sweep + update the briefs/docs it invalidates
330
+ before building.
331
+ 4. **Write the architecture.** `plan.md` `## Architecture` then `## Build` —
332
+ per [artifacts.md → plan.md](artifacts.md). The moment `plan.md` exists,
333
+ collapse `task.md`'s `## Approach` to `Approach → plan.md` so the
334
+ architecture has one owner.
335
+
336
+ As you go, keep the ownership rules true: live status is the bucket, the
337
+ architecture only in `plan.md`, durable rationale only in the owning domain. If you hit a
338
+ contradiction between files, fix it in the owner and reduce the rest to
339
+ pointers before continuing.
340
+
341
+ **Native plan mode (cowork / cross-device).** When a plan is authored in
342
+ native plan mode rather than the in-folder `plan.md` — e.g. a desktop or cloud
343
+ session that produces a committed plan file — record the pointer with
344
+ `work.py plan <name> --file <path>` (with `plans.dir` configured, `--file`
345
+ defaults to `<plans.dir>/<name>.md`; with it null, `--file` is required). It appends a dated, deduped entry to the task's
346
+ `plans:` frontmatter; the plan body is **not** copied into the task folder. A
347
+ fresh session reads `task.md`, walks `plans:` newest-first, and re-enters plan
348
+ mode from the first ref that still exists — so plan continuity survives
349
+ desktop↔cloud↔mobile hops.
350
+
351
+ ## Hygiene checkpoint
352
+
353
+ The "Leave no legacy" rule (in the files `spine.conventions` names) is enforced
354
+ *continuously*. Run this repo's `hygiene.review` (bug pass) and `hygiene.simplify`
355
+ (quality pass) — the `SessionStart` block names them where they are configured, and
356
+ where they are not there is no pass to run and none is invented. Capture their
357
+ findings in native `TodoWrite` and
358
+ work them like a checklist: fix in-scope findings, re-run, iterate. The todo
359
+ list is the working surface — nothing is written into the task folder.
360
+
361
+ ## Complete a task
362
+
363
+ **`move … complete` is a gate, not a checklist you tick.** It refuses, before the
364
+ folder moves, unless: every acceptance criterion is checked · `verify.*` ran and
365
+ passed at the current commit (`work.py verify --task <name>`) · every `covers:`
366
+ criterion has coverage a run produced *or* a recorded eyes-on (`work.py observed
367
+ <name> --ac AC-01 --saw "…"`) · a tier-3 task carries a human's sign-off. It names
368
+ every gate that held, all at once. `--accept "<reason>" --owner <name>` overrides,
369
+ as a recorded event — never invent an owner for one.
370
+
371
+ 1. Confirm `task.md`'s Acceptance criteria are met (all `- [ ]` checked). (If
372
+ a `handoff.md` was ever filled for a cross-conversation handoff, stamp its
373
+ `**Status:**` done — but a task completed within one session needn't have
374
+ one.)
375
+ 2. **Final hygiene gate** — a last run of whatever `hygiene.*` names; clear
376
+ every in-scope finding. Nothing configured, nothing to run.
377
+ 3. **Governance sync gate** (SKILL.md "Governance cuts both ways") — if the
378
+ task changed behaviour or direction: update the `owner:` `<feature>.md`
379
+ (description/requirements + `state:` frontmatter) to describe what was
380
+ BUILT, promote any durable calls made mid-task to the domain that owns them
381
+ (supersede, never edit, an old entry), and re-check the edit set against the
382
+ rules the blast-radius sweep surfaced — where `graph.mcp` is set the graph can
383
+ do this from the diff; where it is null it is a manual re-read.
384
+ Flag governance the diff touches that the plan never cited. Fix or
385
+ escalate every flag; a doc describing the retired design blocks
386
+ completion like a failing test.
387
+ 4. **Offer a code review** via `AskUserQuestion`, where `hygiene.review` names
388
+ one. If yes, run it over the working diff, capture findings in `TodoWrite`,
389
+ work them. Deeper multi-agent review is user-invoked, never yours to launch.
390
+ 5. Move it (warns on leftover unchecked criteria):
391
+ ```bash
392
+ work.py move <name> complete
393
+ ```
394
+
395
+ ## Release a version
396
+
397
+ When every task is complete:
398
+
399
+ ```bash
400
+ work.py release <v>
401
+ ```
402
+
403
+ It refuses unless every task is complete **and** `version.md` states an
404
+ `outcome:`. It stamps `released: <today>`, prints the outcome, and **deletes
405
+ every `epic.md` in the version** — an epic is temporary by definition,
406
+ and how the work was planned stops being true the moment it ships. The epic
407
+ folders stay as the grouping of what shipped; git holds the plans.
408
+
409
+ **So distill BEFORE you release, not after** — once `release` runs, the plan
410
+ docs are gone from the working tree:
411
+
412
+ 1. **Land the governance** each `epic.md` promised in **Governance this
413
+ implies** — it belongs in `product/**` · `architecture/**` ·
414
+ `quality/**` now, because it is true now.
415
+ 2. **Promote durables** — any decision still load-bearing that lives only in an
416
+ `epic.md`, `handoff.md` or `plan.md` moves to the domain that owns it or its
417
+ owning `<system>.md`.
418
+ 3. **Repoint inbound links** — anything (backlog epics especially) that
419
+ deep-links into this version's folders repoints to the id.
420
+ 4. **Archive** (next section).
421
+
422
+ Finalise the **Release notes** in `version.md` first.
423
+
424
+ ## Archive a version
425
+
426
+ After release, strip the bulk — git keeps the full history:
427
+
428
+ ```bash
429
+ work.py archive <v>
430
+ ```
431
+
432
+ For every task in the version it deletes all files except `task.md`, stamps
433
+ `archived: <today>` (with the pre-archive commit sha) in `version.md`, and
434
+ **moves the whole version to `work/archive/versions/<v>/`**. It refuses unless
435
+ the version is released.
436
+
437
+ **Off the board, not gone.** `list`, the README table and every lint read
438
+ `versions/` only, so the board shows work that is *moving* — the reason this
439
+ move exists is that three live tasks were being read past sixty completed ones.
440
+ `work.py path <v>` and `work.py where <id>` still resolve into the archive.
441
+
442
+ Run the release distill checklist *first* — once archived, the
443
+ `plan.md`/`handoff.md`/research are gone from the working tree (recoverable
444
+ only via git).