@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.
Files changed (230) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/agents/abm-strategist.md +67 -1
  3. package/agents/ads-meta.md +67 -1
  4. package/agents/analyst.md +67 -1
  5. package/agents/animator.md +108 -0
  6. package/agents/architect.md +269 -20
  7. package/agents/brand-guardian.md +67 -1
  8. package/agents/brand-strategist.md +67 -1
  9. package/agents/campaign-content.md +67 -1
  10. package/agents/campaign-email.md +67 -1
  11. package/agents/campaign-sms.md +67 -1
  12. package/agents/campaign-social.md +67 -1
  13. package/agents/cco.md +83 -2
  14. package/agents/ceo.md +108 -11
  15. package/agents/chairman.md +197 -0
  16. package/agents/cmo.md +82 -2
  17. package/agents/community-greeter.md +67 -1
  18. package/agents/community-moderator.md +67 -1
  19. package/agents/compliance.md +67 -1
  20. package/agents/copywriter.md +67 -1
  21. package/agents/creative-strategist.md +67 -1
  22. package/agents/cro.md +81 -1
  23. package/agents/cto.md +266 -28
  24. package/agents/customer-interviewer.md +67 -1
  25. package/agents/customer-researcher.md +67 -1
  26. package/agents/customer-success-manager.md +67 -1
  27. package/agents/customer-trainer.md +67 -1
  28. package/agents/cxo.md +82 -1
  29. package/agents/demand-creator.md +67 -1
  30. package/agents/demo-mover.md +67 -1
  31. package/agents/demo-specialist.md +67 -1
  32. package/agents/demo-thai-family-law.md +67 -1
  33. package/agents/designer.md +67 -1
  34. package/agents/discovery-caller.md +67 -1
  35. package/agents/doctor.md +269 -0
  36. package/agents/educate-coach.md +67 -1
  37. package/agents/elevate-tutor.md +67 -1
  38. package/agents/email-lifecycle-marketer.md +67 -1
  39. package/agents/engage-specialist.md +67 -1
  40. package/agents/events-coordinator.md +67 -1
  41. package/agents/foundation-builder.md +67 -1
  42. package/agents/funnel-architect.md +67 -1
  43. package/agents/gift-creator.md +67 -1
  44. package/agents/google-ads.md +67 -1
  45. package/agents/guide.md +67 -1
  46. package/agents/helpdesk-dispatcher.md +67 -1
  47. package/agents/hook-specialist.md +67 -1
  48. package/agents/identify-optimizer.md +67 -1
  49. package/agents/implementer.md +313 -45
  50. package/agents/incident-commander.md +67 -1
  51. package/agents/insights-lead.md +87 -1
  52. package/agents/journey-runner.md +67 -1
  53. package/agents/linkedin-ads.md +67 -1
  54. package/agents/live-sales-chat.md +67 -1
  55. package/agents/market-researcher.md +67 -1
  56. package/agents/media-buyer.md +67 -1
  57. package/agents/memory-keeper.md +195 -0
  58. package/agents/movers-customer-researcher.md +67 -1
  59. package/agents/movers-foundation-builder.md +67 -1
  60. package/agents/movers-market-researcher.md +67 -1
  61. package/agents/movers-pricing-strategist.md +67 -1
  62. package/agents/nurture-architect.md +67 -1
  63. package/agents/offer-architect.md +67 -1
  64. package/agents/onboarder.md +67 -1
  65. package/agents/onboarding-specialist.md +67 -1
  66. package/agents/operations-dashboard.md +87 -1
  67. package/agents/perf-engineer.md +333 -37
  68. package/agents/playbook-writer.md +67 -1
  69. package/agents/plg-strategist.md +67 -1
  70. package/agents/positioning-architect.md +67 -1
  71. package/agents/press-officer.md +67 -1
  72. package/agents/pricing-strategist.md +67 -1
  73. package/agents/privacy-officer.md +67 -1
  74. package/agents/referral-manager.md +67 -1
  75. package/agents/refine-analyst.md +67 -1
  76. package/agents/release-manager.md +446 -39
  77. package/agents/renewals-upsell-rep.md +67 -1
  78. package/agents/review-engineer.md +319 -45
  79. package/agents/rewards-steward.md +67 -1
  80. package/agents/sales-call-coach.md +67 -1
  81. package/agents/sales-closer.md +67 -1
  82. package/agents/security-auditor.md +343 -48
  83. package/agents/sell-closer.md +67 -1
  84. package/agents/share-amplifier.md +67 -1
  85. package/agents/social-media-manager.md +67 -1
  86. package/agents/storyteller.md +301 -0
  87. package/agents/strategist.md +67 -1
  88. package/agents/strategy-aligner.md +67 -1
  89. package/agents/support-agent.md +67 -1
  90. package/agents/tagger.md +327 -0
  91. package/agents/tech-writer.md +195 -22
  92. package/agents/test-engineer.md +398 -29
  93. package/agents/tiktok-ads.md +67 -1
  94. package/agents/tracking-engineer.md +67 -1
  95. package/agents/trailkeeper.md +181 -0
  96. package/agents/upsell-strategist.md +67 -1
  97. package/agents/voice.md +67 -1
  98. package/agents/w1-recon.md +1 -1
  99. package/agents/w2-decide.md +1 -1
  100. package/agents/w3-edit.md +8 -2
  101. package/agents/w4-verify.md +13 -0
  102. package/agents/workflow-optimiser.md +81 -1
  103. package/commands/close.md +916 -160
  104. package/commands/deploy.md +102 -724
  105. package/commands/do.md +58 -2
  106. package/commands/sweep.md +159 -0
  107. package/commands/tasks.md +222 -0
  108. package/hooks/scripts/dev-only.sh +135 -0
  109. package/hooks/scripts/git-add-guard.sh +37 -2
  110. package/hooks/scripts/session-start.sh +32 -4
  111. package/package.json +1 -1
  112. package/rules/scripts.md +85 -0
  113. package/scripts/CLAUDE.md +315 -0
  114. package/scripts/ad-copy-lint.sh +656 -0
  115. package/scripts/agent-actor-parity.sh +129 -0
  116. package/scripts/blocks-manifest-cached.sh +100 -0
  117. package/scripts/chat-context-check.sh +89 -0
  118. package/scripts/chrome.mjs +18 -0
  119. package/scripts/close-metrics.sh +587 -0
  120. package/scripts/close-owner.sh +326 -0
  121. package/scripts/db-sync-lock-check.sh +116 -0
  122. package/scripts/deploy-emit.sh +311 -0
  123. package/scripts/deploy-gate-check.sh +155 -0
  124. package/scripts/deploy-ready.sh +78 -0
  125. package/scripts/deploy-record.sh +605 -0
  126. package/scripts/deploy-schema-check.sh +58 -0
  127. package/scripts/deploy.sh +393 -243
  128. package/scripts/do-auto.sh +127 -26
  129. package/scripts/do-board.sh +429 -0
  130. package/scripts/do-close.sh +1184 -0
  131. package/scripts/do-consumer-sweep.sh +18 -1
  132. package/scripts/do-decide.sh +476 -0
  133. package/scripts/do-fleet.sh +8 -2
  134. package/scripts/do-plan-json.mjs +110 -12
  135. package/scripts/do-prove-selftest.sh +108 -0
  136. package/scripts/do-prove.sh +86 -10
  137. package/scripts/do-rank.py +200 -3
  138. package/scripts/do-reconcile.sh +73 -12
  139. package/scripts/do-signal.sh +101 -23
  140. package/scripts/do-smoke.sh +18 -1
  141. package/scripts/do-w4-gates.sh +11 -1
  142. package/scripts/do-world-check.sh +153 -0
  143. package/scripts/download-stats.sh +172 -0
  144. package/scripts/factory-brief-check.sh +330 -0
  145. package/scripts/factory-check.sh +18 -1
  146. package/scripts/factory-close-check.sh +257 -0
  147. package/scripts/factory-emit.sh +211 -0
  148. package/scripts/factory-executor-check.mjs +353 -0
  149. package/scripts/factory-peak.sh +301 -0
  150. package/scripts/factory-repo.sh +71 -0
  151. package/scripts/factory-review-check.mjs +61 -0
  152. package/scripts/factory-tasks-check.sh +18 -1
  153. package/scripts/fixtures/factory-brief-real.md +44 -0
  154. package/scripts/flywheel-outcome.sh +63 -0
  155. package/scripts/gate-reaper-check.sh +98 -0
  156. package/scripts/gate-reaper.sh +9 -0
  157. package/scripts/gate-watchdog.sh +619 -0
  158. package/scripts/gc-content-check.sh +142 -0
  159. package/scripts/gh-traffic-capture.sh +153 -0
  160. package/scripts/govern-order-check.sh +202 -0
  161. package/scripts/governor-doors-check.sh +86 -5
  162. package/scripts/health.sh +448 -0
  163. package/scripts/id-inventory.mjs +418 -0
  164. package/scripts/incident.sh +212 -0
  165. package/scripts/land.sh +755 -45
  166. package/scripts/lib/gc-finished.sh +77 -0
  167. package/scripts/livekit-ratchet.sh +18 -1
  168. package/scripts/machine-check.sh +1 -1
  169. package/scripts/memory-index-budget.sh +79 -0
  170. package/scripts/npm-downloads.sh +109 -0
  171. package/scripts/one-agents.mjs +204 -8
  172. package/scripts/one-resume.sh +31 -3
  173. package/scripts/pr-body.sh +335 -0
  174. package/scripts/preview-fd-check.sh +289 -0
  175. package/scripts/redirect-lint.sh +169 -0
  176. package/scripts/release.sh +40 -6
  177. package/scripts/resume-lost-sessions.sh +68 -0
  178. package/scripts/shoot-pages.mjs +140 -0
  179. package/scripts/signal-meta-backfill.ts +451 -0
  180. package/scripts/signal-watch.sh +63 -6
  181. package/scripts/speed-cache-check.sh +12 -2
  182. package/scripts/sweep.sh +426 -0
  183. package/scripts/task-titles-dump.ts +101 -0
  184. package/scripts/test-cached.sh +47 -10
  185. package/scripts/test-lanes.sh +14 -0
  186. package/scripts/thread-name-backfill.ts +215 -0
  187. package/scripts/triage-shape-check.sh +149 -0
  188. package/scripts/tsc-cached.sh +155 -8
  189. package/scripts/typedb-flake-check.sh +3 -1
  190. package/scripts/urls-lint.sh +8 -0
  191. package/scripts/verify-board-doors.sh +80 -0
  192. package/scripts/verify-fast.sh +159 -6
  193. package/scripts/worktree-up.sh +21 -3
  194. package/skills/astro/SKILL.md +9 -3
  195. package/skills/astro/optimize-performance.md +3 -2
  196. package/skills/cloudflare/SKILL.md +3 -2
  197. package/skills/cloudflare-security-audit/AI-AND-LLM.md +83 -0
  198. package/skills/cloudflare-security-audit/ATTACK-CLASSES.md +130 -0
  199. package/skills/cloudflare-security-audit/CLIENT-SIDE.md +83 -0
  200. package/skills/cloudflare-security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
  201. package/skills/cloudflare-security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
  202. package/skills/cloudflare-security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
  203. package/skills/cloudflare-security-audit/HUNTING.md +251 -0
  204. package/skills/cloudflare-security-audit/LICENSE +21 -0
  205. package/skills/cloudflare-security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
  206. package/skills/cloudflare-security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
  207. package/skills/cloudflare-security-audit/PROVENANCE.md +78 -0
  208. package/skills/cloudflare-security-audit/RECONNAISSANCE.md +156 -0
  209. package/skills/cloudflare-security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
  210. package/skills/cloudflare-security-audit/SKILL.md +192 -0
  211. package/skills/cloudflare-security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
  212. package/skills/cloudflare-security-audit/VALIDATION-AND-REPORTING.md +186 -0
  213. package/skills/cloudflare-security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
  214. package/skills/cloudflare-security-audit/report-schema.json +461 -0
  215. package/skills/cloudflare-security-audit/validate-coverage-ledger.cjs +872 -0
  216. package/skills/cloudflare-security-audit/validate-coverage-ledger.test.cjs +740 -0
  217. package/skills/cloudflare-security-audit/validate-findings.cjs +773 -0
  218. package/skills/cloudflare-security-audit/validate-findings.test.cjs +652 -0
  219. package/skills/deploy/REFERENCE.md +713 -0
  220. package/skills/deploy/SKILL.md +140 -0
  221. package/skills/fleet-audit/SKILL.md +58 -0
  222. package/skills/meeting/SKILL.md +220 -0
  223. package/skills/planning/SKILL.md +256 -0
  224. package/skills/shadcn/SKILL.md +1 -1
  225. package/skills/typedb/SKILL.md +7 -0
  226. package/skills/voice/SKILL.md +94 -6
  227. package/skills/voice/corpus-check.sh +87 -0
  228. package/templates/template-agent.md +7 -1
  229. package/templates/template-feature.md +9 -0
  230. package/templates/template-todo.md +29 -0
