@oneie/claude 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/agents/abm-strategist.md +67 -1
- package/agents/ads-meta.md +67 -1
- package/agents/analyst.md +67 -1
- package/agents/animator.md +108 -0
- package/agents/architect.md +269 -20
- package/agents/brand-guardian.md +67 -1
- package/agents/brand-strategist.md +67 -1
- package/agents/campaign-content.md +67 -1
- package/agents/campaign-email.md +67 -1
- package/agents/campaign-sms.md +67 -1
- package/agents/campaign-social.md +67 -1
- package/agents/cco.md +83 -2
- package/agents/ceo.md +108 -11
- package/agents/chairman.md +197 -0
- package/agents/cmo.md +82 -2
- package/agents/community-greeter.md +67 -1
- package/agents/community-moderator.md +67 -1
- package/agents/compliance.md +67 -1
- package/agents/copywriter.md +67 -1
- package/agents/creative-strategist.md +67 -1
- package/agents/cro.md +81 -1
- package/agents/cto.md +266 -28
- package/agents/customer-interviewer.md +67 -1
- package/agents/customer-researcher.md +67 -1
- package/agents/customer-success-manager.md +67 -1
- package/agents/customer-trainer.md +67 -1
- package/agents/cxo.md +82 -1
- package/agents/demand-creator.md +67 -1
- package/agents/demo-mover.md +67 -1
- package/agents/demo-specialist.md +67 -1
- package/agents/demo-thai-family-law.md +67 -1
- package/agents/designer.md +67 -1
- package/agents/discovery-caller.md +67 -1
- package/agents/doctor.md +269 -0
- package/agents/educate-coach.md +67 -1
- package/agents/elevate-tutor.md +67 -1
- package/agents/email-lifecycle-marketer.md +67 -1
- package/agents/engage-specialist.md +67 -1
- package/agents/events-coordinator.md +67 -1
- package/agents/foundation-builder.md +67 -1
- package/agents/funnel-architect.md +67 -1
- package/agents/gift-creator.md +67 -1
- package/agents/google-ads.md +67 -1
- package/agents/guide.md +67 -1
- package/agents/helpdesk-dispatcher.md +67 -1
- package/agents/hook-specialist.md +67 -1
- package/agents/identify-optimizer.md +67 -1
- package/agents/implementer.md +313 -45
- package/agents/incident-commander.md +67 -1
- package/agents/insights-lead.md +87 -1
- package/agents/journey-runner.md +67 -1
- package/agents/linkedin-ads.md +67 -1
- package/agents/live-sales-chat.md +67 -1
- package/agents/market-researcher.md +67 -1
- package/agents/media-buyer.md +67 -1
- package/agents/memory-keeper.md +195 -0
- package/agents/movers-customer-researcher.md +67 -1
- package/agents/movers-foundation-builder.md +67 -1
- package/agents/movers-market-researcher.md +67 -1
- package/agents/movers-pricing-strategist.md +67 -1
- package/agents/nurture-architect.md +67 -1
- package/agents/offer-architect.md +67 -1
- package/agents/onboarder.md +67 -1
- package/agents/onboarding-specialist.md +67 -1
- package/agents/operations-dashboard.md +87 -1
- package/agents/perf-engineer.md +333 -37
- package/agents/playbook-writer.md +67 -1
- package/agents/plg-strategist.md +67 -1
- package/agents/positioning-architect.md +67 -1
- package/agents/press-officer.md +67 -1
- package/agents/pricing-strategist.md +67 -1
- package/agents/privacy-officer.md +67 -1
- package/agents/referral-manager.md +67 -1
- package/agents/refine-analyst.md +67 -1
- package/agents/release-manager.md +446 -39
- package/agents/renewals-upsell-rep.md +67 -1
- package/agents/review-engineer.md +319 -45
- package/agents/rewards-steward.md +67 -1
- package/agents/sales-call-coach.md +67 -1
- package/agents/sales-closer.md +67 -1
- package/agents/security-auditor.md +343 -48
- package/agents/sell-closer.md +67 -1
- package/agents/share-amplifier.md +67 -1
- package/agents/social-media-manager.md +67 -1
- package/agents/storyteller.md +301 -0
- package/agents/strategist.md +67 -1
- package/agents/strategy-aligner.md +67 -1
- package/agents/support-agent.md +67 -1
- package/agents/tagger.md +327 -0
- package/agents/tech-writer.md +195 -22
- package/agents/test-engineer.md +398 -29
- package/agents/tiktok-ads.md +67 -1
- package/agents/tracking-engineer.md +67 -1
- package/agents/trailkeeper.md +181 -0
- package/agents/upsell-strategist.md +67 -1
- package/agents/voice.md +67 -1
- package/agents/w1-recon.md +1 -1
- package/agents/w2-decide.md +1 -1
- package/agents/w3-edit.md +8 -2
- package/agents/w4-verify.md +13 -0
- package/agents/workflow-optimiser.md +81 -1
- package/commands/close.md +916 -160
- package/commands/deploy.md +102 -724
- package/commands/do.md +58 -2
- package/commands/sweep.md +159 -0
- package/commands/tasks.md +222 -0
- package/hooks/scripts/dev-only.sh +135 -0
- package/hooks/scripts/git-add-guard.sh +37 -2
- package/hooks/scripts/session-start.sh +32 -4
- package/package.json +1 -1
- package/rules/scripts.md +85 -0
- package/scripts/CLAUDE.md +315 -0
- package/scripts/ad-copy-lint.sh +656 -0
- package/scripts/agent-actor-parity.sh +129 -0
- package/scripts/blocks-manifest-cached.sh +100 -0
- package/scripts/chat-context-check.sh +89 -0
- package/scripts/chrome.mjs +18 -0
- package/scripts/close-metrics.sh +587 -0
- package/scripts/close-owner.sh +326 -0
- package/scripts/db-sync-lock-check.sh +116 -0
- package/scripts/deploy-emit.sh +311 -0
- package/scripts/deploy-gate-check.sh +155 -0
- package/scripts/deploy-ready.sh +78 -0
- package/scripts/deploy-record.sh +605 -0
- package/scripts/deploy-schema-check.sh +58 -0
- package/scripts/deploy.sh +393 -243
- package/scripts/do-auto.sh +127 -26
- package/scripts/do-board.sh +429 -0
- package/scripts/do-close.sh +1184 -0
- package/scripts/do-consumer-sweep.sh +18 -1
- package/scripts/do-decide.sh +476 -0
- package/scripts/do-fleet.sh +8 -2
- package/scripts/do-plan-json.mjs +110 -12
- package/scripts/do-prove-selftest.sh +108 -0
- package/scripts/do-prove.sh +86 -10
- package/scripts/do-rank.py +200 -3
- package/scripts/do-reconcile.sh +73 -12
- package/scripts/do-signal.sh +101 -23
- package/scripts/do-smoke.sh +18 -1
- package/scripts/do-w4-gates.sh +11 -1
- package/scripts/do-world-check.sh +153 -0
- package/scripts/download-stats.sh +172 -0
- package/scripts/factory-brief-check.sh +330 -0
- package/scripts/factory-check.sh +18 -1
- package/scripts/factory-close-check.sh +257 -0
- package/scripts/factory-emit.sh +211 -0
- package/scripts/factory-executor-check.mjs +353 -0
- package/scripts/factory-peak.sh +301 -0
- package/scripts/factory-repo.sh +71 -0
- package/scripts/factory-review-check.mjs +61 -0
- package/scripts/factory-tasks-check.sh +18 -1
- package/scripts/fixtures/factory-brief-real.md +44 -0
- package/scripts/flywheel-outcome.sh +63 -0
- package/scripts/gate-reaper-check.sh +98 -0
- package/scripts/gate-reaper.sh +9 -0
- package/scripts/gate-watchdog.sh +619 -0
- package/scripts/gc-content-check.sh +142 -0
- package/scripts/gh-traffic-capture.sh +153 -0
- package/scripts/govern-order-check.sh +202 -0
- package/scripts/governor-doors-check.sh +86 -5
- package/scripts/health.sh +448 -0
- package/scripts/id-inventory.mjs +418 -0
- package/scripts/incident.sh +212 -0
- package/scripts/land.sh +755 -45
- package/scripts/lib/gc-finished.sh +77 -0
- package/scripts/livekit-ratchet.sh +18 -1
- package/scripts/machine-check.sh +1 -1
- package/scripts/memory-index-budget.sh +79 -0
- package/scripts/npm-downloads.sh +109 -0
- package/scripts/one-agents.mjs +204 -8
- package/scripts/one-resume.sh +31 -3
- package/scripts/pr-body.sh +335 -0
- package/scripts/preview-fd-check.sh +289 -0
- package/scripts/redirect-lint.sh +169 -0
- package/scripts/release.sh +40 -6
- package/scripts/resume-lost-sessions.sh +68 -0
- package/scripts/shoot-pages.mjs +140 -0
- package/scripts/signal-meta-backfill.ts +451 -0
- package/scripts/signal-watch.sh +63 -6
- package/scripts/speed-cache-check.sh +12 -2
- package/scripts/sweep.sh +426 -0
- package/scripts/task-titles-dump.ts +101 -0
- package/scripts/test-cached.sh +47 -10
- package/scripts/test-lanes.sh +14 -0
- package/scripts/thread-name-backfill.ts +215 -0
- package/scripts/triage-shape-check.sh +149 -0
- package/scripts/tsc-cached.sh +155 -8
- package/scripts/typedb-flake-check.sh +3 -1
- package/scripts/urls-lint.sh +8 -0
- package/scripts/verify-board-doors.sh +80 -0
- package/scripts/verify-fast.sh +159 -6
- package/scripts/worktree-up.sh +21 -3
- package/skills/astro/SKILL.md +9 -3
- package/skills/astro/optimize-performance.md +3 -2
- package/skills/cloudflare/SKILL.md +3 -2
- package/skills/cloudflare-security-audit/AI-AND-LLM.md +83 -0
- package/skills/cloudflare-security-audit/ATTACK-CLASSES.md +130 -0
- package/skills/cloudflare-security-audit/CLIENT-SIDE.md +83 -0
- package/skills/cloudflare-security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
- package/skills/cloudflare-security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
- package/skills/cloudflare-security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
- package/skills/cloudflare-security-audit/HUNTING.md +251 -0
- package/skills/cloudflare-security-audit/LICENSE +21 -0
- package/skills/cloudflare-security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
- package/skills/cloudflare-security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
- package/skills/cloudflare-security-audit/PROVENANCE.md +78 -0
- package/skills/cloudflare-security-audit/RECONNAISSANCE.md +156 -0
- package/skills/cloudflare-security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
- package/skills/cloudflare-security-audit/SKILL.md +192 -0
- package/skills/cloudflare-security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
- package/skills/cloudflare-security-audit/VALIDATION-AND-REPORTING.md +186 -0
- package/skills/cloudflare-security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
- package/skills/cloudflare-security-audit/report-schema.json +461 -0
- package/skills/cloudflare-security-audit/validate-coverage-ledger.cjs +872 -0
- package/skills/cloudflare-security-audit/validate-coverage-ledger.test.cjs +740 -0
- package/skills/cloudflare-security-audit/validate-findings.cjs +773 -0
- package/skills/cloudflare-security-audit/validate-findings.test.cjs +652 -0
- package/skills/deploy/REFERENCE.md +713 -0
- package/skills/deploy/SKILL.md +140 -0
- package/skills/fleet-audit/SKILL.md +58 -0
- package/skills/meeting/SKILL.md +220 -0
- package/skills/planning/SKILL.md +256 -0
- package/skills/shadcn/SKILL.md +1 -1
- package/skills/typedb/SKILL.md +7 -0
- package/skills/voice/SKILL.md +94 -6
- package/skills/voice/corpus-check.sh +87 -0
- package/templates/template-agent.md +7 -1
- package/templates/template-feature.md +9 -0
- package/templates/template-todo.md +29 -0
package/commands/do.md
CHANGED
|
@@ -171,12 +171,16 @@ The pruned spine by tier:
|
|
|
171
171
|
```
|
|
172
172
|
PATCH AIM → BUILD → VERIFY (edit + W4 verify gate; 0 spawns)
|
|
173
173
|
FIX AIM → SURVEY → [INVESTIGATE] → DOCS → BUILD → TEST → PROVE → LEARN
|
|
174
|
-
FEATURE AIM → PROMISE → SURVEY → DESIGN ▸clarify → DOCS → PLAN ▸analyze → TEST → BUILD → VERIFY → PROVE → TEACH → SHIP → LEARN
|
|
174
|
+
FEATURE AIM → PROMISE → SURVEY → DESIGN ▸clarify → DOCS → PLAN ▸analyze ▸board ▸meet → TEST → BUILD → VERIFY → PROVE → TEACH → SHIP → LEARN
|
|
175
175
|
SCHEMA = FEATURE + Substrate reconcile at max
|
|
176
176
|
|
|
177
177
|
▸ Docs-first order: DOCS (the full doc set = the spec) is written right after DESIGN; TEST (from those docs)
|
|
178
178
|
is written before BUILD; BUILD makes docs+tests true; PROVE validates the shipped thing against the docs;
|
|
179
179
|
TEACH is now reconcile-not-author (correct any doc↔reality drift so every doc ends true).
|
|
180
|
+
|
|
181
|
+
▸ Board-then-meet: ▸board mirrors the plan's batch DAG onto the board as chained rows; ▸meet convenes
|
|
182
|
+
the heads who will claim them. BOTH run before BUILD, and in that order — a meeting called before the
|
|
183
|
+
rows are chained hands every head every cycle at once, and the fan-out is a race.
|
|
180
184
|
```
|
|
181
185
|
|
|
182
186
|
`INVESTIGATE` rides any tier when the work touches code we didn't just write — it's the "understand before you change" step, not a separate tier.
|
|
@@ -199,6 +203,8 @@ For each **enabled** stop (per the pruned spine), in order: check if its artifac
|
|
|
199
203
|
| **DOCS** | doc set exists ∧ reconciles | the **full** doc set, written as the spec — `docs` skill picks the types, `writer` drafts: always `-docs.md`; + `-tutorial`/`-how-to`/`-reference`/`-agents-docs` per condition. Written in the reader's language *before* code — this is the acceptance spec PROVE validates against. Not a draft: the real thing | sonnet · medium |
|
|
200
204
|
| **PLAN** | `test -f text/<slug>-todo.md` | `/create todo` + `text/template-todo.md` — cycles + deliverables derived from the docs (each cycle ships a documented observable) | sonnet · medium |
|
|
201
205
|
| ↳ *ANALYZE* | — | `do-analyze.sh text/<slug>-todo.md [--strict-promise]` — CRITICAL exit 1 blocks BUILD. Also reads `text/<slug>.md` and HIGH-warns any promise `accept:` no `C<n>` cycle carries (the todo's `outcome:` and `Correct-course` blocks are not corpus — that's where the gap hides); `--strict-promise` makes an uncovered deliverable CRITICAL | bash · none |
|
|
206
|
+
| ↳ *BOARD* | `bash .claude/scripts/do-board.sh <slug> --status` shows a tid and a live status on every cycle (drift 0) | **`bash .claude/scripts/do-board.sh <slug>`** — the ONE minter of cycle rows (`do-signal.sh --task-plan` delegates to it). Reuses the plan row do-auto already filed as `anchor:`, walks the batches **in order**, one `tasks:subtask` per cycle (`parent` = anchor, `blockedBy` = the previous batch's tids, tags `slug:<slug>-c<n>` + `plan:<slug>`, notes = the cycle's goal delta + the close command), and writes `anchor:` + `cycle_rows:` back after **every batch**. Idempotent (an existing row only gets its edges re-asserted with `tasks:depend`) and resumable. `--dry-run` walks the same loop against a fake door. **The return leg already exists:** the engine's `--task-cycle <slug> C<n> done` resolves the same `slug:` tag, so closing a cycle unblocks the next batch on the board with no new wire | bash · none |
|
|
207
|
+
| ↳ *MEET* *(fan-out plans only — any batch with ≥2 cycles)* | a thread id in `/u/<slug>/in` and every claimable cycle row carries a **named** assignee (`do-board.sh <slug> --status`) | the `meeting` skill — chairman convenes, heads **claim · delegate · chat · chain**, ledger read back from D1. Under the shared gateway key every head claims as the workspace (`@one`), so a head **names itself with `tasks:reassign` after `tasks:claim`** — the two calls are one act. Skipped when the plan is one head wide | opus · high |
|
|
202
208
|
| **TEST** | test file in the repo folder | test-first **from the docs** — one assertion per documented observable; the docs' claims become the executable acceptance tests *before* code exists | sonnet · low |
|
|
203
209
|
| **BUILD** | survey verdict = `build` (≥70% match → `expose`/`extend` instead) | the BUILD engine (Step 3) — make the docs + tests true | sonnet · low–medium |
|
|
204
210
|
| **VERIFY** | rubric + ratchet pass | `do-reconcile.sh <canon>` per category + rubric composite ≥ 0.65 | haiku · medium |
|
|
@@ -207,6 +213,56 @@ For each **enabled** stop (per the pruned spine), in order: check if its artifac
|
|
|
207
213
|
| **SHIP** | changelog / README row | `/release` + adoption signal | sonnet · low |
|
|
208
214
|
| **LEARN** | learnings entry written | close: learnings + cost signal + trust write | — |
|
|
209
215
|
|
|
216
|
+
### The board is the plan's other rendering — and the gate that enforces it
|
|
217
|
+
|
|
218
|
+
A plan's cycles and the board's rows are **the same work, twice**. Until ▸board existed they were
|
|
219
|
+
two graphs that drifted: `batches:` held the ordering in markdown, D1 held it in `blocks` edges,
|
|
220
|
+
and nothing joined them.
|
|
221
|
+
|
|
222
|
+
**The join is not bookkeeping. `tasks:claim` is BLOCKER-GATED and reads the very `blocks` edge
|
|
223
|
+
`tasks:depend` writes.** So a plan mirrored onto the board **cannot be claimed out of order** —
|
|
224
|
+
the substrate enforces the batch DAG that used to be a comment. A plan that is *not* mirrored
|
|
225
|
+
hands every agent every cycle at once.
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
batches: [[C1],[C2,C3],[C4]] ≡ C2.blockedBy=[C1] · C3.blockedBy=[C1] · C4.blockedBy=[C2,C3]
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**If the two disagree, the BOARD is truth** — it is what the gate reads. `do-plan-json.sh` emits
|
|
232
|
+
both so the drift is visible rather than silent.
|
|
233
|
+
|
|
234
|
+
**Mint with `tasks:subtask`, never `tasks:create`.** Subtask writes the row, its notes, its tags,
|
|
235
|
+
its `containment` edge to the anchor **and** every `blockedBy` in ONE pipeline — precisely so a
|
|
236
|
+
child never appears claimable with an empty body or missing ordering. A cycle row made with
|
|
237
|
+
`tasks:create` is an orphan that some head claims before it is ready.
|
|
238
|
+
|
|
239
|
+
**A cycle with no row is invisible to every agent but the one running `/do`.** Measured
|
|
240
|
+
2026-09-12: fifteen filed rungs returned in **zero** of `tasks:everywhere`'s reads at any limit
|
|
241
|
+
(hard cap 200, weight DESC, `tag` ignored) — which, not neglect, was why every one had sat open.
|
|
242
|
+
Read rungs with `tasks:list` by tag, and treat a count from a capped door as not a count.
|
|
243
|
+
|
|
244
|
+
### ▸meet — the four verbs, and when the chairman convenes
|
|
245
|
+
|
|
246
|
+
A meeting is **not** where heads report. It is where they take a row, hand a row, argue on the
|
|
247
|
+
row, and put the rows in order:
|
|
248
|
+
|
|
249
|
+
| verb | door |
|
|
250
|
+
|---|---|
|
|
251
|
+
| **claim** | `tasks:claim` — blocker-gated; the claimant is the **attested caller**, never a body field. Under the gateway key that caller is the *workspace* (`@one`) for every head, so a spawned head follows the claim with `tasks:reassign {assignee: <its own slug>}` or the ledger cannot say who took what |
|
|
252
|
+
| **delegate** | `tasks:reassign` (moves the row in place) · `tasks:announce` (to the world by tags — returns `matched`, and **matched is not delivered**) |
|
|
253
|
+
| **chat** | `tasks:comment` (same D1 store as the inbox thread) · `thread:append` for the room |
|
|
254
|
+
| **chain** | `tasks:subtask` (split) · `tasks:depend` (order) |
|
|
255
|
+
|
|
256
|
+
Protocol: the **`meeting` skill** (`.claude/skills/meeting/SKILL.md`); form:
|
|
257
|
+
`text/template-meeting.md`. Two rules the skill enforces that `/do` depends on — **no head leaves
|
|
258
|
+
without a tid**, and **size the spawn to the budget** (five heads spawned into a budget that
|
|
259
|
+
carried two report nothing at all).
|
|
260
|
+
|
|
261
|
+
**The lease moves to the cycle.** `blockersResolved` reads `containment` too, so once cycle rows hang off the anchor a `tasks:claim` on the plan row is refused as `blocked` for the whole run. `do-signal.sh --task-claim` and `do-fleet.sh` therefore lease the **first open cycle's row** when `cycle_rows:` is filled — the row actually in flight, gated by its own batch.
|
|
262
|
+
|
|
263
|
+
**▸meet is skipped when the plan is one head wide.** A meeting with one attendee is a status
|
|
264
|
+
update, and the checklist refuses it.
|
|
265
|
+
|
|
210
266
|
**Docs are written first, validated last.** `text/<slug>-docs.md` (and the conditional doc set) is the **spec** — authored at DOCS, *before* BUILD, in the reader's language, so the plan and tests are drawn from a sharp description of intended behavior. It is not a draft: it is what the build must make true. At TEACH / plan close the same doc is **validated, not re-authored** — PROVE checks the shipped thing against it, and any drift is reconciled (fix the doc where reality justifiably diverged, or loop BUILD where the code fell short). Every doc ends true. *(This supersedes the old seed-at-PLAN / refine-at-TEACH pattern: the first write is the real spec, the last touch is validation.)*
|
|
211
267
|
|
|
212
268
|
**Doc artifacts — all ten templates, each gated.** Every doc type in `text/docs.md` has a template (`text/templates.md` is the map). The **promise's `derives:` manifest** (`text/<slug>.md` frontmatter) declares which of these the feature spawns; the `docs` skill (`.claude/skills/docs/SKILL.md`) confirms the choice at DESIGN/DOCS — neither forces all ten. **Three are spine (automatic); seven are conditional (write only if the promise's `derives:` set them true, else skip).** All are written *before* BUILD. Copy the template, never write from scratch.
|
|
@@ -232,7 +288,7 @@ The two `↳` rows are **gates, not artifacts**: CLARIFY edits the plan in place
|
|
|
232
288
|
- **`deliverables:`** — the **schedule of work**, enumerated and exhaustive: every item the client receives, each with its own `accept:` check. Not on the schedule = not promised (it goes in the promise's "Out of scope" section, in writing); on the schedule = ships or the whole promise settles broken. Acceptance is indivisible — no partial credit. `assumes:` alongside it lists client-side dependencies, each green at the making (a failing assumption means the contract can't be signed yet).
|
|
233
289
|
- **`proof:`** — the acceptance test that settles the contract: **derived, not authored** — the `&&`-join of every deliverable's `accept:`, verbatim, so the one observable is exhaustive *by construction*. It *becomes* the todo's `outcome:`, the TEST assertion, and the PROVE oracle — named once here, never restated loosely. The schedule ⇄ proof law is enforced by `.claude/scripts/do-promise-lint.sh <slug>` (zero LLM — the PROMISE gate): every `accept:` must appear inside `proof:`, every item must carry an `accept:`, every assumption must hold; `--red` additionally requires the proof RED at PROMISE. Promises predating the schedule (no `deliverables:` block) pass grandfathered.
|
|
234
290
|
- **`derives:`** — the manifest of which artifacts this promise spawns. `/do` reads it to know which conditional stops to backfill (instead of re-deciding at DESIGN); the tier prune still applies on top.
|
|
235
|
-
- **`world:`** — the manifest of runtime the kept promise puts into the substrate: lifecycle moves (+ the signal that records each), workflow steps (the locked kinds), agents (+ their `subscribes:` tags), skills, task tags, tracking marks/warns, routing paths. At DESIGN/PLAN, `/do` presence-checks every entry the same way it checks an artifact — **missing agent → a `template-agent.md` cycle (+ `subscriptions:register` for its tags) · missing skill → `/skill-creator` · missing workflow/lifecycle stage → a cycle in the todo · present → skip**. PROVE reads `tracking.marks` as production observables alongside the docs; the `routing:` paths are where cycle composites and run-time outcomes land, so the ranker routes the next wave toward what worked. The fan-out builds exactly what the promise names — nothing more.
|
|
291
|
+
- **`world:`** — the manifest of runtime the kept promise puts into the substrate: lifecycle moves (+ the signal that records each), workflow steps (the locked kinds), agents (+ their `subscribes:` tags), skills, the tag's own **code** surfaces, task tags, tracking marks/warns, routing paths. At DESIGN/PLAN, `/do` presence-checks every entry the same way it checks an artifact — **missing agent → a `template-agent.md` cycle (+ `subscriptions:register` for its tags) · missing skill → `/skill-creator` · missing workflow/lifecycle stage → a cycle in the todo · present → skip**. The `code:` sub-block is the one kind checked MECHANICALLY rather than by reading — it names `ts`/`astro`/`tql`/`panel`/`command` paths relative to the repo root, and `bash .claude/scripts/do-world-check.sh <slug>` answers missing/present for each under the same rule (exit 1 on a declared path that does not exist). PROVE reads `tracking.marks` as production observables alongside the docs; the `routing:` paths are where cycle composites and run-time outcomes land, so the ranker routes the next wave toward what worked. The fan-out builds exactly what the promise names — nothing more.
|
|
236
292
|
|
|
237
293
|
**Contract-backed promises mint on-chain.** When the promise carries an uncommented `contract:` block (`text/template-feature.md`'s optional on-chain block), PROMISE also runs `bun pay/tools/promise-chain.ts mint <slug>` — it submits the generated `create_promise` with `terms_hash = sha256(text/<slug>.md)` and writes `object_id` + `maker` back into the frontmatter. Unarmed (no signer) → clean exit 2, the promise stays off-chain-only until minted. At close, `do-promise-settle.sh` settles **both halves** from the one proof exit code.
|
|
238
294
|
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# /sweep
|
|
2
|
+
|
|
3
|
+
> **Every finished branch into `dev`, one gate, one sha to promote.**
|
|
4
|
+
> `/sweep` is the door before `/deploy`. It runs `bash .claude/scripts/sweep.sh`
|
|
5
|
+
> — the script is the authority for the procedure; this page never carries a
|
|
6
|
+
> second copy of the steps.
|
|
7
|
+
>
|
|
8
|
+
> ```bash
|
|
9
|
+
> bash .claude/scripts/sweep.sh --dry-run # the whole plan, nothing moved
|
|
10
|
+
> bash .claude/scripts/sweep.sh # doctor · fetch · classify · land · ONE gate · gc · close
|
|
11
|
+
> bash .claude/scripts/sweep.sh --pr # …and open/refresh the dev → main PR
|
|
12
|
+
> ```
|
|
13
|
+
>
|
|
14
|
+
> **Run it from the MAIN tree's copy**, like every other door script — `land.sh`,
|
|
15
|
+
> `release.sh` and `deploy.sh` all derive `ROOT` from `${BASH_SOURCE[0]}/../..`,
|
|
16
|
+
> so a worktree's copy sweeps that worktree (`../CLAUDE.md § The dev → prod loop`).
|
|
17
|
+
|
|
18
|
+
**It does not ship.** It ends by naming the sha and handing the last two doors to
|
|
19
|
+
`release-manager` — see `/deploy`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## What it costs: one gate, not N
|
|
24
|
+
|
|
25
|
+
`land.sh` gates each branch **with dev merged in**. That is a different tree per
|
|
26
|
+
branch, so six branches is six full computes plus the release gate. The memo does
|
|
27
|
+
not help: `test-cached.sh` is content-addressed, so the same suite on the same
|
|
28
|
+
tree is already free — the cost was never double-calling, it was N different trees.
|
|
29
|
+
|
|
30
|
+
`/sweep` inverts the order:
|
|
31
|
+
|
|
32
|
+
1. merge every **ready** branch into `dev` first,
|
|
33
|
+
2. read each branch's own typecheck as a **memo lookup** (`tsc-cached.sh --probe`
|
|
34
|
+
— a read, never a compute),
|
|
35
|
+
3. run **one** gate on the integrated tree.
|
|
36
|
+
|
|
37
|
+
N+1 computes becomes 1. The lane is chosen deterministically from what was swept:
|
|
38
|
+
`fast` by default, **`full`** whenever the merge touched `schema/`,
|
|
39
|
+
`packages/sdk/` or auth — the canon's own escalation rule, applied by the script
|
|
40
|
+
rather than remembered by a human.
|
|
41
|
+
|
|
42
|
+
**What that trade costs, said out loud.** A batched gate cannot name which of N
|
|
43
|
+
branches owns a red. So a red **resets `dev` to the pre-sweep sha** and prints the
|
|
44
|
+
`land.sh` line for each branch — where the four-owner diagnosis (branch · dev ·
|
|
45
|
+
seam · environment, measured 2026-09-12) still lives. You pay the N gates only in
|
|
46
|
+
the case that needs them. **`land.sh`'s own default is unchanged**; use it
|
|
47
|
+
directly for a single branch.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## The four classes
|
|
52
|
+
|
|
53
|
+
Classification is `lib/gc-finished.sh` — the same predicate the sweep that removes
|
|
54
|
+
worktrees uses, sourced, not re-implemented. It is **content identity**, never
|
|
55
|
+
`rev-list --count`: a branch whose patch landed under another sha (rebase,
|
|
56
|
+
squash, cherry-pick) is finished, and a merge commit carrying unique files is not.
|
|
57
|
+
|
|
58
|
+
| Class | Test | What happens |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `infra` | `main` · `dev` · `release` | never touched |
|
|
61
|
+
| `landed` | its content is already in `dev` | left for the gc, which measures against **`origin/main`** |
|
|
62
|
+
| `busy` | ahead, but its worktree has uncommitted **work** or something is running in it | skipped, and the reason is named |
|
|
63
|
+
| `ready` | ahead, clean, idle | merged |
|
|
64
|
+
|
|
65
|
+
Build output is not work: `one.ie/web/.astro`, `.wrangler`, `.preview`,
|
|
66
|
+
`node_modules` and friends never make a branch busy.
|
|
67
|
+
|
|
68
|
+
**gc measures against `origin/main`, after a fetch, and that is deliberate.** A
|
|
69
|
+
branch is finished when it is *released*, not when it is integrated — a reviewer
|
|
70
|
+
asking for changes must not find the branch already deleted. A stale local ref
|
|
71
|
+
makes landed work look unmerged (measured 2026-09-07: local main 131 behind, 1.0 GB
|
|
72
|
+
hoarded), which is why the fetch is the first git command.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## The doctor holds the box
|
|
77
|
+
|
|
78
|
+
Phase 0 is `health.sh --box --json`. A sweep **starts gates**, and on a paging box
|
|
79
|
+
a gate runs long — a slow gate is indistinguishable from a red one at the wall
|
|
80
|
+
clock, which is how a good tree gets diagnosed as broken code.
|
|
81
|
+
|
|
82
|
+
| Verdict | What sweep does |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `HEALTHY` | go |
|
|
85
|
+
| `DEGRADED` | go — it funds one concurrent gate, and a sweep needs exactly one |
|
|
86
|
+
| `UNHEALTHY` | reap **only if `orphans > 0`**, re-read, then refuse (exit 3) and name the agent |
|
|
87
|
+
|
|
88
|
+
On a refusal, spawn the doctor and re-run:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Agent({ subagent_type: "doctor", model: "opus",
|
|
92
|
+
prompt: "health.sh says: <why>. Reclaim what nothing is coming back for, then report the verdict." })
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`--dry-run` is **never** refused for the box — a plan starts no gates, and a tool
|
|
96
|
+
that refuses a plan teaches you to pass `--no-doctor` by reflex. The flag you
|
|
97
|
+
reach for out of habit is the flag that stops protecting you.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## The numeric close
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
ready=2 merged=2 landed=4 busy=1 infra=3 gates=1 lane=fast box=DEGRADED dev=85250c0b6 ahead_of_main=8
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Every number is counted, not narrated. `gates` is the one that matters: it is the
|
|
108
|
+
count of real computes the sweep cost, and it is 1.
|
|
109
|
+
|
|
110
|
+
Exit codes are distinct and named, because "it refused" is not a finding:
|
|
111
|
+
`0` ok · `2` usage · `3` box refused · `4` integrated gate RED (dev reset) ·
|
|
112
|
+
`5` a merge conflicted (dev reset) · `6` nothing to sweep and nothing to promote.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Prove it can go red
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
bash .claude/scripts/sweep.sh --self-test # 22 assertions, ~3s
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The live estate is usually three worktrees and no feature branches, so a green
|
|
123
|
+
`/sweep` proves only that a no-op is a no-op. The self-test mints throwaway
|
|
124
|
+
estates instead, and covers **both halves** — the read-only classifier and the
|
|
125
|
+
half that moves refs:
|
|
126
|
+
|
|
127
|
+
| What it proves | How |
|
|
128
|
+
|---|---|
|
|
129
|
+
| every class | one branch each: landed **under a different sha**, ready, dirty, ephemeral-dirt-only, infra |
|
|
130
|
+
| liveness, both ways | it must see a process under the tree (`exec -a`, because `pgrep -f` matches argv, not cwd) and must **not** match a prefix neighbour |
|
|
131
|
+
| the merge | ready branches land, the dirty one does not, `gates=1` |
|
|
132
|
+
| the lane | a `schema/` touch selects `full` without being asked |
|
|
133
|
+
| **a red gate** | exit 4, `dev` **back at the pre-sweep sha**, and the `land.sh` line named per branch |
|
|
134
|
+
| **a conflict** | exit 5, `dev` back at the pre-sweep sha — a conflict is not a red suite and does not share its code |
|
|
135
|
+
| the seam itself | `SWEEP_GATE_CMD` is read **only** under `SWEEP_ROOT`, so there is no way to spell "skip the gate" in a real run |
|
|
136
|
+
|
|
137
|
+
The two reset paths are the reason the merge half is tested at all: a reset
|
|
138
|
+
nobody ever runs is a reset nobody knows is broken, and the ref it fails to
|
|
139
|
+
restore is someone's branch. Deleting the reset line makes the suite go **RED**
|
|
140
|
+
on exactly that assertion — verified, not asserted.
|
|
141
|
+
|
|
142
|
+
It has already bitten its own author twice: an untracked `one.ie/` collapsing in
|
|
143
|
+
`git status --porcelain` read a build-output-only branch as busy, and a `sleep`
|
|
144
|
+
with no path in its argv proved nothing about `pgrep -f`.
|
|
145
|
+
|
|
146
|
+
**The box refusal is proved the same way** — stub `health.sh` to `UNHEALTHY` and
|
|
147
|
+
a real run exits **3** while `--dry-run` reports and proceeds.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Then deploy
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
/sweep --pr # dev carries everything, one gate green, PR open
|
|
155
|
+
/deploy # spawns release-manager, which promotes the named sha and ships
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
See also: `/deploy` · `.claude/skills/deploy/SKILL.md` (the run as tracked work) ·
|
|
159
|
+
`../CLAUDE.md § The dev → prod loop`.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# /tasks
|
|
2
|
+
|
|
3
|
+
Take what someone wants and make it claimable. One pass: size it, say it in
|
|
4
|
+
human words, break it into the pieces its size implies, chain them so only the
|
|
5
|
+
first is ready, and hand each to the agent that answers for it.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/tasks drain every untriaged row on the board
|
|
9
|
+
/tasks "<a sentence>" shape one captured thing
|
|
10
|
+
/tasks --dry show the writes, apply nothing
|
|
11
|
+
/tasks --rewrite <tid> re-say one existing row in human words
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Nothing parses those.** This file is instructions to a model, not a CLI —
|
|
15
|
+
there is no `tasks.sh`, and the words above are shapes a reader types so the
|
|
16
|
+
model knows which leg to run. `--dry` means *pass `dryRun: true` to the
|
|
17
|
+
workflow*; `--rewrite <tid>` means *run the shape leg against one existing row
|
|
18
|
+
and skip the drain*. Where a flag IS parsed the authority is a script and this
|
|
19
|
+
file says so: `do-triage.sh` and `do-tier.sh` below both take real arguments and
|
|
20
|
+
both have a real `--self-test`.
|
|
21
|
+
|
|
22
|
+
Saying that plainly matters here more than usual. A declared interface with no
|
|
23
|
+
implementation is the defect this whole command exists to catch — see the
|
|
24
|
+
`tasks:bulk` note under **Don't**, which is advertised with a full schema and
|
|
25
|
+
answers `unknown_receiver`.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## The one rule
|
|
30
|
+
|
|
31
|
+
**A task is a Want.** Not a label, not a ticket, not a cycle number — the thing
|
|
32
|
+
someone wants that is not true yet, written so a stranger knows what changes
|
|
33
|
+
when it is done.
|
|
34
|
+
|
|
35
|
+
`schema/story.tql` makes this the one refusal it enforces: `story_has_want()`,
|
|
36
|
+
and its comment — *a story with no Want is a log*. A row that cannot state its
|
|
37
|
+
Want is not small, it is **unshaped**, and it goes to `shaping:` rather than to
|
|
38
|
+
an agent.
|
|
39
|
+
|
|
40
|
+
That is what "human sounding" means here, and it is not cosmetic. `lifecycle-human C0`
|
|
41
|
+
tells a puller nothing. *A wallet backed up twice loses its key* tells them what
|
|
42
|
+
is wrong, who it hurts and when they are finished.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## The pipeline
|
|
47
|
+
|
|
48
|
+
The workflow already exists — **`/tasks` does not reimplement it.**
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Workflow({ name: "triage", args: { dryRun: <bool>, tasks: [{title}] } })
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`.claude/workflows/triage.js`, four phases:
|
|
55
|
+
|
|
56
|
+
| phase | does | cost |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| `Untriaged` | `do-rank.py --board-check`, then rows whose notes lack `accept:` | haiku |
|
|
59
|
+
| `findPaths` | **`do-triage.sh` first — zero LLM.** A model names paths only if it comes back empty | bash, then haiku |
|
|
60
|
+
| `tier` | **`do-tier.sh` on the paths.** Never re-size in JS | bash |
|
|
61
|
+
| `reshape` | original words to `tasks:comment` **before** any rename; `accept:` into notes | haiku |
|
|
62
|
+
|
|
63
|
+
**That order is load-bearing.** The original sentence is preserved as a comment
|
|
64
|
+
before anything overwrites the title, because a rewrite that loses what the
|
|
65
|
+
person actually said is not a rewrite, it is a deletion.
|
|
66
|
+
|
|
67
|
+
### What `/tasks` adds
|
|
68
|
+
|
|
69
|
+
`plannedWrites()` in `triage.js` today writes a comment and a note and **stops**
|
|
70
|
+
— it never renames, never makes a subtask, never assigns, never chains. The
|
|
71
|
+
workflow's own description says it renames; it does not. `/tasks` is that
|
|
72
|
+
missing half, and it runs only on rows the tier phase actually sized:
|
|
73
|
+
|
|
74
|
+
5. **say** — `tasks:rename` to the Want, in human words
|
|
75
|
+
6. **split** — `tasks:subtask` per piece, by tier (below)
|
|
76
|
+
7. **chain** — `blockedBy` so exactly one sibling is ready
|
|
77
|
+
8. **hand over** — `assignee` from the routing table (below)
|
|
78
|
+
|
|
79
|
+
A row that reaches step 5 unsized does not get steps 5–8. It gets
|
|
80
|
+
`shaping:` and waits for a person.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Sizing decides the shape
|
|
85
|
+
|
|
86
|
+
`bash .claude/scripts/do-tier.sh --intent "<text>" <paths...>` is the authority.
|
|
87
|
+
Never guess a tier, and never let a model pick one.
|
|
88
|
+
|
|
89
|
+
| tier | subtasks | chained | who |
|
|
90
|
+
|---|---|---|---|
|
|
91
|
+
| `PATCH` | none — it is one piece | — | `implementer` |
|
|
92
|
+
| `FIX` | 2–3, only if they are separately checkable | yes | `implementer`, refuted by `review-engineer` |
|
|
93
|
+
| `FEATURE` | one per surface it touches | yes | `architect` writes the spec **first**, then `implementer` |
|
|
94
|
+
| `SCHEMA` | one per migration + one to prove it | yes | `architect`, W2 on Fable |
|
|
95
|
+
| `UNSIZED` | **none** | — | nobody — it goes to `shaping:` |
|
|
96
|
+
|
|
97
|
+
### UNSIZED is a state, not a default
|
|
98
|
+
|
|
99
|
+
`do-tier.sh` exits **3** and returns `{"tier":"UNSIZED","spine":"recon"}` when it
|
|
100
|
+
is given no paths, and its own header says why:
|
|
101
|
+
|
|
102
|
+
> *Absence of recon must not read as simplicity.*
|
|
103
|
+
|
|
104
|
+
A new task is a sentence with no diff, so **every** fresh capture is UNSIZED
|
|
105
|
+
until recon finds files. Two real mis-sizings are on record from treating that as
|
|
106
|
+
PATCH (`text/learnings.md:485`, `text/remote-suspend-todo.md:5`). So the pipeline
|
|
107
|
+
is **capture → recon → size → agent**, never capture → size → agent. `/tasks`
|
|
108
|
+
reports UNSIZED as its own outcome and files nothing under it.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Who answers for it
|
|
113
|
+
|
|
114
|
+
Routing is by what the work *is*, not by who is free. Category comes from the
|
|
115
|
+
paths `do-triage.sh` found.
|
|
116
|
+
|
|
117
|
+
| paths touch | agent |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `schema/`, a receiver, an authority walk | `architect` — spec before code |
|
|
120
|
+
| a component, a route, a page | `implementer` |
|
|
121
|
+
| auth, keys, money, a bounded door | `security-auditor` |
|
|
122
|
+
| a gate, a ratchet, a proof | `test-engineer` |
|
|
123
|
+
| `text/` only | `tech-writer` |
|
|
124
|
+
| a deploy, a release, `.release/` | `release-manager` |
|
|
125
|
+
| a measurement, a budget, a clock | `perf-engineer` |
|
|
126
|
+
| prod is down | `incident-commander` |
|
|
127
|
+
| nothing matches | `cto` — ranks it into a rung and re-routes |
|
|
128
|
+
|
|
129
|
+
**Every row leaves `/tasks` with an assignee.** An unassigned row is the one that
|
|
130
|
+
sits — 219 of 452 rows on this board sat at priority 0 with empty notes and no
|
|
131
|
+
owner, and none of them moved. Assigning is not bureaucracy; it is the difference
|
|
132
|
+
between a row and a queue.
|
|
133
|
+
|
|
134
|
+
`agent` never means *autonomous*: `tool → skill → agent` is the autonomy ladder
|
|
135
|
+
(root `CLAUDE.md`). A money-moving or authority-changing row gets `human` in its
|
|
136
|
+
chain regardless of tier.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Chaining: one ready sibling, not ten
|
|
141
|
+
|
|
142
|
+
`tasks:subtask` writes the row, its notes, its containment edge **and** its
|
|
143
|
+
`blockedBy` in one call — that is why it exists and why two calls are wrong. Its
|
|
144
|
+
own contract says it: *between the two calls the child sits on the board
|
|
145
|
+
claimable with an empty body and no ordering, and the factory will take it.*
|
|
146
|
+
|
|
147
|
+
So: subtask N is `blockedBy` subtask N−1, unless they are genuinely independent.
|
|
148
|
+
Ten unchained siblings is ten agents claiming the same ground.
|
|
149
|
+
|
|
150
|
+
Where a real chain already exists, mirror it rather than inventing one —
|
|
151
|
+
`lifecycle-human` is `C0 alone → C1 decides → C2 → C5/C6/C7`, with C3, C4 and C8
|
|
152
|
+
parallel throughout. That shape came from the plan, not from a rule.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Story · promise · contract — the vocabulary, and what is not wired
|
|
157
|
+
|
|
158
|
+
The substrate already models this. Read it before inventing a fourth noun:
|
|
159
|
+
|
|
160
|
+
| word | is | where |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| **task** | a Want — what is not true yet | `story_tasks($origin)` |
|
|
163
|
+
| **story** | an origin plus its beats: world · cast · knock · want · way · turn · lesson | `story_members`, `story_missing` |
|
|
164
|
+
| **promise** | the frozen oath: terms plus exactly one checkable proof | `story_promise($origin)`, `text/promise.md` |
|
|
165
|
+
| **contract** | who may admit work here, and what facts are still missing | `contract-admissible`, `missing-facts` |
|
|
166
|
+
|
|
167
|
+
The arc is `promise → progress → payoff`, read off the promise rung's status
|
|
168
|
+
(`story_arc`). Tasks are the Wants between them; `story_next($origin)` picks the
|
|
169
|
+
next one the ant-colony way — strongest net trail (`strength − resistance`),
|
|
170
|
+
never toxic — and `story_unexplored` is what the colony explores when there is no
|
|
171
|
+
trail yet. Priority does not appear anywhere in that decision.
|
|
172
|
+
|
|
173
|
+
**None of it is wired, and `/tasks` must not pretend otherwise.** Measured
|
|
174
|
+
2026-09-12:
|
|
175
|
+
|
|
176
|
+
- `schema/story.tql` has **no migration** and nothing loads it. Its own header
|
|
177
|
+
says blocks 3–5 are *NOT YET VALIDATED: no TypeDB was reachable from the
|
|
178
|
+
session that wrote them* — 35 functions, unchecked.
|
|
179
|
+
- Its only consumer is a **prompt**: `.claude/agents/storyteller.md`.
|
|
180
|
+
- The SDK registry carries four story receivers — `story:chain`, `story:demo`,
|
|
181
|
+
`story:event`, `story:view`. **Not one mints an origin or binds a task to a
|
|
182
|
+
story.**
|
|
183
|
+
- `story_tasks` matches `has tag $g; $g == $origin` — the binding is a **bare
|
|
184
|
+
tag**. So a renamed row keeps its story only if it still carries that tag, and
|
|
185
|
+
today no row carries one because nothing writes one.
|
|
186
|
+
|
|
187
|
+
So `/tasks` uses the vocabulary and files rows a story could later adopt. It does
|
|
188
|
+
**not** call a story function, and it does not claim a story exists. Wiring it
|
|
189
|
+
means: a migration, a receiver that mints an origin, and the 35 functions
|
|
190
|
+
validated against a reachable TypeDB — in that order, as its own plan.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Don't
|
|
195
|
+
|
|
196
|
+
- **Don't set priority.** `triage.js` refuses to, on purpose. The board ranks by
|
|
197
|
+
learned weight where it has evidence; an authored number competes with a
|
|
198
|
+
measurement and usually wins for the wrong reason. This board had 219 rows at
|
|
199
|
+
priority 0 including *the ladder cannot be climbed* and *tasks:comment silently
|
|
200
|
+
truncates*, and a panel layout tweak at 1.00.
|
|
201
|
+
- **Don't rename before the original text is a comment.** `tasks:comment`
|
|
202
|
+
truncates at 4,000 bytes and returns `ok:true`, so a long capture must be split
|
|
203
|
+
and read back, not trusted.
|
|
204
|
+
- **Don't clear notes by omission.** `tasks:notes` with an absent `notes` key is
|
|
205
|
+
a silent DELETE returning `{ok:true, notes:null}`. Read-modify-write, always,
|
|
206
|
+
with the full body.
|
|
207
|
+
- **Don't reach for `tasks:bulk`.** It is advertised by the MCP server with a
|
|
208
|
+
full schema and answers `unknown_receiver` in production. One call per row.
|
|
209
|
+
- **Don't add a second sizing path or a second triage door.** `do-triage.sh` and
|
|
210
|
+
`do-tier.sh` are the two, and both have `--self-test`.
|
|
211
|
+
- **Don't size from a title.** `do-tier.sh` sizes a **diff**. "Add a null check"
|
|
212
|
+
and "add a settings page" share a verb.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## See also
|
|
217
|
+
|
|
218
|
+
- `.claude/workflows/triage.js` — the pipeline this command drives
|
|
219
|
+
- `.claude/scripts/do-triage.sh` · `do-tier.sh` — the two authorities, both `--self-test`
|
|
220
|
+
- `text/board-review-2026-09-12.md` — where the defects quoted above were measured
|
|
221
|
+
- `text/story-framework.md` · `schema/story.tql` — the vocabulary, unwired
|
|
222
|
+
- `.claude/commands/do.md` — what claims a shaped task and builds it
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# DEV-ONLY — PreToolUse(Edit|Write|MultiEdit, Bash): development happens on
|
|
3
|
+
# `dev`, or in a worktree cut from `dev`. Never on the shared main tree.
|
|
4
|
+
#
|
|
5
|
+
# THE RULE, in one line: main RECEIVES, it does not get EDITED.
|
|
6
|
+
#
|
|
7
|
+
# WHY THIS EXISTS — three measured failures, none of them theoretical:
|
|
8
|
+
#
|
|
9
|
+
# 1. WORK VANISHES. 2026-09-16: a session deleted eight root .astro pages in
|
|
10
|
+
# the shared main tree and never committed. The 12:14 reboot killed it,
|
|
11
|
+
# `git pull --ff-only` fast-forwarded main, and every deleted file came
|
|
12
|
+
# back. `git status` was CLEAN afterwards, so nothing recorded that the
|
|
13
|
+
# work had existed at all. The operator reported it as "I deleted them but
|
|
14
|
+
# they don't seem to be deleted" — which is exactly what it looks like.
|
|
15
|
+
#
|
|
16
|
+
# 2. THE RECEIPT CAN NEVER BIND. `test-cached.sh` keys on tree CONTENT
|
|
17
|
+
# (`git diff HEAD` + untracked + .env). A gate run on a main tree that any
|
|
18
|
+
# neighbour is editing measures a MOVING tree, so it mints a receipt for a
|
|
19
|
+
# state that never ships. 2026-09-05: four full suites, ~25 minutes, zero
|
|
20
|
+
# receipts. `./deploy` from main was simply never runnable that day.
|
|
21
|
+
#
|
|
22
|
+
# 3. A DEV SERVER ON MAIN SERVES THE STALE TRUNK. Every page it renders is a
|
|
23
|
+
# false reading — the same false-RED class as running a gate there.
|
|
24
|
+
# 2026-09-16: localhost:4321 answered /media with 200 for hours after the
|
|
25
|
+
# page was deleted on dev, because it was booted from the main tree.
|
|
26
|
+
#
|
|
27
|
+
# WHAT IS ALLOWED. Any linked worktree — its git-dir sits under
|
|
28
|
+
# `.git/worktrees/…`, which is the test this hook uses. That covers
|
|
29
|
+
# `.claude/worktrees/dev` (the workbench), every `feat/*` tree cut from dev,
|
|
30
|
+
# and `.release` (which must stay writable: it is how prod ships). Anything
|
|
31
|
+
# outside the repo entirely is none of this hook's business.
|
|
32
|
+
#
|
|
33
|
+
# WHERE TO GO INSTEAD:
|
|
34
|
+
# small, hours -> .claude/worktrees/dev (already minted and warm)
|
|
35
|
+
# large or long -> bash .claude/scripts/worktree-up.sh <name> --no-dev
|
|
36
|
+
# (cuts from dev by default — worktree-up.sh:63 BASE="dev")
|
|
37
|
+
#
|
|
38
|
+
# Disable: ECC_DISABLED_HOOKS=hook:dev-only
|
|
39
|
+
|
|
40
|
+
# shellcheck source=lib/hook.sh
|
|
41
|
+
source "$CLAUDE_PROJECT_DIR/.claude/hooks/lib/hook.sh"
|
|
42
|
+
is_hook_disabled "hook:dev-only" && exit 0
|
|
43
|
+
|
|
44
|
+
PAYLOAD="${1:-}"
|
|
45
|
+
# Claude Code delivers the payload on STDIN; argv stays first so a positional
|
|
46
|
+
# caller still works. Bounded read — a bare $(cat) hangs forever on a tty and
|
|
47
|
+
# would freeze every Edit and every Bash call in the session.
|
|
48
|
+
if [[ -z "$PAYLOAD" && ! -t 0 ]]; then
|
|
49
|
+
IFS= read -r -d '' -t 2 PAYLOAD <&0 || true
|
|
50
|
+
fi
|
|
51
|
+
[[ -z "$PAYLOAD" ]] && exit 0
|
|
52
|
+
|
|
53
|
+
TOOL=$(printf '%s' "$PAYLOAD" | jq -r '.tool_name // empty' 2>/dev/null)
|
|
54
|
+
|
|
55
|
+
# ── is this path inside the SHARED MAIN tree? ────────────────────────────────
|
|
56
|
+
# A linked worktree's absolute git-dir contains /worktrees/. The primary tree's
|
|
57
|
+
# does not. That single fact is the whole test, and it needs no path list that
|
|
58
|
+
# could drift as worktrees come and go.
|
|
59
|
+
_is_shared_main() {
|
|
60
|
+
local p="$1" d gitdir
|
|
61
|
+
[[ -z "$p" ]] && return 1
|
|
62
|
+
# A Write to a file that does not exist yet still has an existing parent.
|
|
63
|
+
d="$p"; while [[ -n "$d" && "$d" != "/" && ! -d "$d" ]]; do d=$(dirname "$d"); done
|
|
64
|
+
[[ -d "$d" ]] || return 1
|
|
65
|
+
gitdir=$(git -C "$d" rev-parse --absolute-git-dir 2>/dev/null) || return 1
|
|
66
|
+
[[ -z "$gitdir" ]] && return 1
|
|
67
|
+
case "$gitdir" in */worktrees/*) return 1 ;; esac # linked worktree — fine
|
|
68
|
+
return 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
_deny() {
|
|
72
|
+
jq -nc --arg r "$1" \
|
|
73
|
+
'{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":$r}}'
|
|
74
|
+
exit 0
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
WHERE="Work in the dev worktree, or a worktree cut from it:
|
|
78
|
+
|
|
79
|
+
small, hours cd .claude/worktrees/dev
|
|
80
|
+
large or long bash .claude/scripts/worktree-up.sh <name> --no-dev
|
|
81
|
+
|
|
82
|
+
Both are cut from \`dev\`, which is the branch every feature integrates into.
|
|
83
|
+
main receives \`--ff-only\` merges and a release PR; it is not an edit surface.
|
|
84
|
+
|
|
85
|
+
Override, for a deliberate one-off: ECC_DISABLED_HOOKS=hook:dev-only"
|
|
86
|
+
|
|
87
|
+
case "$TOOL" in
|
|
88
|
+
Edit|Write|MultiEdit|NotebookEdit)
|
|
89
|
+
FP=$(printf '%s' "$PAYLOAD" | jq -r '.tool_input.file_path // .tool_input.notebook_path // empty' 2>/dev/null)
|
|
90
|
+
[[ -z "$FP" ]] && exit 0
|
|
91
|
+
_is_shared_main "$FP" || exit 0
|
|
92
|
+
_deny "[dev-only] This file is in the SHARED MAIN tree. Development happens on dev.
|
|
93
|
+
|
|
94
|
+
$FP
|
|
95
|
+
|
|
96
|
+
An edit here is not saved work. Main gets fast-forwarded by whichever session
|
|
97
|
+
pulls next, and an uncommitted change is silently reverted — on 2026-09-16 that
|
|
98
|
+
resurrected eight deleted pages and left \`git status\` clean, so nothing
|
|
99
|
+
recorded that the work had ever existed.
|
|
100
|
+
|
|
101
|
+
$WHERE"
|
|
102
|
+
;;
|
|
103
|
+
|
|
104
|
+
Bash)
|
|
105
|
+
CMD=$(printf '%s' "$PAYLOAD" | jq -r '.tool_input.command // empty' 2>/dev/null)
|
|
106
|
+
[[ -z "$CMD" ]] && exit 0
|
|
107
|
+
# Strip heredoc bodies so a command that merely NAMES a dev server in a
|
|
108
|
+
# commit message or a doc is never refused — same discipline as
|
|
109
|
+
# hook:governor-escape.
|
|
110
|
+
STRIPPED=$(printf '%s' "$CMD" | sed -E '/<<-?[A-Za-z_'"'"'"]/,/^[[:space:]]*[A-Za-z_]+[[:space:]]*$/d')
|
|
111
|
+
printf '%s' "$STRIPPED" | grep -qE '(^|[|&;[:space:]])(astro[[:space:]]+dev|(bun|npm|pnpm|yarn)[[:space:]]+run[[:space:]]+dev)([[:space:]]|$)' || exit 0
|
|
112
|
+
|
|
113
|
+
# Where would it run? An explicit `cd` wins, else the tool's cwd.
|
|
114
|
+
TARGET=$(printf '%s' "$CMD" | grep -oE "(^|&&|;|\|)[[:space:]]*cd[[:space:]]+(\"[^\"]+\"|'[^']+'|[^[:space:]]+)" | head -1 \
|
|
115
|
+
| sed -E "s/.*cd[[:space:]]+//; s/^[\"']//; s/[\"']\$//")
|
|
116
|
+
[[ -z "$TARGET" ]] && TARGET=$(printf '%s' "$PAYLOAD" | jq -r '.cwd // empty' 2>/dev/null)
|
|
117
|
+
case "$TARGET" in *'$'*) TARGET=$(printf '%s' "$PAYLOAD" | jq -r '.cwd // "."' 2>/dev/null) ;; esac
|
|
118
|
+
[[ -z "$TARGET" ]] && TARGET="."
|
|
119
|
+
_is_shared_main "$TARGET" || exit 0
|
|
120
|
+
_deny "[dev-only] A dev server started here serves the STALE TRUNK.
|
|
121
|
+
|
|
122
|
+
$TARGET
|
|
123
|
+
|
|
124
|
+
Every page it renders is a false reading: main does not carry what dev has
|
|
125
|
+
integrated. Measured 2026-09-16 — localhost:4321 answered /media with 200 for
|
|
126
|
+
hours after that page was deleted on dev, because the server was booted from
|
|
127
|
+
the main tree. One dev server, and it runs on dev.
|
|
128
|
+
|
|
129
|
+
cd .claude/worktrees/dev/one.ie/web && bun run dev
|
|
130
|
+
|
|
131
|
+
$WHERE"
|
|
132
|
+
;;
|
|
133
|
+
esac
|
|
134
|
+
|
|
135
|
+
exit 0
|