@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.
- package/bin/config-change.mjs +1 -1
- package/bin/jarvis.mjs +8 -12
- package/bin/pre-tool-use.mjs +2 -2
- package/bin/session-start.mjs +1 -1
- package/bin/stop.mjs +1 -1
- package/bin/user-prompt-submit.mjs +1 -1
- package/dist/bin.js +10104 -37174
- package/dist/bin.js.map +1 -1
- package/dist/data/backends.mjs +469 -0
- package/dist/data/chunk-7REP35VA.mjs +462 -0
- package/dist/data/chunk-AKQQC5IT.mjs +1224 -0
- package/dist/data/chunk-AYOJSS2F.mjs +72 -0
- package/dist/data/chunk-RRJ6KKYL.mjs +42 -0
- package/dist/data/chunk-YWSWQEJN.mjs +138 -0
- package/dist/data/embedders.mjs +80 -0
- package/dist/data/finders.mjs +35 -0
- package/dist/data/index.mjs +137 -0
- package/dist/data/linkers.mjs +83 -0
- package/dist/data/mcp.mjs +2624 -0
- package/dist/data/persistences.mjs +8 -0
- package/dist/data/rerankers.mjs +52 -0
- package/dist/data/stores.mjs +38 -0
- package/dist/hooks/config-change.js +7 -29
- package/dist/hooks/config-change.js.map +1 -1
- package/dist/hooks/pre-tool-use.js +129 -32
- package/dist/hooks/pre-tool-use.js.map +1 -1
- package/dist/hooks/session-start.js +72 -37
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/stop.js +140 -32
- package/dist/hooks/stop.js.map +1 -1
- package/dist/hooks/user-prompt-submit.js +7 -29
- package/dist/hooks/user-prompt-submit.js.map +1 -1
- package/harness/assets/architecture-template.md +53 -0
- package/harness/assets/design-template.md +89 -0
- package/harness/assets/domains/architecture.md +43 -0
- package/harness/assets/domains/commercial.md +42 -0
- package/harness/assets/domains/design.md +42 -0
- package/harness/assets/domains/legal.md +43 -0
- package/harness/assets/domains/operations.md +39 -0
- package/harness/assets/domains/product.md +43 -0
- package/harness/assets/domains/quality.md +43 -0
- package/harness/assets/domains/security.md +44 -0
- package/harness/assets/domains/support.md +45 -0
- package/harness/assets/epic-template.md +85 -0
- package/harness/assets/feature-template.md +86 -0
- package/harness/assets/handoff-template.md +26 -0
- package/harness/assets/task-template.md +38 -0
- package/harness/assets/version-template.md +43 -0
- package/harness/harness/__init__.py +7 -0
- package/harness/harness/align.py +582 -0
- package/harness/harness/architecture.py +254 -0
- package/harness/harness/autonomy.py +374 -0
- package/harness/harness/branches.py +408 -0
- package/harness/harness/config.py +1482 -0
- package/harness/harness/coverage.py +199 -0
- package/harness/harness/epic.py +220 -0
- package/harness/harness/events.py +153 -0
- package/harness/harness/extend.py +99 -0
- package/harness/harness/frontmatter.py +218 -0
- package/harness/harness/gate.py +591 -0
- package/harness/harness/generate.py +267 -0
- package/harness/harness/git.py +775 -0
- package/harness/harness/ids.py +140 -0
- package/harness/harness/kickoff.py +231 -0
- package/harness/harness/lint.py +505 -0
- package/harness/harness/model.py +364 -0
- package/harness/harness/peers.py +187 -0
- package/harness/harness/product.py +29 -0
- package/harness/harness/registry.py +382 -0
- package/harness/harness/report.py +227 -0
- package/harness/harness/safety.py +387 -0
- package/harness/harness/scaffold.py +129 -0
- package/harness/harness/shard.py +63 -0
- package/harness/harness/shift.py +348 -0
- package/harness/harness/task.py +507 -0
- package/harness/harness/tree.py +258 -0
- package/harness/harness/version.py +305 -0
- package/harness/harness/wrap.py +217 -0
- package/harness/hooks/guard.py +259 -0
- package/harness/presets/appchy/PRESET.md +717 -0
- package/harness/presets/appchy/references/artifacts.md +539 -0
- package/harness/presets/appchy/references/graph.md +154 -0
- package/harness/presets/appchy/references/operations.md +444 -0
- package/harness/presets/appchy/references/research.md +216 -0
- package/harness/schema/work.config.schema.json +401 -0
- package/harness/test_work.py +5002 -0
- package/harness/work.py +534 -0
- package/package.json +34 -40
- package/bin/config-change.dev.mjs +0 -17
- package/bin/jarvis-dev.mjs +0 -30
- package/bin/pre-tool-use.dev.mjs +0 -23
- package/bin/session-start.dev.mjs +0 -17
- package/bin/stop.dev.mjs +0 -17
- package/bin/user-prompt-submit.dev.mjs +0 -17
- package/dev/bin.js +0 -38718
- package/dev/bin.js.map +0 -1
- package/dev/env.json +0 -1
- package/dev/hooks/config-change.js +0 -110
- package/dev/hooks/config-change.js.map +0 -1
- package/dev/hooks/pre-tool-use.js +0 -120
- package/dev/hooks/pre-tool-use.js.map +0 -1
- package/dev/hooks/session-start.js +0 -115
- package/dev/hooks/session-start.js.map +0 -1
- package/dev/hooks/stop.js +0 -112
- package/dev/hooks/stop.js.map +0 -1
- package/dev/hooks/user-prompt-submit.js +0 -111
- package/dev/hooks/user-prompt-submit.js.map +0 -1
- 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).
|