@appchy/jarvis 0.1.37 → 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 +10168 -37341
- 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 -38821
- 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,539 @@
|
|
|
1
|
+
# Artifacts — file format specs
|
|
2
|
+
|
|
3
|
+
The on-demand reference for every file this skill scaffolds, reads or
|
|
4
|
+
maintains. Load this when writing or editing a `task.md`, `epic.md`,
|
|
5
|
+
`handoff.md`, `plan.md`, a domain's hosted rules, `version.md`, `<feature>.md`,
|
|
6
|
+
`design.md` or `architecture.md`.
|
|
7
|
+
|
|
8
|
+
`SKILL.md` carries the workflow + the ownership rules; this file carries the
|
|
9
|
+
contracts. Keep both in sync with the templates under `assets/`.
|
|
10
|
+
|
|
11
|
+
## Conventions
|
|
12
|
+
|
|
13
|
+
- Frontmatter is YAML between `---` fences.
|
|
14
|
+
- File-level title is `# Title` on the first body line.
|
|
15
|
+
- Names (task, epic and version) are kebab-case and globally unique across all three.
|
|
16
|
+
- Dates are `YYYY-MM-DD` (today's, when the file is created or updated).
|
|
17
|
+
- Files marked **scaffolded** are written by `work.py` from `assets/`
|
|
18
|
+
templates. Everything else is created by hand — only when the state needs
|
|
19
|
+
it, never pre-emptively.
|
|
20
|
+
|
|
21
|
+
## `task.md` — the brief (the only file that survives archive)
|
|
22
|
+
|
|
23
|
+
The single source of truth for a task's scope. **There is no length cap** (the
|
|
24
|
+
old ≤30 was removed 2026-08-02): a brief is read by a session starting cold, so
|
|
25
|
+
front-load the hard reasoning, the dead ends and the big-picture traps rather
|
|
26
|
+
than making it re-derive them. What must NOT appear is a competing plan — the
|
|
27
|
+
design is settled once, in `epic.md` §Plan, and a `plan.md` in a queue folder is
|
|
28
|
+
still a lint. Length was never the failure; a second design doc is.
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
---
|
|
32
|
+
priority: P1 # P0 now · P1 next · P2 later
|
|
33
|
+
depends_on: [other-task-name]
|
|
34
|
+
tags: [engine] # optional — area tags; the vocabulary is `tags.allowed` in config
|
|
35
|
+
owner: lesson-flow # a bare <feature> or org-domain — expected on every task (lint)
|
|
36
|
+
code: [renderer, compose] # optional, multi-valued — durable code-locality, see below
|
|
37
|
+
covers: [AC-01, AC-02] # optional, multi-valued — the feature's AC ids this task satisfies
|
|
38
|
+
start: 2026-08-01 # optional — YYYY-MM-DD
|
|
39
|
+
end: 2026-08-10 # optional — YYYY-MM-DD, must be >= start
|
|
40
|
+
created: 2026-05-22
|
|
41
|
+
updated: 2026-05-22
|
|
42
|
+
sessions: # added by work.py — never by hand
|
|
43
|
+
- 2026-05-22 <session-id>
|
|
44
|
+
plans: # added by `work.py plan` — pointers to native-mode plans
|
|
45
|
+
- 2026-05-22 <plans.dir>/<slug>.md
|
|
46
|
+
completed: 2026-05-22 # added by `work.py move … complete`, absent otherwise
|
|
47
|
+
---
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Recognised keys: `priority`, `depends_on`, `tags`, `product`, `code`,
|
|
51
|
+
`covers`, `start`, `end`, `created`, `updated`, `sessions`, `plans`,
|
|
52
|
+
`completed`. `sessions:`, `plans:` and `completed:` are written by `work.py`
|
|
53
|
+
— leave them alone.
|
|
54
|
+
|
|
55
|
+
`owner: <feature>` or `owner: <domain>` bridges an engineering task to
|
|
56
|
+
the product layer — the sole owner of the task↔feature link (see the
|
|
57
|
+
Ownership table in `SKILL.md`). **There is no app tier** while `product.tiers` is
|
|
58
|
+
`flat`: the repo is ONE product,
|
|
59
|
+
so a feature name is globally unique and `product/<feature>.md` is its
|
|
60
|
+
whole address. Set it with `work.py new --owner
|
|
61
|
+
<feature>|<domain>` or by hand; format-checked (kebab segments) but never
|
|
62
|
+
existence-checked at write time — a stale or not-yet-scaffolded reference
|
|
63
|
+
surfaces as a `work.py list` lint warning instead of a refusal. **Every task
|
|
64
|
+
is expected to carry one** — a task with no `owner:` also surfaces as
|
|
65
|
+
a lint warning. No reciprocal `tasks:` list on the feature side — that
|
|
66
|
+
link is always a query over this field, never hand-maintained on the other
|
|
67
|
+
end.
|
|
68
|
+
|
|
69
|
+
**A bare `owner: <domain>`** — `operations`, `quality`, `harness`… — is how a
|
|
70
|
+
task with **no user-facing feature** links: the harness itself, the docs tooling,
|
|
71
|
+
CI, the deploy pipeline. It satisfies the link requirement without inventing a
|
|
72
|
+
fake feature, and it **retired the old `product: infra` sentinel**, which modelled
|
|
73
|
+
infrastructure as an app so the lint would pass. Infrastructure is a domain
|
|
74
|
+
(`work/operations/`), not an app. Product-building tasks still take a real
|
|
75
|
+
`<feature>`.
|
|
76
|
+
|
|
77
|
+
`code: [<region>, …]` (optional, multi-valued) — a durable,
|
|
78
|
+
cross-version code-locality tag, ORTHOGONAL to `owner` (code-locality ≠
|
|
79
|
+
behaviour). The vocabulary is **derived**: a region is valid because some
|
|
80
|
+
`work/architecture/<system>.md` declares it in its `code:`. There is no
|
|
81
|
+
hand-curated list to keep in sync — `work.py new --code a,b` dies on a region no
|
|
82
|
+
system owns. Query every task touching a region with `work.py code <region>`,
|
|
83
|
+
and the rules it drags in with `work.py rules --task <name>`.
|
|
84
|
+
|
|
85
|
+
`covers: [AC-nn, …]` (optional, multi-valued) — which of the linked
|
|
86
|
+
feature's `AC-nn` acceptance criteria this task satisfies. Set via `work.py
|
|
87
|
+
new --covers AC-01,AC-02`; format-checked at write time (`AC-\d+`), and
|
|
88
|
+
existence-checked (does that AC actually exist in the linked `<feature>.md`) as
|
|
89
|
+
a `work.py list` lint warning. A feature's coverage — which `AC-nn` are
|
|
90
|
+
covered by a COMPLETED task's `covers:` — rolls up automatically; a `shipped`
|
|
91
|
+
feature with an uncovered AC is a lint warning.
|
|
92
|
+
|
|
93
|
+
`start` / `end` (optional, `YYYY-MM-DD`) — a task-level date window. Set
|
|
94
|
+
via `work.py new --start/--end`; validated as dates, and `start <= end` when
|
|
95
|
+
both are present.
|
|
96
|
+
|
|
97
|
+
Body sections, in order:
|
|
98
|
+
|
|
99
|
+
1. `# Title` — kebab-derived human title.
|
|
100
|
+
2. **Problem** — what's wrong / what's needed. One or two paragraphs;
|
|
101
|
+
enough to start cold.
|
|
102
|
+
3. **Context & findings** — investigation notes (root cause, `path:line`
|
|
103
|
+
refs, what was ruled out). Empty until investigated.
|
|
104
|
+
4. **Approach** — fill in *only once agreed*, and **omit the heading until
|
|
105
|
+
then**. Once the goal is planned this collapses to one line —
|
|
106
|
+
`Approach → epic.md` (or `→ plan.md` in the rare task-local case) — so the
|
|
107
|
+
architecture has a single owner.
|
|
108
|
+
5. **Acceptance criteria** — checkable outcomes (`- [ ] …`), one concrete
|
|
109
|
+
line each. These are the task's intent in checkable form; tests trace
|
|
110
|
+
back to them via a `covers()` call or a Playwright tag — never a comment.
|
|
111
|
+
6. **Open questions** — unknowns to resolve at pick-up.
|
|
112
|
+
|
|
113
|
+
There is no "Plans & analysis" section and no task-level `stories.md`: the
|
|
114
|
+
design lives in `epic.md` §Plan, written once for the goal; live status is the
|
|
115
|
+
task's bucket.
|
|
116
|
+
|
|
117
|
+
Scaffolded by `work.py new <name>`. Template:
|
|
118
|
+
[`../assets/task-template.md`](../assets/task-template.md).
|
|
119
|
+
|
|
120
|
+
## `handoff.md` — the CROSS-CONVERSATION continuity file (not a per-session journal)
|
|
121
|
+
|
|
122
|
+
Write/refresh this **only when you hand the task to a fresh conversation** —
|
|
123
|
+
you're wrapping up, or the user asks to continue elsewhere. **You do NOT touch
|
|
124
|
+
it while actively working the task in one conversation**; a task can complete
|
|
125
|
+
within a single session having never had a filled handoff. `work.py move …
|
|
126
|
+
in-progress` scaffolds the template so it's ready, but an empty/untouched
|
|
127
|
+
scaffold mid-work is fine. When you do write it, it's the one file the next
|
|
128
|
+
session reads to know *where things stand*. **Hard cap 80 lines** —
|
|
129
|
+
prune-on-absorb when over (move durable facts to `plan.md` / the owning domain,
|
|
130
|
+
drop the oldest log entries).
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
# handoff — <task>
|
|
134
|
+
|
|
135
|
+
**Status:** <one line: where the task is right now>
|
|
136
|
+
**NEXT:** <one line: the single next action to take>
|
|
137
|
+
|
|
138
|
+
## Read order
|
|
139
|
+
<the files a cold session reads, in order — e.g. task.md → plan.md → the governing rules>
|
|
140
|
+
|
|
141
|
+
## Log
|
|
142
|
+
- 2026-06-12 <what happened this session, ≤3 lines, latest first>
|
|
143
|
+
- 2026-06-11 <older entry>
|
|
144
|
+
|
|
145
|
+
## Decisions
|
|
146
|
+
- <id>: <title> # promoted to the owning domain — pointer only
|
|
147
|
+
- <small, task-local decision that didn't earn a D-number>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Rules:
|
|
151
|
+
|
|
152
|
+
- `**Status:**` and `**NEXT:**` capture the in-flight snapshot *for the next
|
|
153
|
+
conversation*; they're written at handoff, never duplicated into `task.md`,
|
|
154
|
+
`plan.md` or `version.md`. The always-true live status is the task's bucket.
|
|
155
|
+
- `## Log` is dated and **latest-first**; the top line is always the freshest
|
|
156
|
+
truth. Pruning removes from the bottom.
|
|
157
|
+
- `## Decisions` holds small task-local calls as bullets (only worth recording
|
|
158
|
+
if you're handing off). A durable one goes to the domain that owns it and is
|
|
159
|
+
reduced here to `<id>: <title>`.
|
|
160
|
+
|
|
161
|
+
Scaffolded by `work.py move … in-progress` / `work.py handoff <name>`.
|
|
162
|
+
Template: [`../assets/handoff-template.md`](../assets/handoff-template.md).
|
|
163
|
+
|
|
164
|
+
## `plan.md` — the EXCEPTION, not the norm (task-level, in-progress only)
|
|
165
|
+
|
|
166
|
+
**The design lives in `epic.md` §Plan**, written once for the whole goal. A task
|
|
167
|
+
gets a `plan.md` only when pickup surfaces a call that document did not settle —
|
|
168
|
+
and that is a **gap in the epic plan**, so the usual fix is to settle it *there*
|
|
169
|
+
and let every sibling inherit it. `work.py list` warns when a task inside an epic
|
|
170
|
+
carries one. Keep it only for a call genuinely nobody else needs.
|
|
171
|
+
|
|
172
|
+
(Everything below is the format for that case, and for a task in a
|
|
173
|
+
grandfathered flat version that has no epic.) One living file, written **at
|
|
174
|
+
pickup, never in the queue**, and the **sole owner** of whatever design it
|
|
175
|
+
does hold. Plan mode is a *drafting tool*, not a storage location: its plan lives
|
|
176
|
+
in the conversation and is discarded once settled — the durable design (the
|
|
177
|
+
*why-this-shape*, which has no other home) is what lands in `plan.md`. The older
|
|
178
|
+
`work.py plan` → the `plans:` pointer into `plans.dir` is at most an
|
|
179
|
+
**archival snapshot** of a native-mode plan (useful for the D172 web/cowork
|
|
180
|
+
handoff), never a second source of truth: if `plan.md` exists, it wins.
|
|
181
|
+
|
|
182
|
+
**Decision-complete before you build.** No *decision* is ever handed to a subagent —
|
|
183
|
+
the brain decides, and a delegate only types out edits already settled to the exact
|
|
184
|
+
string (SKILL.md §Model routing) — so the reader of this plan is the session that
|
|
185
|
+
picks the task up, possibly days later with none of today's context.
|
|
186
|
+
Before it is a plan:
|
|
187
|
+
- **Resolve every open question** with the founder first. A plan with an
|
|
188
|
+
unresolved fork is NOT ready — settle it, don't carry it forward.
|
|
189
|
+
- **Make every input explicit** so nothing has to be inferred twice: the exact
|
|
190
|
+
files / `path:line` to touch, the rule ids / `<feature>.md` requirements to honour,
|
|
191
|
+
the edge cases + failure behaviour, data shapes, and the `AC-nn` it satisfies.
|
|
192
|
+
- If a reader would have to ask *"how should this work?"*, the plan is
|
|
193
|
+
incomplete — finish that thinking here.
|
|
194
|
+
|
|
195
|
+
The test is the same one the founder call rests on: work that would *surface*
|
|
196
|
+
decisions while being built (an unproven approach, runtime-only correctness,
|
|
197
|
+
intricate logic) is not settled, however complete the plan looks.
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
# plan — <task>
|
|
201
|
+
|
|
202
|
+
## As-found (≤40 lines)
|
|
203
|
+
<raw, factual account of how the in-scope code works today — no opinions.
|
|
204
|
+
This replaces the old state.md; it lives here, beside the architecture it informs.>
|
|
205
|
+
|
|
206
|
+
## Architecture (≤80 lines)
|
|
207
|
+
┌─ diagram ─┐ …rationale… …pseudo-code for the load-bearing pieces…
|
|
208
|
+
└───────────┘
|
|
209
|
+
|
|
210
|
+
## Build
|
|
211
|
+
<steps / phases / prose — whatever fits. Anchor-link what each step
|
|
212
|
+
implements ([→ Architecture §…], [wireframes/ui.md → Mixer]).>
|
|
213
|
+
|
|
214
|
+
## Completion
|
|
215
|
+
<acceptance criteria met · hygiene clean · tests green>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
- **`## As-found`** — the as-found baseline (former `state.md`). Kept live
|
|
219
|
+
for the task's footprint: extend as scope grows; don't rewrite an entry as
|
|
220
|
+
the code changes.
|
|
221
|
+
- **`## Architecture`** — lead with a diagram, then rationale (*why* this
|
|
222
|
+
shape, alternatives weighed), then pseudo-code for the load-bearing pieces.
|
|
223
|
+
For UI work keep this to the *technical* shape and anchor-link
|
|
224
|
+
`wireframes/ui.md` for what the user sees. Link code paths; never paste code.
|
|
225
|
+
- **`## Build`** — lay it out however's clearest. The only hard rule is at
|
|
226
|
+
the task level: when it lands the repo is green and integrated;
|
|
227
|
+
intermediate states are not separately shippable.
|
|
228
|
+
|
|
229
|
+
Only create `plan.md` when the task genuinely needs a design — a one-line fix
|
|
230
|
+
doesn't. Refactor-natured work is its own commit, kept separate from feature/fix
|
|
231
|
+
work.
|
|
232
|
+
|
|
233
|
+
## Hosted rules — `architecture/<system>.md` and the org-domain READMEs
|
|
234
|
+
|
|
235
|
+
A lightweight ADR ledger — **only** the business / product / high-level
|
|
236
|
+
architecture decisions that set lasting direction and must not regress.
|
|
237
|
+
Not a log of plan-level choices (those live in code + git history), and
|
|
238
|
+
never a duplicate of current operating truth (that's the files `spine.standards`
|
|
239
|
+
and `spine.conventions` name, and the code).
|
|
240
|
+
|
|
241
|
+
Shape: an `## Index` of one-line titles up top (scannable without reading
|
|
242
|
+
the body), then entries grouped by area (`## Direction` / `## Architecture`
|
|
243
|
+
/ `## Engine model` / `## Agent & editing` / `## Product & UX` /
|
|
244
|
+
`## Decided, not built`).
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
### <id> — <the rule, in one line>
|
|
248
|
+
The engine core stays a vector IR; glTF-export and Three.js-preview are
|
|
249
|
+
additive backends, not the render pipeline. Why: keeps assets portable and
|
|
250
|
+
the core small. Supersedes: <id>.
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Rules:
|
|
254
|
+
|
|
255
|
+
- **Significance gate.** Add an entry only when a decision sets lasting
|
|
256
|
+
direction and isn't self-evident from the code. "Would a future session
|
|
257
|
+
regress this without the entry?" — if no, don't add it.
|
|
258
|
+
- **Append-only, immutable.** A change of mind is a NEW entry marking the
|
|
259
|
+
old one `Superseded by <id>` — never edit history.
|
|
260
|
+
- **2–6 lines per entry**: the decision + the why (+ `Supersedes:`). Keep
|
|
261
|
+
the index line in sync.
|
|
262
|
+
- This is the only file others deep-link for rationale — cite the id,
|
|
263
|
+
never restate.
|
|
264
|
+
|
|
265
|
+
## `version.md` — the RELEASE record
|
|
266
|
+
|
|
267
|
+
One per version, scaffolded by `work.py version-new`. **A version is a release**
|
|
268
|
+
and `version-new` enforces both halves of that: it **refuses without
|
|
269
|
+
`--outcome`**, and it **refuses while any earlier version is still unreleased**.
|
|
270
|
+
If you cannot state the outcome, the work is an epic until you can.
|
|
271
|
+
|
|
272
|
+
```yaml
|
|
273
|
+
---
|
|
274
|
+
created: 2026-05-22
|
|
275
|
+
updated: 2026-05-22
|
|
276
|
+
order: 10 # roadmap sort order — optional; see derivation below
|
|
277
|
+
outcome: a teacher can publish a lesson under their own name # REQUIRED
|
|
278
|
+
start: 2026-06-01 # optional — YYYY-MM-DD
|
|
279
|
+
end: 2026-06-30 # optional — YYYY-MM-DD, must be >= start
|
|
280
|
+
target: 2026-06-30 # optional; line absent when no target
|
|
281
|
+
released: 2026-06-30 # absent until `work.py release`
|
|
282
|
+
archived: 2026-07-02 # absent until `work.py archive`
|
|
283
|
+
---
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`outcome` (**required**) — ONE line, in user terms, saying what someone can newly
|
|
287
|
+
**do** when this ships. Not a task summary and not a component list. It is
|
|
288
|
+
printed by `list` and by `release`, rendered into the README table, and lint-
|
|
289
|
+
checked on every mutation. The honest one is better than the flattering one:
|
|
290
|
+
`16-mcp-package`'s reads *"nothing user-visible — a refactor released as a
|
|
291
|
+
version, the exact shape the release gate now refuses"*, which is precisely why
|
|
292
|
+
the gate exists.
|
|
293
|
+
|
|
294
|
+
`order` (optional, int) — the roadmap's legible sort key, read at every
|
|
295
|
+
site that lists versions (`list`, the README table, `code`). Set via
|
|
296
|
+
`work.py version-new --order N`; derived when absent: an explicit `order:`
|
|
297
|
+
frontmatter value wins, else a leading numeric prefix on the version's own
|
|
298
|
+
name (`80-scene-generator` → 0, `100-poc-map` → 3), else `9999` (unordered
|
|
299
|
+
versions sort last, never breaking the roadmap). A name with a numeric prefix
|
|
300
|
+
and an explicit `--order` that disagree is refused at scaffold time.
|
|
301
|
+
|
|
302
|
+
`start` / `end` (optional, `YYYY-MM-DD`) — a version-level date window
|
|
303
|
+
for the roadmap timeline. Set via `work.py version-new --start/--end`;
|
|
304
|
+
validated as dates, and `start <= end` when both are given. Distinct from
|
|
305
|
+
`target` (the release-date estimate) and `released` (the actual ship date).
|
|
306
|
+
|
|
307
|
+
Body: `# Title` · **Goal** (the capability in user terms — expands `outcome:`,
|
|
308
|
+
never contradicts it) · **Scope** (narrative reasoning for why these epics ship
|
|
309
|
+
together — the live task list is the README table, don't mirror it) · **Release
|
|
310
|
+
notes** (finalised at release).
|
|
311
|
+
|
|
312
|
+
Template: [`../assets/version-template.md`](../assets/version-template.md).
|
|
313
|
+
|
|
314
|
+
## `epic.md` — the plan-it-together doc (TEMPORARY)
|
|
315
|
+
|
|
316
|
+
One per epic, scaffolded by `work.py epic-new`. An **epic** is the tier between
|
|
317
|
+
version and task: the coherent goal you plan as a whole, and the
|
|
318
|
+
folder holding the tasks that deliver it.
|
|
319
|
+
|
|
320
|
+
```yaml
|
|
321
|
+
---
|
|
322
|
+
type: epic
|
|
323
|
+
created: 2026-08-01
|
|
324
|
+
updated: 2026-08-01
|
|
325
|
+
covers: [profiles/AC-03, sharing/AC-01] # FEATURE-QUALIFIED
|
|
326
|
+
---
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
`covers` — the feature acceptance criteria this epic moves, each **qualified by
|
|
330
|
+
its feature**. That is the one format difference from a task's bare `covers:
|
|
331
|
+
[AC-01]`, and it exists because an epic **spans features** by design (the org
|
|
332
|
+
tier touches `profiles`, `sharing`, `content-trust` and `accounts` at
|
|
333
|
+
once), so a bare id would name several criteria at the same time. An epic with
|
|
334
|
+
no `covers:` is a lint: work that moves no stated criterion is work nobody can
|
|
335
|
+
tell is finished.
|
|
336
|
+
|
|
337
|
+
Body: `# Title` · **Goal** (what a user can newly do, one paragraph) · **Plan**
|
|
338
|
+
(see below — THE design) · **Governance this implies** · **Non-goals**.
|
|
339
|
+
|
|
340
|
+
**`## Plan` is the design, written ONCE for the whole goal, and it replaced the
|
|
341
|
+
per-task `plan.md`.** As-found (`path:line`, confirmed by the sweep) · target
|
|
342
|
+
architecture + the shared seams every task builds against · the constraining
|
|
343
|
+
rules (`work.py rules --task` over the union of the tasks' `code`
|
|
344
|
+
regions) · the slices in landing order · the forward-compat pass. Budgets are the
|
|
345
|
+
ones `plan.md` used to carry: As-found ≤40 lines, Architecture ≤80.
|
|
346
|
+
|
|
347
|
+
This is the tier's whole purpose. Before it, every small task re-ran the same
|
|
348
|
+
as-found, the same blast-radius sweep and the same seam argument — expensive, and
|
|
349
|
+
worse, two tasks that analysed separately reached two different answers. A task
|
|
350
|
+
now **executes** this plan. It gets a `plan.md` of its own only for a call this
|
|
351
|
+
document did not settle, and that is a **gap here**: settle it and write it back
|
|
352
|
+
up, so the next task inherits the answer rather than re-deriving it.
|
|
353
|
+
|
|
354
|
+
**`## Governance this implies` is plan-then-apply.** `work/product/**`,
|
|
355
|
+
`work/architecture/**` and `work/quality/**` describe what is true NOW, never a
|
|
356
|
+
proposal — so the domain-doc edits this epic will make are listed here, one
|
|
357
|
+
line each naming the owner file and the change, and applied when the work lands.
|
|
358
|
+
If the epic implies no governance change, say so explicitly.
|
|
359
|
+
|
|
360
|
+
**Lifetime: `work.py release` DELETES this file.** How the work was planned
|
|
361
|
+
stops being true the moment it ships; the folder stays as the grouping of what
|
|
362
|
+
shipped, and git holds the text. So anything durable in **Plan** or
|
|
363
|
+
**Governance this implies** must have landed in its owner doc *before* release.
|
|
364
|
+
|
|
365
|
+
Where it lives: **in the backlog by default** (`epic-new <name>` with no
|
|
366
|
+
`--version`). A version is expensive to open and its boundary is known only the
|
|
367
|
+
day it opens, so an epic is planned first and promoted with `work.py pull <epic>
|
|
368
|
+
--version <v>` — which moves the folder whole and gives its tasks the `queue/`
|
|
369
|
+
bucket they had no need of in the backlog.
|
|
370
|
+
|
|
371
|
+
Template: [`../assets/epic-template.md`](../assets/epic-template.md).
|
|
372
|
+
|
|
373
|
+
## `stories.md` — RETIRED (was version-level)
|
|
374
|
+
|
|
375
|
+
**Retired 2026-07-17.** Stories now live per-feature in
|
|
376
|
+
`work/product/**.md` (§`<feature>.md`), the durable owner of behaviour —
|
|
377
|
+
release/priority tooling *references* behaviour, it never owns it. New versions
|
|
378
|
+
no longer scaffold a `stories.md`; a `version.md`'s **Scope** links the feature
|
|
379
|
+
stories in the cut. Existing files carry a dated deprecation pointer and stay
|
|
380
|
+
for history (git). There is no task-level stories.md either — a task's intent is
|
|
381
|
+
its acceptance criteria in `task.md`, which cite the feature `AC-` ids they serve.
|
|
382
|
+
|
|
383
|
+
## `design.md` — claude.ai/design UI brief ONLY (conditional, version-level)
|
|
384
|
+
|
|
385
|
+
Scaffolded only with `work.py version-new --design` — UI-bearing versions
|
|
386
|
+
need it; an engine-only version doesn't. **This is the claude.ai/design UI
|
|
387
|
+
brief and nothing else** — the version's technical design lives in
|
|
388
|
+
`architecture.md` (below). Sections: `# Title — claude.ai/design brief` ·
|
|
389
|
+
**Goal** · **User stories** (lifted from `stories.md`) · **Audience** ·
|
|
390
|
+
**Workflow** (the two-pass + cleanup instructions that produce
|
|
391
|
+
`wireframes/project/` and `wireframes/ui.md`).
|
|
392
|
+
|
|
393
|
+
The Workflow section is load-bearing — edit it only if the design workflow
|
|
394
|
+
itself changes. Template:
|
|
395
|
+
[`../assets/design-template.md`](../assets/design-template.md).
|
|
396
|
+
|
|
397
|
+
## `architecture.md` — version-level technical brief (conditional, version-level)
|
|
398
|
+
|
|
399
|
+
Scaffolded only with `work.py version-new --architecture` — a version whose
|
|
400
|
+
tasks share seams/contracts a single technical brief should own; a small or
|
|
401
|
+
single-task version doesn't need one. **Not** a UI brief (that's `design.md`)
|
|
402
|
+
and not a duplicate of per-task design: it owns the *cross-task* technical
|
|
403
|
+
design (the "plan FROM this" doc — shared architecture, phased build order,
|
|
404
|
+
rules) while each task's own `plan.md` still owns that task's own
|
|
405
|
+
As-found/Architecture at pickup. Sections: `# Title — technical architecture
|
|
406
|
+
+ phased plan` · **As-found** · **Target architecture** · **Code map** · **Phases**
|
|
407
|
+
(each independently shippable) · **Risks**. The **Code map** is one block
|
|
408
|
+
per `code` region the version touches — its **seam**, **known-next constraints**,
|
|
409
|
+
**consumers** (features/tasks), and **foundation task** — a MAP of
|
|
410
|
+
seams/decisions/links (never a code dump) where Pass-1 topology + the architect's
|
|
411
|
+
foundation reasoning land (`SKILL.md` §Two-pass decomposition). Template:
|
|
412
|
+
[`../assets/architecture-template.md`](../assets/architecture-template.md).
|
|
413
|
+
|
|
414
|
+
## `wireframes/ui.md` — canonical per-page UI spec
|
|
415
|
+
|
|
416
|
+
Produced by Claude Design from `design.md`, in `wireframes/ui.md` (sibling to
|
|
417
|
+
`work/`, alongside `wireframes/project/`). Per changed page: **Layout**
|
|
418
|
+
(ASCII before → after) · **Changed elements** (Does / Looks / Relates to /
|
|
419
|
+
Blast radius, one per changed element) · **Backend changes deduced** (the
|
|
420
|
+
bridge into each task's `plan.md`). Scope tightly — only changed elements
|
|
421
|
+
appear. A task may anchor-link sections of this from its `plan.md`.
|
|
422
|
+
|
|
423
|
+
## `work/product/` — the product layer (orthogonal to `versions/`)
|
|
424
|
+
|
|
425
|
+
A **separate, sibling tree** to `versions/` — not nested under a version, because a
|
|
426
|
+
feature is a durable product concept that can span many release cuts (idea → defined
|
|
427
|
+
in one, building → shipped over several more), while a version is one time-boxed
|
|
428
|
+
cut. The only bridge between the two trees is `task.md`'s `owner:` field (above) —
|
|
429
|
+
one-directional, so nothing here ever lists its tasks back.
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
work/product/
|
|
433
|
+
├── README.md — what this domain owns (the product, described)
|
|
434
|
+
├── <feature>.md — the durable behaviour spec
|
|
435
|
+
└── wireframes/<feature>/ — appears only once a wireframe exists; never scaffolded
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**There is no app tier, and no folder tier.** The repo is ONE product, so a feature
|
|
439
|
+
is a FILE directly under `product/`, its name is globally unique, and `owner:` is a
|
|
440
|
+
bare feature name. `app-new` and `feature-new --app` are gone with it.
|
|
441
|
+
|
|
442
|
+
**And the path is permanent.** A spec is never a folder holding one file: a folder
|
|
443
|
+
that appears the day a sibling arrives is a path that changes on file count, which
|
|
444
|
+
breaks every inbound citation on a day unrelated to the content. So a sibling goes
|
|
445
|
+
in a KIND folder next to the spec (`product/wireframes/<feature>/`), the way a
|
|
446
|
+
version's wireframes already sit beside `version.md` rather than inside it.
|
|
447
|
+
|
|
448
|
+
### `<feature>.md` — the durable behaviour spec
|
|
449
|
+
|
|
450
|
+
**The sole source of truth for what a feature should do**, independent of the
|
|
451
|
+
code. If code and this file disagree, the code is what's wrong (see `SKILL.md`
|
|
452
|
+
§The spec may say the code is wrong). Stories live HERE, next to the feature —
|
|
453
|
+
not at the version level (retired).
|
|
454
|
+
|
|
455
|
+
```yaml
|
|
456
|
+
---
|
|
457
|
+
type: feature
|
|
458
|
+
state: defined # idea | defined | building | shipped | retired — evidence-derived
|
|
459
|
+
depends_on: [other-feature] # optional, multi-valued — an enabler feature this one needs first
|
|
460
|
+
log:
|
|
461
|
+
- 2026-08-01 defined (created) # the state TIMELINE — one dated line per transition
|
|
462
|
+
---
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
`depends_on` (optional) — a **bare `<feature>`**; there is no app tier to
|
|
466
|
+
qualify against, and an app-qualified value is itself a lint warning.
|
|
467
|
+
Existence-checked as a `work.py list` lint warning, same as `owner:` on tasks.
|
|
468
|
+
|
|
469
|
+
No `# Title` heading (unlike every other spine file) — a feature's display name is
|
|
470
|
+
its kebab-case folder name. Body sections, in order:
|
|
471
|
+
|
|
472
|
+
1. **Problem** — what's wrong or needed, in product language.
|
|
473
|
+
2. **Stories** — the value/context lens (user story *or* job story), NOT the
|
|
474
|
+
container. Each story points at the `B-`/`AC-` ids it frames. Intent, not a
|
|
475
|
+
backlog — one or a few.
|
|
476
|
+
3. **Behaviour** — `B-nn` observable rules: what the app DOES, phrased
|
|
477
|
+
independent of implementation. Links the feature's own `wireframes/` folder
|
|
478
|
+
once one exists (via `artifact-design`) rather than restating it.
|
|
479
|
+
4. **Acceptance criteria** — `AC-nn` checkable conditions that bound "done"
|
|
480
|
+
(observable condition → outcome, incl. failure/edge). Plain IDed bullets by
|
|
481
|
+
default; Given/When/Then only where a gnarly rule earns it. **The trace
|
|
482
|
+
unit**: a task's `owner:` cites the AC ids it implements, and a test declares
|
|
483
|
+
them with `covers("<feature>/AC-nn")` or `{ tag: ["@<feature>/AC-nn"] }` — a
|
|
484
|
+
binding the runner reads, so it cannot outlive the test that carries it.
|
|
485
|
+
5. **Non-goals** — what the feature deliberately does NOT do (bounds the spec).
|
|
486
|
+
|
|
487
|
+
`B-`/`AC-` ids are unique **within the feature**; never reuse an id after ship.
|
|
488
|
+
|
|
489
|
+
`state` is EVIDENCE-DERIVED, not hand-mood (see `SKILL.md` §Feature state):
|
|
490
|
+
`idea` (open product questions) → `defined` (all sections complete) →
|
|
491
|
+
`building` (an in-progress task targets it) → `shipped` (every in-scope AC has
|
|
492
|
+
passing evidence + a release ref) → `retired` (a durable rule says it's gone). It describes **what is true now**, never an
|
|
493
|
+
intention — an AC that states a plan belongs in the plan until the
|
|
494
|
+
evidence exists.
|
|
495
|
+
`work.py list` lints the bookkeeping (id uniqueness, valid links, no `shipped`
|
|
496
|
+
without evidence).
|
|
497
|
+
|
|
498
|
+
Feature names are **globally unique**, like every other name in `work/` — there is
|
|
499
|
+
no app tier to scope them within, which is exactly why `owner:` is a bare
|
|
500
|
+
feature name and `product/<feature>.md` is a feature's whole address.
|
|
501
|
+
|
|
502
|
+
Scaffolded by `work.py feature-new <feature> [--state ...]`. Template:
|
|
503
|
+
[`../assets/feature-template.md`](../assets/feature-template.md).
|
|
504
|
+
|
|
505
|
+
No product-decision ledger: rationale for a feature lives inline in its own
|
|
506
|
+
`<feature>.md` prose, the same way a task's rationale lives in `task.md` — never a
|
|
507
|
+
second ledger-shaped file.
|
|
508
|
+
|
|
509
|
+
## Conditional task-level files
|
|
510
|
+
|
|
511
|
+
Created only when the state needs them; never pre-emptively. **One canonical
|
|
512
|
+
home each — no suffix variants** (`plan-x.md`, `state-y.md` are lint errors;
|
|
513
|
+
a task that needs two plans is two tasks).
|
|
514
|
+
|
|
515
|
+
- **Research** — *either* a single `research.md` (raw external findings:
|
|
516
|
+
quotes, links, data) *or*, when it's large, a `research/` subfolder with a
|
|
517
|
+
mandatory `00-report.md` (≤30 lines, decisions-relevant findings only) plus
|
|
518
|
+
the raw notes. Never both forms, never topic-suffixed siblings. The read
|
|
519
|
+
order points at the summary; drill into the rest only when a question
|
|
520
|
+
demands it.
|
|
521
|
+
- **`ui.md`** — task-scoped per-page UI spec (same format as
|
|
522
|
+
`wireframes/ui.md`); folded back into the canonical spec when the task
|
|
523
|
+
settles.
|
|
524
|
+
Working checklists (`hygiene.*` findings) are **not** task
|
|
525
|
+
files — they live in native `TodoWrite`. Work them off the todo list, not a
|
|
526
|
+
`slop.md` / `review.md` in the folder (those were retired).
|
|
527
|
+
|
|
528
|
+
There is no task-level `decisions.md` and no version-level `HANDOFF.md` /
|
|
529
|
+
`roadmap.md`: durable rules live in the domain that owns them (small task-local
|
|
530
|
+
ones only surface in a `handoff.md` if you're handing off), and live status is
|
|
531
|
+
the README table + each task's bucket.
|
|
532
|
+
|
|
533
|
+
## `work/research/<dir>/` — shared research pool
|
|
534
|
+
|
|
535
|
+
Cross-task research (engine audits, market analysis, prior art) lives here,
|
|
536
|
+
one subfolder per topic. **Each subfolder carries a `00-*.md` summary**
|
|
537
|
+
(canonically `00-report.md`; `00-synthesis.md` / `00-analysis.md` etc. also
|
|
538
|
+
count), ≤30 lines, as its distilled entry point — that's what the read order
|
|
539
|
+
points at; the numbered files behind it are drilled into only when needed.
|