package/commands/close.md CHANGED
@@ -1,179 +1,935 @@
1
1
  # /close
2
2
 
3
- **Skills:** `/signal` (4-outcome grammar: result / timeout / dissolved / failure) · `/typedb` (mark-dims, rubric scoring, write completion)
3
+ Close the loop. One bounded pass, then a row for anything it could not close.
4
4
 
5
- Mark a result — close the signal loop.
5
+ ```bash
6
+ bash .claude/scripts/do-close.sh <slug> [--tid <tid>] [--composite 0.NN] [--dims k=v,…]
7
+ ```
8
+
9
+ **That script is the authority.** It carries the leg table, the budgets, the
10
+ credential ladder and the remediation commands; this file does not restate them.
11
+ `--help` prints the flags, `--self-test` proves the red halves.
12
+
13
+ ---
14
+
15
+ # The close is a conversation, not a status flip
16
+
17
+ A close that only writes `status=done` is a closed loop the substrate can read and
18
+ **no person can**. Since 2026-09-09 every close runs as a real thread in
19
+ `/u/one/in`, spoken by the agent that owns the work and answered by the agent it
20
+ reports to. The thread IS the record: an operator who was not here opens one room
21
+ and learns what was done, what it cost, what it disproved, and what is next.
22
+
23
+ The acts below run in order. None is optional; each names the door it uses.
6
24
 
7
- ## Flags
25
+ ## One close, many rooms
8
26
 
9
- | Flag | Signal | Outcome | Loop |
10
- |------|--------|---------|------|
11
- | `<task-id>` | mark() | result chain strengthens | L2 |
12
- | `<task-id> --fail` | warn(1) | failure full warn, chain breaks | L2 |
13
- | `<task-id> --dissolved` | warn(0.5) | dissolved mild warn | L2 |
14
- | `<task-id> --timeout` | neutral | timeout — slow, not bad | L2 |
15
- | *(no arg)* | mark() + tick | session report — record all outcomes | L2, L6 |
16
- | `--todo <slug> --wave N` | emit `do:close` | wave close (soft gate) — append learnings.md; next wave proceeds if skipped | L1, L6 |
17
- | `--todo <slug> --cycle N` | emit `do:close` | cycle close (**hard gate**) — verify learnings grew; block next cycle if missing | L1, L2, L6 |
18
- | `--todo <slug> --cycle N --wave N` | emit `do:close` | specific wave of a cycle — used for re-running a failed close | L1 |
27
+ **A close that touches eight things belongs in eight conversations, not one.** This
28
+ is not extra work — it is already how the store is built. `tasks:comment` writes
29
+ the SAME D1 message store the inbox reads, keyed ``getOrCreateThread(env.DB, {
30
+ slug, agentId: `task:${tid}` })`` (`resolvers/tasks.ts:1815`). So a comment on a
31
+ task **is** a conversation in `/u/<slug>/in`, titled by that task. There is no
32
+ second store and no second step.
19
33
 
20
- ## Routing
34
+ Two levels, and they carry different things:
21
35
 
22
- `/close` maps to `mark()` or `warn()` — the human emitting the Four Outcomes
23
- signal back into the substrate. Rule 1 (Closed Loop): every signal must close.
24
- Without `/close`, paths cannot learn.
36
+ | Room | Holds | Door |
37
+ |---|---|---|
38
+ | **the close thread** (`close-<slug>`) | the spine — what was closed as a whole, the superior's verdict, the rubric | `thread:append` |
39
+ | **each thing's own thread** (`task:<tid>`) | what THIS row's close means for THIS row, in the place its watchers already read | `tasks:comment` |
25
40
 
26
- ## Four Outcomes Reference (smoke)
41
+ Post the whole close only to the spine, and post to each thing's room the one
42
+ paragraph that matters **there**. A watcher who follows one task must not have to
43
+ read a session-wide close to learn what happened to it — and a `@mention` in that
44
+ comment INSERTs a `follows` row (`tasks.ts:1686-1693`), so speaking in a room is
45
+ also how the next person gets subscribed to it.
27
46
 
28
- The four flags map 1:1 to the Four Outcomes from routing.md:
47
+ The failure this prevents is measured: agents post without `replyTo` 0 of 33 on
48
+ the launch board — and the board was invisible for two days that way. One lump
49
+ thread nobody is watching is the same defect wearing a tidier shape.
29
50
 
51
+ ## Act 0 — the room exists before anyone speaks
52
+
53
+ Open the thread FIRST, then let the agents talk into it. A room minted after the
54
+ fact is a transcript; a room minted first is a place.
55
+
56
+ ```bash
57
+ K=$(grep -m1 '^GATEWAY_API_KEY=' one.ie/web/.dev.vars | cut -d= -f2- | tr -d '"')
58
+ curl -s -X POST https://one.ie/api/ask/thread:append \
59
+ -H "Authorization: Bearer $K" -H 'Content-Type: application/json' \
60
+ -d '{"data":{"slug":"one","group":"close-<slug>","role":"user",
61
+ "content":"<what is being closed, in one paragraph>"}}'
30
62
  ```
31
- result → /close <id> mark() strength++ chain strengthens
32
- timeout → /close <id> --timeout neutral no change chain continues
33
- dissolved → /close <id> --dissolved warn(0.5) resist+=0.5 mild — path missing
34
- failure → /close <id> --fail warn(1) resist+=1 full agent failed
63
+
64
+ Four traps, each one measured, each one silent when you hit it:
65
+
66
+ | Trap | What happens |
67
+ |---|---|
68
+ | `.env`'s `GATEWAY_API_KEY` | prod REFUSES it. The working service key is in **`.dev.vars`** |
69
+ | python `urllib` | **403** from prod. Use `curl` |
70
+ | field named `text` | `validation … field: content`. The field is **`content`** |
71
+ | omitting `sender` | `validation … field: sender`. It is **required**, even for `role:"user"` |
72
+ | no `data` envelope | `envelope_missing`. Every payload is `{"data":{…}}` |
73
+
74
+ **`ok:true` is not proof.** This door can answer `ok` with nothing durable. Read
75
+ the row back before believing it:
76
+
77
+ ```bash
78
+ npx wrangler d1 execute one-owners --remote --json --command \
79
+ "SELECT role, substr(content_json,1,120), ts FROM messages
80
+ WHERE thread_id='<tid>' ORDER BY ts DESC LIMIT 5;"
35
81
  ```
36
82
 
37
- Every possible outcome has a corresponding `/close` flag. No outcome goes unmarked.
38
- This is Rule 1 enforced at the human boundary.
83
+ `chat:context` answers **forbidden** here the service key is not a workspace
84
+ member. D1 is the read.
85
+
86
+ ## Act 1 — every task has an owner, or the close assigns one
87
+
88
+ An unassigned task cannot report to anyone, so the close **fills the gap rather
89
+ than skipping it**. Resolution order, and it never invents a name — the roster is
90
+ `ls one.ie/ai/agents/`:
91
+
92
+ Resolution is a **script, not a judgment** — `bash .claude/scripts/close-owner.sh
93
+ --tags "<task tags>"` answers owner + the full chain above them, and
94
+ `--self-test` proves it can go red. Run it; do not reason it out.
95
+
96
+ 1. **Match the task's tags against `subscribes:`** in each `one.ie/ai/agents/*/agent.md`.
97
+ Bare words only — `marketing`, never `lifecycle:marketing`; a namespaced tag
98
+ matches zero signals (root `CLAUDE.md`). Measured 2026-09-09: **56** agents,
99
+ 48 declare `subscribes:` — but only **30 carry a bare stake**, so 26 cannot be
100
+ tag-matched at all and reach an owner only through the ladder below.
101
+ 2. **Ties break DOWNWARD** — the lowest tier that matches wins. Sending a director
102
+ to do specialist work is how a director stops being available for the judgment
103
+ only they can make.
104
+ 3. **No match → `world:route`**, which walks the staked tag→receiver paths and
105
+ answers `reason:'ceo'` for a workspace with no history.
106
+ 4. **Still nothing → the CEO.** Repo law: the CEO is the default receiver, always
107
+ listening via a standing workflow.
108
+
109
+ Assign with `tasks:reassign` (or `tasks:claim` if the agent is taking it itself),
110
+ and say so in the room. An assignment nobody announced is an assignment nobody acts on.
111
+
112
+ **The owner does not have to be in this workspace.** `tasks:everywhere` admits a row
113
+ tagged `@actor` with **no workspace check** (`subscriptions.ts:81`, rule a), so an
114
+ assignment crosses the tenant line and lands on that actor's `/tasks` and `/plan`.
115
+ Use it when the right owner lives elsewhere — the ladder fallback exists for when
116
+ nobody fits, not for when the person who fits is simply somewhere else.
117
+
118
+ Two constraints that follow: the `@` tag is writable **only** through
119
+ create/claim/reassign (`tasks:tag` admits plain words only — `resolvers/tasks.ts:112`),
120
+ and each of those takes identity from `ctx`, never the payload. So you can reach
121
+ anyone, and the row always carries who sent it.
122
+
123
+ ## Act 2 — the owner analyses, and closes what it can prove
124
+
125
+ The owning agent reads the work and answers four questions in the thread, in its
126
+ own name (`role:"assistant"`, `sender:"<agent>"`):
127
+
128
+ - **What was asked**, quoted from the task's own `notes`, not paraphrased.
129
+ - **What was done**, as file:line or a receipt — never "implemented X".
130
+ - **What it disproved.** The most valuable line in any close. A close that
131
+ disproved nothing usually measured nothing.
132
+ - **What it did NOT do**, and why. Scope declined on purpose is a result;
133
+ scope silently dropped is a defect.
134
+
135
+ Then it acts on the board, and every act is a door that already exists:
136
+
137
+ | Act | Door |
138
+ |---|---|
139
+ | close a task it can prove | `tasks:status` → `done` |
140
+ | leave a comment on the task | `tasks:comment` — writes the SAME message store as the inbox thread |
141
+ | tag the row so it routes | `tasks:tag` (bare words) |
142
+ | file what it found | `tasks:subtask` — with `notes` AND `blockedBy` in the SAME call |
143
+ | rank it | `tasks:priority` |
144
+
145
+ **Never close a task on inference.** A task closed because it "looks done" is worse
146
+ than one left open — the board stops being a record of what is true. If the proof
147
+ is not in hand, comment what is missing and leave it open.
148
+
149
+ ## Act 2b — the blast radius: what ELSE touches what you just closed
150
+
151
+ A thing is never closed alone. Sweep the four surfaces that reference it, and
152
+ **say what you found in each one's own room**. A close that names no neighbours
153
+ either touched nothing or did not look.
154
+
155
+ | Surface | The question | How to answer it |
156
+ |---|---|---|
157
+ | **tasks** | what did this unblock, and what is now orphaned? | `blockers-of($t)` · `ready-tasks($plan)` · `orphans($plan)` — a row with no `parent` is unreachable from any plan and the factory's walk will never find it |
158
+ | **workflows** | does a step still name what I changed? | a step's receiver is a literal string; a renamed receiver dissolves at run time as `unknown_receiver`, silently. Grep `trigger_source` and step receivers for the old name |
159
+ | **pages** | which surfaces render it? | Puck blocks + `PAGE_CHAT_META`. A block registered but never rendered is inert, and a page that names a dead receiver fails only when a person clicks |
160
+ | **agents** | who staked on these tags? | the bare words in `subscribes:` — those actors should hear it. `world:announce` fans it; silence is not routing |
161
+
162
+ Three substrate funs are the close's own auditors. Run them on what you touched:
163
+
164
+ - **`overclaimed()`** — an asserted hypothesis carrying more confidence than its
165
+ evidence. If your close created one, you did not close, you claimed.
166
+ - **`well-made-wrong()`** — high craft, `rubric-fit 0.0`. The most expensive
167
+ failure there is: it looks finished and it solved the wrong problem.
168
+ - **`unproven($plan)` / `covered-unproven($plan)`** — a deliverable that shipped
169
+ and was never proven. Shipping is not proving.
170
+
171
+ ## Act 3b — will anyone ever know if this was any good?
172
+
173
+ **This is the act most closes skip, and it is the one that decides whether the
174
+ world can learn.** Rule 1 says every signal closes with `mark`, `warn` or
175
+ `dissolve`. But a thing that emits no signal can never be marked or warned — so no
176
+ path to it ever moves, no outcome ever re-ranks it, and the substrate is blind to
177
+ it forever. Verification > presence: "it shipped" is a claim about the past.
178
+
179
+ So before writing `done`, answer: **what future event tells us this worked?**
180
+
181
+ | What shipped | What must emit |
182
+ |---|---|
183
+ | a UI affordance | `emitClick('ui:<surface>:<action>')` — every onClick is a signal |
184
+ | a page or block | a view event, and a lane if it is a choice — a block with no lane is scenery |
185
+ | money or a commitment | `recordConversion` with a real `ConversionStage` (`event-vocabulary.ts`) — the server narrates a purchase, never the client |
186
+ | a receiver | it closes with `mark`/`warn`, never a silent return |
187
+ | an agent's work | an attempt that closes with a rubric, so `craft-composite` has something to read |
188
+
189
+ If the honest answer is **nothing will emit** — say so in the room and file the
190
+ tracking rung. Do not launder it into `done`. An unmeasurable feature is a feature
191
+ the world cannot rank, and a substrate whose whole thesis is that outcomes re-rank
192
+ routing has just been handed a permanent blind spot.
193
+
194
+ Two traps worth naming because both read as success: a stage in `MARK_STAGES` that
195
+ excludes awareness means **reaching** something is deliberately not a `mark` — do
196
+ not upgrade a view into evidence. And a cron handler whose expression is missing
197
+ from `[triggers] crons` is **dark** — registered in code, never fired, and nothing
198
+ says so.
199
+
200
+ ## Act 3 — the follow-ups are filed before the close, not after
201
+
202
+ Anything the analysis surfaced becomes a row **in the same act**, or it evaporates.
203
+ Use `tasks:subtask` and pass `notes` + `blockedBy` in the one call: between a bare
204
+ `tasks:create` and a later `tasks:notes`, the row sits on the board claimable, with
205
+ an empty body and no ordering, and the factory will take it.
206
+
207
+ Every row lands with its metadata filled — no bare titles:
208
+
209
+ | Field | Rule |
210
+ |---|---|
211
+ | `notes` | the goal in prose: what done looks like, and why. **A row with no notes is the row that sits untouched.** |
212
+ | `tags` | bare routing words that will actually reach a subscriber |
213
+ | `priority` | **0..1, a fraction** — `tasks:create`'s zod is `z.number().min(0).max(1)` and `55` is rejected outright as `validation … field: priority`. The registry is the spec; this table said `1–100` until 2026-09-15 and every row filed from it bounced. The rubric that decides the number: money/security bleeding NOW ≫ decisions blocking others ≫ critical path ≫ backlog |
214
+ | `parent` | omit it and the row is an orphan, unreachable from any plan |
215
+ | `blockedBy` | what stops ten siblings going ready at once |
216
+ | `dueAt` | **only where a real date exists.** Never invent one — a fabricated deadline is a lie the Today lens repeats every morning |
217
+
218
+ ## Act 4 — it reports to its superior, and the superior answers
219
+
220
+ The chain is declared, not guessed: `reports_to:` in the agent's own frontmatter,
221
+ resolved by `close-owner.sh --owner <agent>`. Measured 2026-09-09 — **39 of 56**
222
+ agents carry it, so a third of the roster needs the fallback, and the fallback is
223
+ a ladder, not a default:
224
+
225
+ ```
226
+ reports_to: in agent.md → use it
227
+ else specialist → the director of its own `domain:`
228
+ director → ceo
229
+ ceo → chairman
230
+ chairman → the human, via human:notify
231
+ else ceo (repo law: the CEO is the standing receiver)
232
+ ```
233
+
234
+ The superior does not rubber-stamp. It reads the thread and answers with one of
235
+ three verdicts, posted in its own name:
236
+
237
+ - **CLOSED** — the work is proven. The loop ends.
238
+ - **OPEN, with a named gap** — what is missing, as a task it files itself.
239
+ - **REFUTED** — the claim does not hold; the owner's close is reversed and the
240
+ row goes back to open with the reason.
241
+
242
+ **The loop runs until the superior closes it, not until the owner is finished.**
243
+ Bound it at 3 rounds: the owner answers the gap, the superior re-reads. Still open
244
+ after 3 → escalate one rung and say so, rather than looping forever. A superior
245
+ that has not answered is not a close — it is an unrun gate, and an unrun gate is
246
+ never a pass.
247
+
248
+ **A verdict is not an account.** CLOSED ends the loop; it does not tell anyone
249
+ what happened. Act 4b is where the work is assessed and said out loud.
250
+
251
+ ## Act 4b — the director assesses, and posts what the world reads
252
+
253
+ Act 4 answers **"is this proven?"** — a gate, and its three verdicts decide only
254
+ whether the loop may end. It does not answer **"does anyone outside this thread
255
+ know, and should they care?"** A close that stops at a verdict is legible to the
256
+ loop and invisible to the company: the superior said CLOSED, the row went green,
257
+ and nobody who was not in the room learned anything.
258
+
259
+ So the close hands the finished work to a **director**, who assesses it and posts
260
+ about it in its own name.
261
+
262
+ **The director is resolved, never chosen.** The same script Act 4 already runs
263
+ returns the whole chain; the DIRECTOR rung of it is the one that posts:
264
+
265
+ ```bash
266
+ # COMMA-separated. Spaces do not split — "deploy release ship" is ONE tag named
267
+ # "deploy release ship", it matches nobody, and the answer is an entirely ordinary
268
+ # `hits=0 confidence=none` that reads exactly like a genuinely unowned area.
269
+ bash .claude/scripts/close-owner.sh --tags "deploy,release,ship"
270
+ # owner=release-manager via=subscribes hits=2 specific=2 contenders=1 confidence=high
271
+ # chain=cto ceo chairman
272
+ ```
273
+
274
+ **The first entry of `chain=` is the director** — `cto` above. When the owner is
275
+ itself a director the chain starts one rung higher (`--owner cmo` → `chain=ceo
276
+ chairman`) and the director who posts is the OWNER; do not walk past it to the
277
+ CEO, or every close in the company arrives at one desk.
278
+
279
+ Two answers are NOT a director, and they are different — the script says which:
280
+
281
+ | It prints | Means | Who posts |
282
+ |---|---|---|
283
+ | `owner=REFUSED … confidence=low` + a `fallback=ceo` line | several agents staked equally — genuine ambiguity | the CEO decides who speaks, and says why |
284
+ | `owner=ceo via=fallback(no-tag-match) … confidence=none` | **nothing staked on these tags at all** | the CEO posts, and files the missing stake as a row |
285
+
286
+ The second is the interesting one: it is not a routing failure, it is the world
287
+ telling you a whole area has no owner. Closing it silently under the CEO hides
288
+ that. File the stake.
289
+
290
+ And the route follows the world rather than the org chart's fiat: emit
291
+ `signal("world", { tags })` and the director who **staked** on those tags gets it
292
+ (root `CLAUDE.md § the universal router`). Bare words only — a namespaced tag
293
+ matches zero subscribers, so `marketing`, never `lifecycle:marketing`. The CEO is
294
+ the receiver of LAST resort, for genuine ambiguity (no clear staker), not the
295
+ default addressee.
296
+
297
+ **Assessment is a different question from review.** Act 6's reviewer asks whether
298
+ the work is correct; the director asks whether it was worth doing and what it
299
+ changes. Four questions, answered in the post or explicitly declined:
300
+
301
+ | Question | What a non-answer looks like |
302
+ |---|---|
303
+ | **Who is better off, and at what?** | "improved the panel" — no persona, no verb |
304
+ | **What did it cost?** | no cycles, no tokens, no wall clock |
305
+ | **What did it disprove?** | every close that discovers nothing is a close that tested nothing |
306
+ | **What is now possible that was not?** | no next rung, or a rung with no owner |
307
+
308
+ Then it posts — one message, into `/u/one/in`, through the door the operator
309
+ already reads:
310
+
311
+ ```bash
312
+ # space:post → the workspace inbox + every connected SSE listener
313
+ signal("chat:send", { space: "one", content: "<the assessment>", replyTo: "<the close thread>" })
314
+ ```
315
+
316
+ ### Five rules for that post, each one a defect this repo has already shipped
317
+
318
+ 1. **Name the lane.** A fast pass is never reported as a full pass. `verify:fast`
319
+ and `bun run verify` are different claims and the reader cannot tell them apart
320
+ afterwards.
321
+ 2. **Name where it landed.** "on `dev`" is not "in production". A post that says
322
+ *shipped* about a branch is the same lie as a green cycle with an unrun gate,
323
+ and it is the one a reader acts on.
324
+ 3. **Name what did NOT ship.** A close post with no open items is a marketing
325
+ post wearing a record's clothes. What was left out, and why, is the half a
326
+ future reader needs — they already assume the rest worked.
327
+ 4. **Quote a measurement, not a claim.** One number that was actually read —
328
+ `816px 560px`, `998 tests`, `0 errors` — beats a paragraph of adjectives, and
329
+ it is what makes the post checkable a month later.
330
+ 5. **It does not post OUTWARD.** Customer-facing copy — blog, social, changelog —
331
+ is a row the director FILES, never a thing the close publishes. `brand-guardian`
332
+ holds a hard veto on creative, and a close that publishes has just routed
333
+ around it.
334
+
335
+ **`ok:true` is not proof here either.** Read the row back (Act 0's D1 query) before
336
+ believing the post landed.
337
+
338
+ The failure this prevents is measured, in this repo, twice. Agents post without
339
+ `replyTo` — **0 of 33** on the launch board — and the board was invisible for two
340
+ days. And a thread only holds the top of the inbox while it is LIVE
341
+ (`LIVE_WINDOW_MS`, `lib/in/live-pin.ts`): a close nobody posts is not merely quiet,
342
+ it is unreachable within the hour, sitting under every task row that spoke more
343
+ recently.
344
+
345
+ ## Act 5 — the docs end true
346
+
347
+ The propagate matrix below (§ Loop Close) already lists which doc each trigger
348
+ touches. Two rules the conversation adds:
349
+
350
+ - **The promise is the first row reconciled**, before any derived doc.
351
+ - **A doc that still says the thing is unbuilt, after you built it, is a defect** —
352
+ the W4 shipped-status check exists because this drifts on 3 of 3 runs.
353
+
354
+ ## Act 6 — the rubric, and who reviews it
355
+
356
+ Score the **cycle** rubric. Weights and axis definitions live in
357
+ `.claude/scripts/rubric-weights.json` (`cycle` block) — read them there, **never
358
+ restate them here**, and never substitute `w4-rubric.ts`'s `task-composite=`
359
+ (five axes, no goal-fit, so the `goal-fit ≥ 0.50` gate becomes unevaluable).
360
+
361
+ Gate, all four conjuncts: `composite ≥ 0.65` **AND** `goal-fit ≥ 0.50` **AND**
362
+ no adversarial > 0.5 **AND** `delta_tsc ≤ 0`.
363
+
364
+ **Who reviews is decided by size × importance, and both are measured, not felt:**
365
+
366
+ | Size (`do-tier.sh`) × importance (task priority) | Model |
367
+ |---|---|
368
+ | the mechanical closes — status flips, comments, tag writes, doc-truth greps | **haiku**, fanned out in parallel, one per task |
369
+ | the analysis and the report | **sonnet** — the default |
370
+ | FEATURE, or priority ≥ 0.90 | **opus** reviews before the superior answers |
371
+ | SCHEMA, or it touched money / auth / custody / a locked name | **fable** reviews — apex judgment, once, fed a context pack |
372
+
373
+ The fan-out is real parallelism and it is where the cost goes: N tasks closing is
374
+ N haiku calls in ONE message, never a serial walk. The review is the opposite —
375
+ one call, at the end, on the whole thread.
376
+
377
+ **A reviewer defaults to REFUTED and must quote the line that changed its mind.**
378
+ No quotable line ⇒ not proven.
379
+
380
+ ## Act 6b — the chain, when the promise was minted
381
+
382
+ A close that settles a **promise** settles it twice: off-chain, which runs the
383
+ loop, and on-chain, which holds the state and enforces the ratchet.
384
+
385
+ `.claude/scripts/do-promise-settle.sh <slug> --composite <n>` is the one door. It
386
+ re-runs the promise's own `proof:` and, when the promise carries an `object_id:`
387
+ in its `contract:` block, submits the generated `settle_promise` on the same run —
388
+ kept/broken, amount = strength×1000 bps. The Move object is **generated from
389
+ `schema/sui.tql`** by `schema/codegen/targets/move.ts`, never hand-written.
390
+
391
+ Read the exit code, not the vibe:
392
+
393
+ | Exit | Meaning | Verb |
394
+ |---|---|---|
395
+ | 0 | proof green | `mark` on `promise:<slug>→proof` + `world:announce` |
396
+ | **3** | **UNVERIFIABLE — a leg could not reach its substrate** | **neither. The promise stays open. Re-run later** |
397
+ | other non-zero | proof red | `warn` |
398
+ | no observable | dissolved — reported, never silent |
399
+
400
+ **Exit 3 is not a close.** A `proof:` is an `&&`-join, so one unreachable leg stops
401
+ the chain. Folding that into `warn` deposits resistance on a proven path for a
402
+ cluster blip — a promise recorded BROKEN for a reason its maker cannot fix.
403
+
404
+ **Measured 2026-09-09, and this is why the chain leg is usually a no-op today: the
405
+ mint door is sealed.** `NETWORK` is absent from `one.ie/web/wrangler.toml` (grep
406
+ count **0**) and the testnet gateway's `worker_public_key` is empty on-chain, so
407
+ nothing can be minted and therefore nothing can settle. The mechanism is wired and
408
+ the schema is real (`Promise` appears 6× in `schema/sui.tql`); the door is shut.
409
+ Arming it is one of Tony's open calls, and it sits deliberately BEHIND the three
410
+ custody decisions — arming a mint before deciding whether a master may derive an
411
+ agent's key is backwards.
412
+
413
+ So: attempt the chain leg, report what it answered, and **never report a sealed
414
+ door as a settled promise.** An unarmed chain degrades cleanly; the off-chain
415
+ settle stands on its own.
416
+
417
+ ## Act 7 — what to do next is COMPUTED, not suggested
418
+
419
+ A close ends by naming the next work, and that name is read out of the graph
420
+ rather than invented. `schema/factory.tql` carries 43 funs; these answer "next":
421
+
422
+ | Question | Fun |
423
+ |---|---|
424
+ | what is now claimable? | `ready-tasks($plan)` · `ready-in-wave` |
425
+ | what did this close unblock? | `blockers-of($t)` — run it BEFORE and AFTER |
426
+ | what is stuck with nothing to wait on? | `deadlocked()` |
427
+ | what has no plan above it? | `orphans($plan)` |
428
+ | what is promised and not built? | `uncovered($plan)` · `incomplete()` |
429
+ | what is built and not proven? | `unproven($plan)` · `covered-unproven($plan)` |
430
+ | what did we settle wrongly? | `mis-settled()` |
431
+ | the single next thing | `next($plan)` |
432
+
433
+ **A recommendation the model composed is a guess wearing a receipt.** The rule is
434
+ the one the plan parser already enforces: any deterministic fact a close needs gets
435
+ a query, and the model executes it rather than substituting for it. Where a fun
436
+ answers, quote its answer. Where none does — a judgment call about sequencing,
437
+ say — mark it plainly as judgment and say what would settle it.
438
+
439
+ Report next work as **rows, not prose**: each one filed with `notes`, `tags`,
440
+ `priority`, `parent` and `blockedBy` per Act 3, so the recommendation IS the board
441
+ rather than a paragraph someone must re-type into it.
442
+
443
+ ## The loop runs until CLOSED or a human is notified — never until tired
444
+
445
+ A close is a loop, and a loop needs a termination condition that is not fatigue.
446
+ There are exactly two exits:
447
+
448
+ | Exit | When | Door |
449
+ |---|---|---|
450
+ | **CLOSED** | the superior answered CLOSED | `mark` + `world:announce` |
451
+ | **NOTIFY** | the loop hit a decision it cannot make | `human:notify` — with the evidence AND the options |
452
+
453
+ Anything else is a round, and rounds are **bounded at 3**. Still open after three
454
+ → escalate one rung and say so. A loop with no bound is not autonomy, it is a
455
+ process nobody can predict the cost of.
456
+
457
+ **A notification without options is an interruption.** When the loop escalates it
458
+ carries what it found, what it ruled out, and the shortlist it could not choose
459
+ between — the way `close-owner.sh` refuses: it prints `owner=REFUSED` *and*
460
+ `candidates=`, because a decision handed up without its options makes the human
461
+ redo the search the loop already did.
462
+
463
+ ### Self-healing: every failure repairs itself, or names why it can't
464
+
465
+ The loop must survive its own failures without a person. The substrate already
466
+ does this in four places — copy the shape, don't invent one:
467
+
468
+ | Failure | Heals by |
469
+ |---|---|
470
+ | a worker died holding a slot | the claim **evaporates** — dead pid (`kill -0`) or an expired lease, checked on the READ path, so the next contender reclaims dead ground by itself |
471
+ | a proof leg cannot reach its substrate | **exit 3**, and the promise stays open to be re-run — never recorded BROKEN for a cluster blip |
472
+ | a gate already ran on this exact tree | the receipt is **content-addressed**, so a neighbour's pass counts and nothing recomputes |
473
+ | a check's premise went stale | the **red proof** — gut the mechanism and assert the check fails. A check that stays green against a gutted mechanism was never checking |
474
+
475
+ Three rules that keep a self-healing loop honest, because each has a failure mode
476
+ that looks exactly like success:
477
+
478
+ 1. **Never heal by weakening the check.** When a test goes red after a change,
479
+ decide first whether it is a *regression* or a *corrected belief*, and say
480
+ which in the commit. Editing an assertion to match new behaviour is legitimate
481
+ ONLY when the old assertion was wrong — and then the comment must say why it
482
+ was wrong. Silently relaxing it is how a suite becomes decorative.
483
+ 2. **Empty work ⇒ do the full thing, never ⇒ pass.** A loop that goes green by
484
+ selecting nothing is worse than a slow one.
485
+ 3. **A repair is a rung, not a detour.** If healing means touching code the close
486
+ never owned, file it — a close that grows scope while running stops being
487
+ something anyone can review.
488
+
489
+ ### The loop's own three questions, each round
490
+
491
+ - What changed since the last round, and did the gate actually *run*? (an exit
492
+ code is a claim about a command that may never have executed)
493
+ - Did this round's repair break something that was green? If so — regression or
494
+ corrected belief?
495
+ - Is the remaining gap something I can decide, or something only the human can?
496
+ If the latter, stop and notify **now**, with the shortlist. Looping past a
497
+ decision you cannot make burns budget and changes nothing.
498
+
499
+ ## Act 8 — the loop closes where it opened
500
+
501
+ Post the verdict into the same thread, `mark`/`warn` per the Four Outcomes, then
502
+ `world:announce` so whoever staked on those tags learns it happened. Read the
503
+ final row back from D1.
504
+
505
+ **This is not Act 4b again.** That act is the account a PERSON reads — prose, in
506
+ the director's name, in the room. This one is the machine close: the outcome the
507
+ substrate records and the fan-out to stakers. Skipping either leaves half a close
508
+ — a company that knows and a substrate that does not, or the reverse. The thread is now the durable record — a terminal
509
+ transcript is read once by one person; a room is read by everyone after.
510
+
511
+ ---
39
512
 
40
513
  ## Steps
41
514
 
42
- ### `<task-id>`success
43
-
44
- 1. Run W4 gate the **cycle** rubric. Weights and axis definitions live in
45
- `.claude/scripts/rubric-weights.json` (`cycle` block); read them there, never
46
- restate them here. Today: `0.30·goal-fit + 0.18·security + 0.18·stability +
47
- 0.12·simplicity + 0.12·integration + 0.10·speed`.
48
- - **goal-fit** is the heaviest axis *and* an independent hard gate. Score it
49
- against the todo's `outcome:` line, not against general quality.
50
- - **integration** is deterministic from `.w2-surface-checklist.json` an
51
- absent checklist is a W4 fail, never a fallback to vibes.
52
- - Gate, all four conjuncts: `composite 0.65` **AND** `goal-fit 0.50` **AND**
53
- no adversarial > 0.5 **AND** `delta_tsc 0`.
54
-
55
- Do **not** use `w4-rubric.ts`'s number here. That script computes the `task`
56
- rubric — five axes, no goal-fit, because a bare diff carries no plan context.
57
- It prints `task-composite=` for exactly this reason. Comparing it to this gate
58
- silently drops the 0.30 axis and makes the `goal-fit 0.50` gate unevaluable.
59
- 2. Close the substrate task POST `http://localhost:4321/api/ask/tasks:status` with
60
- `{ "data": { "tid": "<tid>", "status": "done", "workspace": "<slug>" } }`.
61
- Don't have the tid? Use the slug door instead — `bash .claude/scripts/do-signal.sh --task-link <slug> "docs=<slug>"`,
62
- which resolves the task by its `slug:` tag and stamps provenance as it closes.
63
- (There is no `/api/tasks/{id}/complete` route `tasks:status` is the only status door.)
64
- 3. POST `http://localhost:4321/api/mark-dims` with
65
- `{ "edge": "<from>→<to>", "dims": { security, stability, simplicity, speed, integration } }`
66
- (the route Zod-parses exactly this shape a bare `{ security, }` body is rejected;
67
- `integration` is the optional 5th axis):
68
- - Dim 0.5 mark() on that dimension path (strength++)
69
- - Dim < 0.5 warn() on that dimension path (resistance++)
70
- 4. Self-checkoff: update task checkbox in the TODO file `[ ]` `[x]`
71
- 5. **Emit feedback signal** POST `http://localhost:4321/api/signal/loop:feedback`.
72
- The receiver is a **path segment**, never a body field: `/api/signal` with no segment
73
- returns 400 `receiver required`. `loop:feedback` has no registered handler, so this
74
- lands as a generic substrate `writeSignal` — which is the point; the path is the address.
75
- ```json
76
- {
77
- "data": {
78
- "tags": ["<task-tags>"],
79
- "strength": "<composite 0–1>",
80
- "content": {
81
- "task_id": "<id>",
82
- "rubric": { "security": X, "stability": Y, "simplicity": Z, "integration": I, "speed": W },
83
- "composite": N,
84
- "velocity": "±N",
85
- "outcome": "result"
86
- }
87
- }
88
- }
89
- ```
90
- This is the return-path pheromone. Future agents with matching tags follow this path.
91
- `composite >= 0.65` mark each tag path. `composite < 0.65` warn(0.5) each tag path.
92
- 6. **Settle the promise** if the slug has a promise (`text/<slug>.md` with a `proof:`), first run
93
- `.claude/scripts/do-derives-check.sh <slug>` (zero LLM: every `derives: <key>: true` file-shaped
94
- artifact must exist on disk closes the class that let security-gates ship a missing `-docs.md`);
95
- a missing artifact forces the settle to BROKEN. Then run
96
- `.claude/scripts/do-promise-settle.sh <slug> --composite <composite>`. Zero LLM: proof exits 0
97
- `mark` on `promise:<slug>→proof` (strength = composite×5) + `world:announce` broadcast; **exit 3
98
- UNVERIFIABLE neither verb, promise stays open, re-run later**; any other non-zero
99
- `warn`; no observable dissolved (reported, never silent). **Exit 3 is not a close.** A
100
- `proof:` is an `&&`-join, so a leg that cannot reach its substrate stops the chain and returns 3;
101
- folding that into `warn` would deposit resistance on a proven path for a cluster blip — a promise
102
- recorded BROKEN for a reason its maker cannot fix. `/close` must treat 3 as "not settled yet" and
103
- re-run when the substrate answers (`text/factory.md` § A note on "red" vs "cannot run"). If the promise was minted on-chain
104
- (`object_id:` in its contract block) the same run submits the generated `settle_promise`
105
- (kept/broken, amount = strength×1000 bps) the state ratchet makes settlement final; unarmed or
106
- failed chain write degrades cleanly, the off-chain settle stands. Build-time reputation lands on
107
- the same weighted path run-time routing reads see `text/marketplace.md`.
108
- 7. Report unlocked tasks (tasks where this was in their `blocks` list)
109
- 8. **Propagate to docs** auto-edit when triggers from the cycle's `.w2-doc-plan.json` match. Zero agent spawns; all bash + Edit tool.
110
-
111
- | Trigger from W3 diff | Target | Edit |
112
- |---|---|---|
113
- | new MCP / CLI / SDK / API export | root `README.md` § public surface | append row |
114
- | new component family or directory | nearest `one.ie/web/src/components/*/CLAUDE.md` | append section |
115
- | 6-dim, L1-L8, or locked-rule change | root `CLAUDE.md` | edit relevant section |
116
- | feature doc named in plan `source_of_truth` | feature doc | sync verb/component/endpoint counts |
117
- | rename across W3 | every `.md` containing old name (W4 stale-name list) | already done by W3 grep |
118
-
119
- For each trigger, the bash dispatcher reads the W3 diff, locates the anchor in the target doc, and applies the Edit tool inline. Anchor mismatch → log to `docs/improvements.md` for manual W2 next cycle (no halt).
120
-
121
- 9. Report:
122
- ```
123
- mark(+5) on <from>→<to>
124
- Rubric: security=X stability=Y simplicity=Z integration=I speed=W composite=N velocity=±N
125
- Feedback: signal emitted loop:feedback tags=[<tags>] strength=N
126
- Promise: <kept|broken|dissolved|none> <mark|warn> promise:<slug>→proof strength=N
127
- Propagate: docs=N edited triggers=[<codes>] manual=N
128
- Unlocked: N tasks
129
- ```
130
-
131
- ### `--fail`
132
-
133
- 1. POST `http://localhost:4321/api/ask/tasks:status` with `{ "data": { "tid": "<tid>", "status": "failed", "workspace": "<slug>" } }`
134
- 2. warn(1) resistance += 1 on the path. Full failure, chain breaks.
135
- 3. Report:
136
- ```
137
- warn(1) on <from>→<to>
138
- Full failure resistance++. Chain breaks.
139
- ```
140
-
141
- ### `--dissolved`
142
-
143
- 1. POST `http://localhost:4321/api/ask/tasks:status` with `{ "data": { "tid": "<tid>", "status": "dissolved", "workspace": "<slug>" } }`
144
- 2. warn(0.5) resistance += 0.5. Mild warn — path doesn't exist yet.
145
- 3. Report:
146
- ```
147
- warn(0.5) on <from>→<to>
148
- Dissolved — missing unit/capability. Mild warn. Chain breaks.
149
- ```
150
-
151
- ### `--timeout`
152
-
153
- 1. Leave `task-status` untouched — a timeout is not an outcome, so the task stays `picked` and the lease holds.
154
- 2. No mark, no warn neutral. Slow, not bad.
155
- 3. Report:
156
- ```
157
- neutral on <from>→<to>
158
- Timeout — slow, not bad. Chain continues.
159
- ```
160
-
161
- ### *(no arg)* session report
162
-
163
- 1. Read git diff: `git diff --stat HEAD` what changed this session
164
- 2. For each completed task this session: POST `/api/ask/tasks:status` with `{ "data": { "tid": "<tid>", "status": "done", "workspace": "<slug>" } }`
165
- 3. POST `http://localhost:4321/api/fade` — apply the group fade-rate to accumulated
166
- pheromone (there is no `/api/tick` route; `fade` is the decay door)
167
- 4. Report session outcomes (numbers first):
168
- ```
169
- Session: N tasks completed M tests K commits
170
- Pheromone: N marks M warns this session
171
- Rubric: security=X stability=Y simplicity=Z integration=I speed=W composite=N velocity=±N (session averages)
172
- Hardened: N highways promoted to permanent (L6)
173
- Frontiers: N new unexplored clusters (L7)
174
- Next: 1. <task> priority=N 2. <task> priority=M 3. <task> priority=K
175
- ```
515
+ Six legs, four states each (`pass` · `fail` · `unrun` · `n/a` do-w4-gates.sh's
516
+ law, unchanged: **an unrun leg is not a closed leg**, plus `skipped` for a leg
517
+ `--only` left outwhich licenses nothing, because an operator narrowing the run
518
+ is not evidence about the tree), run in three waves because
519
+ there are exactly three dependency edges and no more.
520
+
521
+ | Leg | Closes | Wave |
522
+ |---|---|---|
523
+ | `gate` | is there a FULL-suite receipt for **this exact tree**? | 1 |
524
+ | `derives` | every `derives: true` artifact is on disk | 1 |
525
+ | `dims` | the five rubric axes `/api/mark-dims` | 1 |
526
+ | `task` | the board write — `tasks:status` (**needs `gate`**) | 2 |
527
+ | `promise` | `do-promise-settle.sh` (**needs `derives`**) | 2 |
528
+ | `feedback` | the return-path pheromone `/api/signal/loop:feedback` (**needs everything**) | 3 |
529
+
530
+ Measured on a warm tree: **~1s** for the two network legs, **~5s** for the whole
531
+ battery. Every curl carries `--max-time`; the two legs that shell out to scripts
532
+ this one does not control go through `run_bounded`.
533
+
534
+ ## The three rules it will not trade for speed
535
+
536
+ 1. **It never runs a suite.** `/close` is a FULL-lane trigger and *a fast pass is
537
+ never reported as a full pass*. So it asks `test-full.sh` (`TEST_CACHE_KEY_ONLY=1`,
538
+ ~1s, cannot start vitest) whether this tree already carries a receipt. The key
539
+ is content-addressed, so **a neighbour's suite counts as yours** a memo HIT is
540
+ free and it counts. No receipt is `unrun`, filed, and named.
541
+ 2. **No receipt the board is not told `done`.** That edge is the whole reason
542
+ this repo once had cycles closed green at 0.92 with "tsc not measured".
543
+ 3. **Exit 3 from the promise settle is UNVERIFIABLE, never `warn`.** A `proof:`
544
+ is an `&&`-join; a leg that cannot reach its substrate has not broken a
545
+ promise. The promise stays open and gets a row.
546
+
547
+ ## The numbers it mines
548
+
549
+ Six leg states say the doors were knocked on. They do not say what changed,
550
+ whether this tree is proven, where the branch sits, or *why* something could not
551
+ be measured. So every close ends with a measured block —
552
+ `.claude/scripts/close-metrics.sh`, additive, **never a seventh leg**: it decides
553
+ nothing, it can turn nothing red, and its exit code is discarded. A metric that
554
+ could fail a close would make this a closer people route around, and the numbers
555
+ would leave with it.
556
+
557
+ | Section | Rows | Owner it composes |
558
+ |---|---|---|
559
+ | `work` | files changed · lines ± · new/deleted/renamed · untracked · test/doc/code split · tests-per-code-file · largest file | `git diff HEAD` |
560
+ | `tree` | branch · worktree kind · dirty paths · ahead/behind trunk · **landed** · worktree count | git; landed is `merge-base --is-ancestor`, an ancestry FACT, never a merge result |
561
+ | `gates` | full-suite receipt HIT/MISS per lane · W4 pack pass/fail/unrun · `delta_tsc` | `test-full.sh` (`TEST_CACHE_KEY_ONLY=1`) · `.w4-gates.json` if a cycle left one |
562
+ | `rubric` | composite vs gate · per-axis · **velocity** vs the last recorded composite | `rubric-weights.json` · `text/learnings.md` |
563
+ | `close` | ledger open rows · unlanded deploy rows · **`packages/claude` mirror drift** · derives | `do-close.sh --pending` · `deploy-record.sh --pending` · `sync-claude-mirror.sh --check` · `do-derives-check.sh` |
564
+ | `machine` | load/cores · free MB · gate slots held · locks | the governor's own dir |
565
+ | `substrate` | which door answered · promise has a `proof:` | `/api/health` · `text/<slug>.md` |
566
+
567
+ Three rules it inherits and one it adds.
568
+
569
+ 1. **It composes, it never re-derives.** Every row names a script that already
570
+ owns that number. A second opinion is drift with a delay on it — which is how
571
+ the receipt row was born broken: it called `test-cached.sh` (which needs a
572
+ `<folder>` and prints usage without one) instead of `test-full.sh`, and read
573
+ `unrun` on trees that were proven.
574
+ 2. **It never runs a gate.** `TEST_CACHE_KEY_ONLY=1` structurally cannot start
575
+ vitest, and `.w4-gates.json` is READ, never regenerated running
576
+ `do-w4-gates.sh` bare would run `do-reconcile.sh types`, a tsc that queues
577
+ minutes behind the governor. No metrics row is worth a gate slot.
578
+ 3. **Four states, and `unrun` never prints a zero.** Measured in this repo:
579
+ `grep -c "error TS" <file>` returned `0` against an output file a queued gate
580
+ had not written yet. "Zero errors" and "never ran" were the same three
581
+ characters. `--self-test` drives the unrun branch on purpose point it at an
582
+ absent base ref, an empty cache, no `--composite` and fails if any of them
583
+ answers with a number instead.
584
+ 4. **The `machine` section is evidence, not vanity.** An `unrun` with no pressure
585
+ reading beside it is a mystery; with one it is a reason. "tsc not measured,
586
+ box saturated" once closed three cycles green at 0.92.
587
+
588
+ ```bash
589
+ bash .claude/scripts/close-metrics.sh --json # for a caller
590
+ bash .claude/scripts/close-metrics.sh --section gates # one section
591
+ bash .claude/scripts/do-close.sh <slug> --no-metrics # legs + verdict only
592
+ ```
593
+
594
+ ## Blockers it decides, instead of asking
595
+
596
+ A blocker that resolves the same way every time is not a decision — it is a chore
597
+ with a human bolted to it. And a blocker that *is* a decision used to be filed as
598
+ bare prose ("gate unrun") with no options, no cost and no recommendation, so the
599
+ person re-derived the same fork every time.
600
+
601
+ `.claude/scripts/do-decide.sh` sits between "a leg is open" and "a human is
602
+ asked". For every open leg it names the fork, ranks the options, prices them,
603
+ recommends one — and **takes** it when, and only when, the substrate has already
604
+ watched that choice come good five times.
605
+
606
+ **The confidence is measured, and it is not this script's opinion.** The oracle
607
+ is the substrate's own routing verdict `world:route decide:true`
608
+ (`subscriptions.ts:672`), the TS twin of `0043_route_verdict.tql`, a pure read
609
+ with no side effects and no tokens:
610
+
611
+ | verdict | means | /close does |
612
+ |---|---|---|
613
+ | `highway` | `best ≥ 5` **and** `best ≥ 2 × second` | **acts**, and notifies |
614
+ | `classified` | a candidate, not a proven one | recommends |
615
+ | `ceo` | no candidate at all | recommends |
616
+
617
+ `best` is `SUM(strength) SUM(resistance)` over `claw_paths` from
618
+ `tag:one:decide:close:<id>` to each option. Measured against production
619
+ 2026-09-09, the whole loop in three calls:
620
+
621
+ ```
622
+ 0 marks → {"reason":"ceo","receiver":null,"rung":3}
623
+ 5 marks of strength 1 → {"reason":"highway","receiver":"optionA","rung":1}
624
+ + 1 warn → {"reason":"ceo","receiver":null,"rung":3}
625
+ ```
626
+
627
+ So the first five times a fork appears **a person answers it**; after that /close
628
+ answers it; and **one bad outcome takes the authority back on the very next
629
+ run.** That is the root `CLAUDE.md`'s "human gates that auto-skip when trust
630
+ earns it", applied to close blockers, on the mechanism G4 already uses for the
631
+ auto-ship gate.
632
+
633
+ **The evidence is written from outcomes, never from exit codes.** A taken
634
+ decision leaves `"decide":"taken:<option>"` on its ledger row. The *next* close
635
+ reads that row before overwriting it and settles it against what actually
636
+ happened to the leg `pass` `mark`, still open `warn`. "The command exited
637
+ 0" measures the command, not the choice.
638
+
639
+ **`strength` is always 1, and that is load-bearing.** `rankCandidates` sums
640
+ strength and cannot tell five marks of 1 from one mark of 5, so
641
+ `HARDEN_STRENGTH = 5` is a de-facto **n ≥ 5 sample floor** only while every
642
+ writer on `tag:one:decide:close:*` writes 1. `do-decide.sh` is the sole writer
643
+ and `--self-test` pins the literal.
644
+
645
+ ### The table, and the two things no confidence can buy
646
+
647
+ ```bash
648
+ bash .claude/scripts/do-decide.sh --table
649
+ ```
650
+
651
+ | id | fires on | options | |
652
+ |---|---|---|---|
653
+ | `gate-mint` | `gate` unrun, no receipt | `mint` (~87s + a gate slot) · `file` | |
654
+ | `derives-retry` | `derives` unrun (out of clock) | `retry` · `file` | |
655
+ | `promise-retry` | `promise` unrun (exit 3 / clock) | `retry` · `file` | |
656
+ | `writeback` | `dims`/`feedback`/`task` door timeout | `file` | **VETO** |
657
+ | `task-gate` | `task` unrun, `gate` not pass | `file` | **VETO** |
658
+
659
+ A fork **not in this table is not a decision** — exit 5, and /close files the row
660
+ exactly as before. Adding a row is how this grows; generalising it is how it
661
+ starts guessing.
662
+
663
+ The two vetoes are checked **before the oracle is called**, so a vetoed fork is
664
+ structurally unreachable rather than reachable-and-outranked:
665
+
666
+ - **`writeback` — the self-poisoning veto.** `dims`, `feedback` and `task` write
667
+ *through* doors. Their `unrun` is a curl timeout: the answer is UNKNOWN and the
668
+ write may well have landed. A retry that double-writes deposits strength twice
669
+ on exactly the `claw_paths` rows this layer reads back as confidence — it would
670
+ inflate its own evidence, and look *more* certain the more it fired.
671
+ - **`task-gate`** — the board write must never be decided around its gate. Cycles
672
+ once closed green at 0.92 with the suite unmeasured; that edge is why they
673
+ can't again.
674
+
675
+ On top of the table there is a blanket class applied to the **command about to
676
+ run**, not to the table row — so a row edited to smuggle one in still cannot
677
+ fire: anything that ships, deploys, pushes, force-writes, deletes, rotates a
678
+ secret, or spends. **Blast radius is a second axis, and confidence does not buy
679
+ it.** That is the one place this layer deliberately does not do what "don't ask
680
+ if you're sure" would imply, and it is a boundary, not an omission.
681
+
682
+ **Every failure direction points at the human.** No oracle answer, a malformed
683
+ answer, an unreachable D1 mirror, a missing key, no `--apply`, no gate headroom —
684
+ all of them read `ceo` and recommend. A layer that acts when it cannot measure is
685
+ worse than the ask it replaced.
686
+
687
+ **A taken decision always pages, and always carries its undo.** The trade this
688
+ layer makes is moving the human's veto from *before* the action to *after* it,
689
+ and that is only honest while the reversal is actually in their hand:
690
+
691
+ ```
692
+ gate → TAKEN mint (confidence: highway)
693
+ undo: bash .claude/scripts/do-decide.sh --settle --leg gate --status mint --outcome open
694
+ ```
695
+
696
+ A recommendation never pages — it is already on the board and in the report, and
697
+ a notifier that fires for things nobody must act on is a notifier the human
698
+ mutes.
699
+
700
+ `--no-decide` restores the old behaviour: every blocker waits for a person.
701
+ `--dry-run` prints what it *would* take and takes nothing (structural — the
702
+ script cannot act without `--apply`, and `--self-test` proves it).
703
+
704
+ ## What the signal carries
705
+
706
+ `feedback` used to fire in wave 1 with four fields — slug, tid, composite,
707
+ outcome. That is a pheromone saying a close happened and nothing about what it
708
+ found, on the one channel the next agent's routing actually reads.
709
+
710
+ It is now **wave 3**, the third dependency edge (`feedback ← everything`), and it
711
+ carries the whole close:
712
+
713
+ | | |
714
+ |---|---|
715
+ | `legs[]` | all six, each with `status`, **`secs`**, and its detail |
716
+ | `closed` · `pass`/`fail`/`unrun`/`na` · `elapsed_s` · `door` · `branch` | the verdict and where it was taken |
717
+ | `decisions[]` | every fork: `taken`/`recommend`/`veto`, the choice, the confidence |
718
+ | `metrics` | the entire measured block, verbatim |
719
+
720
+ **Measured once, read twice.** The measured block is ~17s of real work. Running
721
+ it for the human and again for the machine would double it *and* measure two
722
+ different instants of a tree seven sessions are editing — so
723
+ `close-metrics.sh --json-out <file>` writes the machine copy while stdout stays
724
+ the table, and the report prints the run the signal carried.
725
+
726
+ ### Tags are derived, and the close closes its own loop on them
727
+
728
+ Tags are how the world routes. A close tagged only `close,<slug>` is a close
729
+ nobody can route on, so the tags are **derived from what was measured** — bare
730
+ words, because a namespaced subscribe tag matches zero subscribers
731
+ (`subscribe-tags-parity.test.ts`):
732
+
733
+ ```
734
+ close · <slug> · closed|unclosed · failing · unrun · decided · recommended
735
+ · <each failing leg> · fast|slow · degraded
736
+ ```
737
+
738
+ Then **Rule 1, on the tags themselves**: each one takes one verb on the edge
739
+ `tag:<workspace>:<tag> → close:kept|open`, carrying the whole tag set.
740
+
741
+ ```
742
+ verbs: mark=0 warn=8 on tag:one:<tag>→close:open
743
+ ```
744
+
745
+ `closed` marks, open warns, and **`--status timeout` writes neither** — a held
746
+ lease is neutral by the same rule that makes `task` and `dims` `n/a` on a
747
+ timeout. A halt is not an outcome. These marks use a `tag:` source, deliberately
748
+ disjoint from the `decide:close:*` sources the decide layer reads: the close must
749
+ never vote on its own authority.
750
+
751
+ ### Accretion, the trail, and the way back
752
+
753
+ *The operator, 2026-09-09: "every time a signal hits a node we should add
754
+ exponentially more metadata and data and images etc. to it… and we can combine
755
+ them back again."*
756
+
757
+ A signal that carries the same four fields at hop 5 as at hop 1 has learned
758
+ nothing from the journey, and the journey is the only thing that knows what
759
+ mattered. So the payload budget **doubles per hop** — `ACCRETE_BASE << hop`,
760
+ `CLOSE_SIGNAL_HOP` read from the caller and re-emitted incremented, so a signal
761
+ crossing four nodes arrives with 16× the room it started with.
762
+
763
+ Two bounds, because "exponentially more" without them is a signal nobody can
764
+ store and every door refuses:
765
+
766
+ 1. **A hard ceiling** (`ACCRETE_MAX`, 256KB). The doubling is how fast the room
767
+ grows, not a promise it grows forever.
768
+ 2. **Bulk travels by reference.** Images, JSON dumps, diffs and gate packs go as
769
+ `{path, bytes, sha256}`, each `test -s`-verified at emit time — never as
770
+ bytes. That is what makes "and images etc." affordable: a screenshot costs
771
+ ~120 bytes of envelope, and the node that wants the pixels reads the path. A
772
+ reference to a file that is not there is "file exists is theater" with an
773
+ extra hop of delay on it.
774
+
775
+ Numbers pack first, then details, then references — a budget that runs out drops
776
+ the biggest thing, never the verdict.
777
+
778
+ **Accretion alone is not enough, and this is the actual insight.** It makes every
779
+ node richer and leaves the one who *asked* knowing nothing. So the envelope
780
+ carries four more fields:
781
+
782
+ | | |
783
+ |---|---|
784
+ | `origin` | who started the work — survives every hop unchanged |
785
+ | `sender` | the previous node, one hop back |
786
+ | `reply_to` | where a return leg goes (`sender`, else `origin`) |
787
+ | `trail[]` | one entry per node crossed: hop, node, bytes added, artifacts, tags, verdict |
788
+
789
+ and `--reply` folds the enriched envelope **back one hop**, tagged `reply` and
790
+ `for:<node>` so the router delivers it to whoever staked on that node instead of
791
+ broadcasting. It returns the outbound payload **verbatim, not a summary** — the
792
+ sender asked for work, and a summary here would be this node deciding what the
793
+ previous node is allowed to know.
794
+
795
+ **This is the ant, and we already had half of it.** Outbound the trail gains
796
+ data; the return trail carries what was found back down the same path, and *the
797
+ return is what moves the weights*. `loop:feedback` has been called "the
798
+ return-path pheromone" for months while it returned four fields — the right name
799
+ for a leg that had nothing to return.
800
+
801
+ Three consequences worth stating, because they generalise past `/close`:
802
+
803
+ - **Routing quality is bounded by return quality.** Paths re-rank on `mark`/`warn`,
804
+ so a mark deposited by a signal that carried no evidence is a vote cast blind.
805
+ Enrich the return leg and the weights start meaning something.
806
+ - **The trail is append-only.** A node may add to the history it was handed; it
807
+ may never rewrite it. A node that edits its own trail is a node whose trail
808
+ cannot be trusted.
809
+ - **The close must never vote on its own authority.** The tag marks use a `tag:`
810
+ source, deliberately disjoint from the `decide:close:*` sources the decide
811
+ layer reads as confidence.
812
+
813
+ ```
814
+ trail: hop=3 origin=pannels sender=do-auto reply=do-auto budget=32768B artifacts=6
815
+ ```
816
+
817
+ The return leg is **opt-in** (`--reply`): a close with no caller has nobody to
818
+ reply to, and a reply nobody asked for is one more signal in a world that already
819
+ routes.
820
+
821
+ ### Speed is a row, not a feeling
822
+
823
+ Every leg is timed, and the measured block is timed separately — because "the
824
+ close was slow" and "the promise proof was slow" are different facts with
825
+ different fixes, and a fixed ~17s cost blamed on a door is a number nobody acts
826
+ on. `slow` is judged on the legs alone.
827
+
828
+ ```
829
+ speed: gate=2s derives=0s task=0s dims=0s feedback=1s promise=0s | metrics=17s total=45s
830
+ tags: close __probe__ unclosed unrun recommended gate fast degraded
831
+ ```
832
+
833
+ The budget does not starve the closing signal: every other leg is skipped once
834
+ the wall clock is spent, `feedback` is exempt — it runs after the measured block,
835
+ so the guard would otherwise fire on the one leg that carries the whole close,
836
+ breaking Rule 1 by its own bound. It stays bounded by its own `--leg-budget`.
837
+
838
+ ## What it cannot close, it files
839
+
840
+ Every non-pass leg lands in three places, in this order — each one a fallback for
841
+ the last failing:
842
+
843
+ 1. **the local ledger** (`.claude/.close-pending.jsonl`) — this write cannot fail
844
+ and it is what `--pending` reads back. Keyed by `<slug>:<leg>`, so a re-run
845
+ replaces its own row: the ledger measures open work, never close attempts.
846
+ 2. **a board task**, tagged `close,unclosed,leg:<name>`, whose `notes` **is the
847
+ paste-able command** that finishes it — not prose. That is the difference
848
+ between a task and a nag.
849
+ 3. **the human** (`notify.sh`) — only when the board was *unreachable*, so the
850
+ row reached nothing anyone watches. `--notify` forces it. `--no-file` (ledger
851
+ only) is the operator's choice and never pages anyone.
852
+
853
+ ```bash
854
+ bash .claude/scripts/do-close.sh --pending # the open rows + each fix. exit 6 if any
855
+ ```
856
+
857
+ ## Run it at the end of every workflow
858
+
859
+ Last line, wrapped, always:
860
+
861
+ ```bash
862
+ bash .claude/scripts/do-close.sh <slug> || true
863
+ ```
864
+
865
+ It exits **0 even with open legs** — by design. A closer that fails its caller is
866
+ a closer callers stop calling, and a skipped close is Rule 1 broken: the signal
867
+ never closes and the paths never learn. `--strict` opts into exit 1 for a caller
868
+ that genuinely wants to stop. Re-running is cheap and safe: the ledger is keyed
869
+ by `<slug>:<leg>` so a row replaces itself, and a leg that passed clears its row.
870
+
871
+ Wired, verified 2026-09-09:
872
+
873
+ | Caller | Where | Status passed |
874
+ |---|---|---|
875
+ | `do-auto.sh` | plan complete (it replaced the bare `--task-link`) | `done` |
876
+ | `do-auto.sh` | `--max-cycles` halt | `timeout` — the lease stays held |
877
+ | `land.sh` | both exits | `--only dims,feedback` |
878
+ | `deploy.sh` | both exits | `--only dims,feedback` |
879
+ | `factory-executor.js` | Close stage, step 6 | `--only dims,feedback,promise,derives` |
880
+
881
+ **Three of the five pass `--only`, and the reason is the same each time: the
882
+ board write must have exactly one writer.**
883
+
884
+ - The factory executor's Close stage already owns it, and its status logic is
885
+ richer than done/failed (a UI task stays `picked` until a person looks). Two
886
+ writers on one row is how the first factory run reported `done` over a row the
887
+ board read `picked`.
888
+ - `land.sh` and `deploy.sh` pass a **branch name** and the literal `deploy`. That
889
+ is also why `_tag_add` sanitises: a branch name carries a `/`, the mark edge is
890
+ a URL path segment, and `tag:one:feat/x→close:open` split into a route that does
891
+ not exist — measured 2026-09-09 as 6 tags derived, 5 verbs landed, and the sixth
892
+ gone with no error. Bare word chars only, pinned by `--self-test` check 13.
893
+ Those names
894
+ are not plan slugs — without `--tid` the task leg resolves by the `slug:` tag
895
+ and would close whatever plan happens to share the name. And `land.sh` runs the
896
+ *fast* lane, so the receipt gate is `unrun` on every land: filing that would
897
+ fire a row on every land forever with nothing anyone can do — the EXPIRED class
898
+ `deploy-record.sh --pending` exists to not be.
899
+
900
+ Only `do-auto.sh` passes a real plan slug, so only it runs the full battery.
901
+
902
+ `do-folder.sh` is deliberately NOT wired: it is a pure emitter whose stdout is
903
+ parsed JSON, and a close line printed there corrupts its only output.
904
+
905
+ ## Outcome flags
906
+
907
+ `--status` picks the outcome word; the legs follow it.
908
+
909
+ ```
910
+ result --status done mark() strength++ chain strengthens
911
+ timeout --status timeout neutral no change lease HELD, status untouched
912
+ dissolved --status dissolved warn(0.5) resist+=0.5 mild — path missing
913
+ failure --status failed warn(1) resist+=1 full — agent failed
914
+ ```
915
+
916
+ A non-`done` close asserts no green, so `gate` reports `n/a` and never blocks it.
917
+
918
+ ## Three specifics that live in the script, not here
919
+
920
+ Moved into `do-close.sh` as code plus comment so they cannot rot apart from the
921
+ behaviour they govern — read them there:
922
+
923
+ - **the three doors take three different credentials** (`mark-dims` and `ask` want
924
+ `GATEWAY_API_KEY`; `/api/signal/*` wants `SERVER_SECRET`, `signal-dispatch.ts:78`).
925
+ Measured 2026-09-09: the feedback pheromone had been 401ing.
926
+ - **`mark-dims` Zod-parses a bare `{edge, dims}`** — a `{data:{…}}` wrapper is a 400.
927
+ - **`w4-rubric.ts`'s `task-composite` is not the cycle gate.** It scores five axes
928
+ with no goal-fit, because a bare diff carries no plan context. The cycle gate is
929
+ `composite ≥ 0.65` AND `goal-fit ≥ 0.50` AND no adversarial > 0.5 AND `delta_tsc ≤ 0`,
930
+ weights in `.claude/scripts/rubric-weights.json` (`cycle` block). Pass the composite
931
+ in with `--composite`; the script does not score judgment and never will.
176
932
 
177
933
  ---
178
934
 
179
- *`/close` is `mark()` made human-readable. Every outcome must close its loop.*
935
+ *`/close` is `mark()` made bounded. Every outcome closes its loop — or leaves the command that does.*