@oneie/claude 0.7.0 → 0.9.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 (263) hide show
  1. package/agents/abm-strategist.md +89 -0
  2. package/agents/ads-meta.md +97 -0
  3. package/agents/analyst.md +173 -0
  4. package/agents/architect.md +280 -0
  5. package/agents/brand-guardian.md +88 -0
  6. package/agents/brand-strategist.md +92 -0
  7. package/agents/campaign-content.md +90 -0
  8. package/agents/campaign-email.md +88 -0
  9. package/agents/campaign-sms.md +87 -0
  10. package/agents/campaign-social.md +87 -0
  11. package/agents/cco.md +99 -0
  12. package/agents/ceo.md +106 -0
  13. package/agents/chairman.md +120 -0
  14. package/agents/cmo.md +101 -0
  15. package/agents/community-greeter.md +89 -0
  16. package/agents/community-moderator.md +92 -0
  17. package/agents/compliance.md +92 -0
  18. package/agents/copywriter.md +100 -0
  19. package/agents/creative-strategist.md +95 -0
  20. package/agents/cro.md +98 -0
  21. package/agents/cto.md +267 -0
  22. package/agents/customer-interviewer.md +93 -0
  23. package/agents/customer-researcher.md +89 -0
  24. package/agents/customer-success-manager.md +90 -0
  25. package/agents/customer-trainer.md +90 -0
  26. package/agents/cxo.md +100 -0
  27. package/agents/demand-creator.md +89 -0
  28. package/agents/demo-mover.md +83 -0
  29. package/agents/demo-specialist.md +89 -0
  30. package/agents/demo-thai-family-law.md +87 -0
  31. package/agents/designer.md +112 -0
  32. package/agents/discovery-caller.md +98 -0
  33. package/agents/doctor.md +139 -0
  34. package/agents/educate-coach.md +100 -0
  35. package/agents/elevate-tutor.md +83 -0
  36. package/agents/email-lifecycle-marketer.md +91 -0
  37. package/agents/engage-specialist.md +99 -0
  38. package/agents/events-coordinator.md +90 -0
  39. package/agents/foundation-builder.md +94 -0
  40. package/agents/funnel-architect.md +89 -0
  41. package/agents/gift-creator.md +100 -0
  42. package/agents/google-ads.md +103 -0
  43. package/agents/guide.md +292 -0
  44. package/agents/helpdesk-dispatcher.md +88 -0
  45. package/agents/hook-specialist.md +91 -0
  46. package/agents/identify-optimizer.md +101 -0
  47. package/agents/implementer.md +305 -0
  48. package/agents/incident-commander.md +120 -0
  49. package/agents/insights-lead.md +93 -0
  50. package/agents/journey-runner.md +113 -0
  51. package/agents/linkedin-ads.md +123 -0
  52. package/agents/live-sales-chat.md +90 -0
  53. package/agents/market-researcher.md +88 -0
  54. package/agents/media-buyer.md +96 -0
  55. package/agents/movers-customer-researcher.md +95 -0
  56. package/agents/movers-foundation-builder.md +96 -0
  57. package/agents/movers-market-researcher.md +97 -0
  58. package/agents/movers-pricing-strategist.md +171 -0
  59. package/agents/nurture-architect.md +99 -0
  60. package/agents/offer-architect.md +92 -0
  61. package/agents/onboarder.md +222 -0
  62. package/agents/onboarding-specialist.md +92 -0
  63. package/agents/operations-dashboard.md +98 -0
  64. package/agents/perf-engineer.md +348 -0
  65. package/agents/playbook-writer.md +71 -0
  66. package/agents/plg-strategist.md +91 -0
  67. package/agents/positioning-architect.md +88 -0
  68. package/agents/press-officer.md +89 -0
  69. package/agents/pricing-strategist.md +94 -0
  70. package/agents/privacy-officer.md +92 -0
  71. package/agents/referral-manager.md +91 -0
  72. package/agents/refine-analyst.md +102 -0
  73. package/agents/release-manager.md +261 -0
  74. package/agents/renewals-upsell-rep.md +90 -0
  75. package/agents/review-engineer.md +327 -0
  76. package/agents/rewards-steward.md +82 -0
  77. package/agents/sales-call-coach.md +94 -0
  78. package/agents/sales-closer.md +98 -0
  79. package/agents/security-auditor.md +313 -0
  80. package/agents/sell-closer.md +101 -0
  81. package/agents/share-amplifier.md +99 -0
  82. package/agents/social-media-manager.md +92 -0
  83. package/agents/storyteller.md +250 -0
  84. package/agents/strategist.md +83 -0
  85. package/agents/strategy-aligner.md +94 -0
  86. package/agents/support-agent.md +90 -0
  87. package/agents/tagger.md +245 -0
  88. package/agents/tech-writer.md +216 -0
  89. package/agents/test-engineer.md +243 -0
  90. package/agents/tiktok-ads.md +105 -0
  91. package/agents/tracking-engineer.md +92 -0
  92. package/agents/upsell-strategist.md +101 -0
  93. package/agents/voice.md +85 -0
  94. package/agents/w4-tools.md +153 -0
  95. package/agents/w4-verify.md +20 -138
  96. package/agents/workflow-optimiser.md +84 -0
  97. package/commands/close.md +814 -160
  98. package/commands/create.md +2 -2
  99. package/commands/deploy.md +554 -175
  100. package/commands/do.md +119 -109
  101. package/commands/fast.md +67 -0
  102. package/commands/improve.md +2 -2
  103. package/commands/one.md +418 -0
  104. package/commands/see.md +1 -1
  105. package/commands/sync.md +1 -1
  106. package/commands/tasks.md +222 -0
  107. package/commands/vespio.md +110 -0
  108. package/commands/vespio.remote.md +227 -0
  109. package/hooks/hooks.json +25 -79
  110. package/hooks/lib/governor-escape-match.sh +88 -0
  111. package/hooks/lib/hook.sh +4 -13
  112. package/hooks/lib/signal.sh +9 -2
  113. package/hooks/scripts/branch-pin.sh +6 -0
  114. package/hooks/scripts/config-protect.sh +6 -0
  115. package/hooks/scripts/do-outcome-gate.sh +19 -2
  116. package/hooks/scripts/git-add-guard.sh +43 -2
  117. package/hooks/scripts/governor-escape.sh +96 -0
  118. package/hooks/scripts/load-guard.sh +127 -0
  119. package/hooks/scripts/post-edit-check.sh +36 -29
  120. package/hooks/scripts/session-start.sh +34 -80
  121. package/hooks/scripts/task-complete-verify.sh +46 -40
  122. package/package.json +1 -1
  123. package/rules/documentation.md +9 -0
  124. package/scripts/ad-copy-lint.sh +656 -0
  125. package/scripts/agentverse-audit.sh +256 -0
  126. package/scripts/asi-walk.sh +435 -0
  127. package/scripts/astro-build-cached.sh +143 -0
  128. package/scripts/blocks-manifest-cached.sh +100 -0
  129. package/scripts/blocks-manifest.mjs +497 -0
  130. package/scripts/blocks-ratchet.sh +116 -0
  131. package/scripts/blocks-render-probe.mjs +529 -0
  132. package/scripts/blocks-usage.mjs +334 -0
  133. package/scripts/chat-context-check.sh +89 -0
  134. package/scripts/close-metrics.sh +558 -0
  135. package/scripts/close-owner.sh +268 -0
  136. package/scripts/db-sync-lock-check.sh +116 -0
  137. package/scripts/deploy-dev.sh +35 -0
  138. package/scripts/deploy-emit.sh +298 -0
  139. package/scripts/deploy-gate-check.sh +128 -0
  140. package/scripts/deploy-ready.sh +78 -0
  141. package/scripts/deploy-record.sh +605 -0
  142. package/scripts/deploy.sh +1273 -0
  143. package/scripts/do-auto.sh +598 -48
  144. package/scripts/do-brief.sh +113 -0
  145. package/scripts/do-close.sh +1137 -0
  146. package/scripts/do-commit.sh +75 -0
  147. package/scripts/do-consumer-sweep.sh +18 -1
  148. package/scripts/do-cycle-shape-check.sh +160 -0
  149. package/scripts/do-decide.sh +476 -0
  150. package/scripts/do-derive-check.sh +436 -0
  151. package/scripts/do-fleet.sh +106 -28
  152. package/scripts/do-folder.sh +10 -1
  153. package/scripts/do-next.sh +106 -0
  154. package/scripts/do-orchestrate.sh +17 -5
  155. package/scripts/do-plan-json.mjs +201 -0
  156. package/scripts/do-plan-json.sh +8 -0
  157. package/scripts/do-preflight.sh +117 -0
  158. package/scripts/do-project.sh +157 -0
  159. package/scripts/do-prove-selftest.sh +108 -0
  160. package/scripts/do-prove.sh +295 -23
  161. package/scripts/do-rank.py +31 -1
  162. package/scripts/do-recon-cache.sh +7 -1
  163. package/scripts/do-recon-pack.sh +196 -0
  164. package/scripts/do-reconcile.sh +121 -4
  165. package/scripts/do-signal.sh +280 -23
  166. package/scripts/do-smoke.sh +18 -1
  167. package/scripts/do-test-gate.sh +80 -0
  168. package/scripts/do-tick.sh +102 -0
  169. package/scripts/do-tier.sh +6 -0
  170. package/scripts/do-triage.sh +182 -0
  171. package/scripts/do-ui-gate.sh +1 -1
  172. package/scripts/do-w4-gates.sh +451 -0
  173. package/scripts/do-walk.sh +12 -1
  174. package/scripts/env-sync.sh +173 -0
  175. package/scripts/factory-brief-check.sh +330 -0
  176. package/scripts/factory-check.sh +68 -14
  177. package/scripts/factory-close-check.sh +257 -0
  178. package/scripts/factory-emit.sh +211 -0
  179. package/scripts/factory-executor-check.mjs +353 -0
  180. package/scripts/factory-peak.sh +301 -0
  181. package/scripts/factory-repo.sh +118 -3
  182. package/scripts/factory-review-check.mjs +61 -0
  183. package/scripts/factory-ship.sh +61 -0
  184. package/scripts/factory-tasks-check.sh +18 -1
  185. package/scripts/factory-turn.sh +326 -0
  186. package/scripts/factory-walk.sh +396 -0
  187. package/scripts/factory-width.sh +57 -0
  188. package/scripts/fade-toxic.sh +4 -3
  189. package/scripts/fixtures/factory-brief-real.md +44 -0
  190. package/scripts/fixtures/triage-dupe.md +5 -0
  191. package/scripts/fleet-manifest.mjs +108 -0
  192. package/scripts/fleet-status.sh +110 -0
  193. package/scripts/full-suite-paths-check.sh +144 -0
  194. package/scripts/gate-reaper-check.sh +98 -0
  195. package/scripts/gate-reaper.sh +125 -0
  196. package/scripts/gate-run.sh +73 -0
  197. package/scripts/gc-content-check.sh +140 -0
  198. package/scripts/gen-dev-config.py +20 -0
  199. package/scripts/govern-bound-check.sh +60 -0
  200. package/scripts/govern-claims-check.sh +233 -0
  201. package/scripts/govern-mem-check.sh +290 -0
  202. package/scripts/governor-doors-check.sh +362 -0
  203. package/scripts/governor-escape-check.sh +171 -0
  204. package/scripts/health.sh +413 -0
  205. package/scripts/id-inventory.mjs +418 -0
  206. package/scripts/land.sh +551 -0
  207. package/scripts/lib/gc-finished.sh +77 -0
  208. package/scripts/lib/govern.sh +361 -0
  209. package/scripts/lib/govern.ts +756 -0
  210. package/scripts/lighthouse-run.sh +187 -0
  211. package/scripts/livekit-live-check.sh +61 -0
  212. package/scripts/livekit-ratchet.sh +46 -0
  213. package/scripts/load-guard-check.sh +49 -0
  214. package/scripts/machine-check.sh +102 -0
  215. package/scripts/machine-watch.sh +177 -0
  216. package/scripts/one-agents.mjs +415 -0
  217. package/scripts/one-resume.sh +78 -0
  218. package/scripts/orphan-baseline.json +182 -0
  219. package/scripts/orphan-modules.mjs +179 -0
  220. package/scripts/pr-body.sh +335 -0
  221. package/scripts/preview-fd-check.sh +289 -0
  222. package/scripts/promise-manifest.mjs +24 -1
  223. package/scripts/release.sh +322 -0
  224. package/scripts/roles-check.sh +946 -0
  225. package/scripts/sdk-build-cached.sh +64 -0
  226. package/scripts/signal-watch.sh +241 -0
  227. package/scripts/skills-publish.sh +94 -0
  228. package/scripts/speed-cache-check.sh +173 -0
  229. package/scripts/speed-check.mjs +907 -0
  230. package/scripts/speed-parity-check.sh +648 -0
  231. package/scripts/speed-waterfall-check.sh +355 -0
  232. package/scripts/substrate-env-parity.mjs +156 -0
  233. package/scripts/tasks-claim-race.mjs +108 -0
  234. package/scripts/tasks-loop.sh +185 -0
  235. package/scripts/test-cached.sh +255 -0
  236. package/scripts/test-full.sh +87 -0
  237. package/scripts/test-honesty.mjs +137 -0
  238. package/scripts/test-lanes.sh +166 -0
  239. package/scripts/test-speed.sh +94 -0
  240. package/scripts/triage-shape-check.sh +149 -0
  241. package/scripts/tsc-cached.sh +179 -0
  242. package/scripts/typedb-flake-check.sh +197 -0
  243. package/scripts/urls-lint.sh +15 -0
  244. package/scripts/verify-fast.sh +445 -0
  245. package/scripts/vespio-sync.sh +149 -0
  246. package/scripts/wf-check.mjs +104 -1
  247. package/scripts/worktree-preview.sh +879 -0
  248. package/scripts/worktree-up.sh +208 -0
  249. package/skills/livekit-agents/SKILL.md +285 -0
  250. package/skills/livekit-agents/references/freshness-rules.md +168 -0
  251. package/skills/shadcn/SKILL.md +1 -1
  252. package/skills/signal/SKILL.md +0 -1
  253. package/skills/voice/SKILL.md +94 -6
  254. package/skills/voice/corpus-check.sh +87 -0
  255. package/hooks/scripts/compact-hint.sh +0 -35
  256. package/hooks/scripts/gate-guard.sh +0 -83
  257. package/hooks/scripts/read-tracker.sh +0 -26
  258. package/hooks/scripts/session-end-verify.sh +0 -51
  259. package/hooks/scripts/stop-reflect.sh +0 -140
  260. package/hooks/scripts/sync-priority-todo.sh +0 -57
  261. package/hooks/scripts/sync-todo-docs.sh +0 -46
  262. package/hooks/scripts/tool-signal.sh +0 -48
  263. package/scripts/do-tasks-bridge.py +0 -366
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env bash
2
+ # do-commit.sh <worktree> <slug> <cid> <path>... — stage EXACTLY these paths and
3
+ # commit them as one cycle, holding a per-worktree lock so two parallel cycles
4
+ # cannot interleave their staging.
5
+ #
6
+ # manifest: needs-env
7
+ #
8
+ # WHY (measured 2026-08-31, lifecycle-money). A batch's cycles run in PARALLEL in
9
+ # ONE worktree. W4 was told to `git -C $WT add -A && git commit`, so whichever
10
+ # cycle committed first swept every sibling's in-progress edits into its own
11
+ # commit. Commit c5eeeb9ec is labelled "C4" and carries C3's escrow + market work
12
+ # and C5's money/sui-network work; its own verifier called it "commit
13
+ # contamination". The repo already forbids blanket staging on the shared tree
14
+ # (hook:git-add-guard) for precisely this reason — the worktree exemption assumed
15
+ # one worker per worktree, which stopped being true when batches went parallel.
16
+ #
17
+ # Two independent problems, two mechanisms:
18
+ # 1. WHAT gets staged — explicit paths only. Never add -A / add . / commit -a.
19
+ # 2. WHEN — `git add` and `git commit` are two calls against one index. Between
20
+ # them a sibling's `git add` can land, and its files join this commit even
21
+ # though this one named only its own. The lock closes that window.
22
+ #
23
+ # It is deliberately NOT an error for a named path to be missing or unchanged: a
24
+ # cycle whose W3 dissolved one spec still commits the rest.
25
+ set -uo pipefail
26
+
27
+ WT="${1:-}"; SLUG="${2:-}"; CID="${3:-}"
28
+ shift 3 2>/dev/null || { echo "usage: do-commit.sh <worktree> <slug> <cid> <path>..." >&2; exit 2; }
29
+ [ -n "$WT" ] && [ -n "$SLUG" ] && [ -n "$CID" ] && [ "$#" -gt 0 ] || {
30
+ echo "usage: do-commit.sh <worktree> <slug> <cid> <path>..." >&2; exit 2; }
31
+ [ -d "$WT/.git" ] || [ -f "$WT/.git" ] || { echo "do-commit: not a worktree: $WT" >&2; exit 3; }
32
+
33
+ for p in "$@"; do
34
+ case "$p" in
35
+ -A|.|--all|-a) echo "do-commit: REFUSING blanket stage '$p' — name the cycle's own paths" >&2; exit 4 ;;
36
+ esac
37
+ done
38
+
39
+ # shellcheck source=lib/govern.sh
40
+ . "$(dirname "${BASH_SOURCE[0]}")/lib/govern.sh"
41
+ LOCK="commit-$(printf '%s' "$WT" | tr '/' '_')"
42
+
43
+ if ! gate_lock "$LOCK" "${DO_COMMIT_WAIT:-300}"; then
44
+ echo "do-commit: could not take the commit lock for $WT within ${DO_COMMIT_WAIT:-300}s" >&2
45
+ exit 5
46
+ fi
47
+ trap 'gate_release_all' EXIT INT TERM
48
+
49
+ # Only stage paths that exist or are tracked-and-deleted; a dissolved spec is normal.
50
+ staged=0
51
+ for p in "$@"; do
52
+ if [ -e "$WT/$p" ] || git -C "$WT" ls-files --error-unmatch -- "$p" >/dev/null 2>&1; then
53
+ git -C "$WT" add -- "$p" 2>/dev/null && staged=$((staged + 1))
54
+ else
55
+ echo "do-commit: skip (absent, never tracked): $p" >&2
56
+ fi
57
+ done
58
+
59
+ if [ "$staged" -eq 0 ] || git -C "$WT" diff --cached --quiet 2>/dev/null; then
60
+ echo "do-commit: ${SLUG} ${CID} — nothing staged to commit"
61
+ gate_release_all; trap - EXIT INT TERM
62
+ exit 0
63
+ fi
64
+
65
+ # Report exactly what this commit carries, so contamination is visible in the log
66
+ # rather than discovered by a later verifier.
67
+ echo "do-commit: ${SLUG} ${CID} staging $(git -C "$WT" diff --cached --name-only | wc -l | tr -d ' ') file(s):"
68
+ git -C "$WT" diff --cached --name-only | sed 's/^/ /'
69
+
70
+ git -C "$WT" commit -m "do(${SLUG}): ${CID}" >/dev/null 2>&1 \
71
+ && echo "do-commit: ${SLUG} ${CID} -> $(git -C "$WT" rev-parse --short HEAD)" \
72
+ || { echo "do-commit: commit FAILED for ${SLUG} ${CID}" >&2; gate_release_all; trap - EXIT INT TERM; exit 6; }
73
+
74
+ gate_release_all; trap - EXIT INT TERM
75
+ exit 0
@@ -9,6 +9,23 @@
9
9
  # default: prints one command per matched rule (no execution)
10
10
  # --exec: also runs each printed command; any non-zero exit fails the sweep
11
11
  set -euo pipefail
12
+
13
+ # _GATE — route a heavy compute through the machine governor. A gate_lock only
14
+ # dedupes IDENTICAL work; a SLOT is what bounds N worktrees each running one of
15
+ # these at once (measured 2026-09-07: three concurrent 2.5GB typecheckers, every
16
+ # lock uncontended, load 171). Empty when already inside a gate, so a nested call
17
+ # inherits the outer slot rather than taking a second one.
18
+ #
19
+ # Resolves gate-run.sh from its OWN directory, deliberately: an earlier version
20
+ # keyed off $ROOT and got inserted above the line that sets it, so _GATE was
21
+ # silently empty and every call ran ungoverned -- a fail-OPEN, which is the exact
22
+ # defect this preamble exists to close.
23
+ _GATE=()
24
+ if [ "${GOVERN_IN_GATE:-0}" != "1" ]; then
25
+ _GR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/gate-run.sh"
26
+ [ -f "$_GR" ] && _GATE=( bash "$_GR" "compute:$(basename "${BASH_SOURCE[0]}")" -- )
27
+ fi
28
+
12
29
  cd "$(dirname "${BASH_SOURCE[0]}")/../.." || exit 2
13
30
 
14
31
  EXEC=false
@@ -63,7 +80,7 @@ for f in "${FILES[@]}"; do
63
80
  [ -z "$cmd" ] && continue
64
81
  echo "[consumer-sweep] $f → $cmd"
65
82
  if $EXEC; then
66
- eval "$cmd" || { echo "[consumer-sweep] FAIL: consumer command failed for $f: $cmd" >&2; rc=1; }
83
+ "${_GATE[@]}" bash -c "$cmd" || { echo "[consumer-sweep] FAIL: consumer command failed for $f: $cmd" >&2; rc=1; }
67
84
  fi
68
85
  done <<< "$cmds"
69
86
  done
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env bash
2
+ # do-cycle-shape-check.sh — BEHAVIOURAL parity gate for the /do Status-kanban
3
+ # cycle-line shape, which is implemented four times in three languages:
4
+ # 1. .claude/workflows/do-engine.js openCyclesFromTodo() (JS regex, reader)
5
+ # 2. .claude/scripts/do-auto.sh _remaining() (grep -E, reader)
6
+ # 3. .claude/scripts/do-auto.sh _sync_status() (sed -E, writer)
7
+ # 4. .claude/scripts/do-tick.sh (grep + perl, writer)
8
+ #
9
+ # It does NOT string-compare the four regexes (that goes stale the day someone
10
+ # reformats one). It runs the REAL implementations — reader #2's pattern is
11
+ # scraped out of the live source at runtime, never pasted here — against one
12
+ # fixture matrix and asserts:
13
+ # READER PARITY both readers see the same open cycle ids
14
+ # WRITER CLOSES READER do-tick.sh can close every id a reader calls open
15
+ # RATCHET do-tick.sh never turns [x] back into [ ]
16
+ #
17
+ # --self-test proves each assertion can go RED (mutated copies in a scratch dir;
18
+ # the real repo files are never touched).
19
+ #
20
+ # manifest: portable
21
+ set -euo pipefail
22
+
23
+ REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
24
+ ENGINE_JS="${DO_SHAPE_ENGINE:-$REPO/.claude/workflows/do-engine.js}"
25
+ AUTO_SH="${DO_SHAPE_AUTO:-$REPO/.claude/scripts/do-auto.sh}"
26
+ TICK_SH="${DO_SHAPE_TICK:-$REPO/.claude/scripts/do-tick.sh}"
27
+ SLUG=shapefixture
28
+ FAILED=0
29
+
30
+ ok() { printf 'ok %s\n' "$*"; }
31
+ fail() { printf 'FAIL %s\n' "$*"; FAILED=1; }
32
+
33
+ # ── fixture ───────────────────────────────────────────────────────────────
34
+ make_fixture() { # $1 = root dir
35
+ mkdir -p "$1/text"
36
+ cat > "$1/text/${SLUG}-todo.md" <<'MD'
37
+ # Status
38
+
39
+ - [ ] C1 — plain
40
+ - [~] C2 — in progress
41
+ - [x] C3 — done
42
+ - [ ] **C4** — bold
43
+ - [ ] C5 — indented two spaces
44
+ - [ ] C10 — two digit
45
+ - [ ] C6 - hyphen not em-dash
46
+ - [ ] D1 — wrong prefix
47
+ Prose mentioning C1 inline should never count.
48
+ MD
49
+ }
50
+ # Expected OPEN set (the contract all four implementations must agree on).
51
+ EXPECTED="C1 C2 C4 C5 C10"
52
+
53
+ # ── reader #1: the real openCyclesFromTodo, sliced out of do-engine.js ─────
54
+ read_engine() { # $1 = todo path, $2 = engine js path
55
+ node -e '
56
+ const fs = require("fs");
57
+ const src = fs.readFileSync(process.argv[1], "utf8");
58
+ const i = src.indexOf("function openCyclesFromTodo");
59
+ if (i < 0) { console.error("openCyclesFromTodo not found"); process.exit(9); }
60
+ const j = src.indexOf("\n}\n", i);
61
+ if (j < 0) { console.error("could not slice function body"); process.exit(9); }
62
+ const fn = (0, eval)("(" + src.slice(i, j + 2) + ")");
63
+ console.log(fn(fs.readFileSync(process.argv[2], "utf8")).join(" "));
64
+ ' "$2" "$1"
65
+ }
66
+
67
+ # ── reader #2: the LIVE grep pattern scraped out of do-auto.sh _remaining() ─
68
+ auto_pattern() { # prints the pattern do-auto.sh actually greps with
69
+ local line
70
+ line=$(grep -nE "grep -cE .*C\[0-9\]" "$AUTO_SH" | head -1)
71
+ [ -n "$line" ] || { echo "do-auto _remaining pattern not found" >&2; return 9; }
72
+ sed -E "s/^[^']*'//; s/'.*$//" <<<"${line#*:}"
73
+ }
74
+ read_auto() { # $1 = todo path
75
+ local pat matched
76
+ pat=$(auto_pattern)
77
+ # SIGPIPE trap: never pipe a producer into a short-circuiting consumer.
78
+ matched=$(grep -oE "$pat" "$1" 2>/dev/null || true)
79
+ local ids; ids=$(grep -oE 'C[0-9]+' <<<"$matched" || true)
80
+ tr '\n' ' ' <<<"$ids" | sed -E 's/ +$//'
81
+ }
82
+
83
+ norm() { tr -s ' ' '\n' <<<"$1" | sed '/^$/d' | tr '\n' ' ' | sed -E 's/ +$//'; }
84
+
85
+ # ── the three checks, parameterised so --self-test can mutate the inputs ───
86
+ run_checks() { # $1 = label prefix, $2 = engine js, $3 = tick cmd, $4 = strict(1)|report(0)
87
+ local label="$1" engine="$2" tick="$3"
88
+ local root; root=$(mktemp -d); trap 'rm -rf "$root"' RETURN
89
+ make_fixture "$root"
90
+ local todo="$root/text/${SLUG}-todo.md"
91
+ local a b
92
+ a=$(norm "$(read_engine "$todo" "$engine")")
93
+ b=$(norm "$(read_auto "$todo")")
94
+ local rc=0
95
+ if [ "$a" = "$b" ]; then ok "$label reader parity — both readers: [$a]"; else
96
+ fail "$label reader parity — do-engine:[$a] != do-auto:[$b]"; rc=1; fi
97
+ if [ "$b" = "$(norm "$EXPECTED")" ]; then ok "$label reader vs contract — [$b]"; else
98
+ fail "$label reader vs contract — expected [$EXPECTED] got [$b]"; rc=1; fi
99
+
100
+ # WRITER CLOSES READER
101
+ local cid wrc=0
102
+ for cid in $b; do
103
+ DO_TICK_ROOT="$root" bash $tick "$SLUG" "$cid" >/dev/null 2>&1 || { fail "$label do-tick failed on $cid"; wrc=1; }
104
+ local a2 b2
105
+ a2=" $(norm "$(read_engine "$todo" "$engine")") "
106
+ b2=" $(norm "$(read_auto "$todo")") "
107
+ case "$a2$b2" in *" $cid "*) fail "$label writer-closes-reader — $cid still open after tick"; wrc=1;; esac
108
+ done
109
+ [ "$wrc" = 0 ] && ok "$label writer closes reader — all of [$b] closed by do-tick" || rc=1
110
+
111
+ # RATCHET — a closed cycle must not reopen
112
+ local before after
113
+ before=$(cksum < "$todo")
114
+ DO_TICK_ROOT="$root" bash $tick "$SLUG" C3 >/dev/null 2>&1 || true
115
+ after=$(cksum < "$todo")
116
+ if [ "$before" = "$after" ]; then ok "$label ratchet — [x] C3 untouched"; else
117
+ fail "$label ratchet — do-tick mutated an already-closed cycle"; rc=1; fi
118
+ return $rc
119
+ }
120
+
121
+ # ── self-test: prove each assertion can go RED ────────────────────────────
122
+ if [ "${1:-}" = "--self-test" ]; then
123
+ SCRATCH="${TMPDIR:-/tmp}/do-cycle-shape-selftest.$$"
124
+ case "${SCRATCH_BASE:-}" in "") ;; *) SCRATCH="$SCRATCH_BASE/do-cycle-shape-selftest.$$";; esac
125
+ mkdir -p "$SCRATCH"; trap 'rm -rf "$SCRATCH"' EXIT
126
+
127
+ # A fixed reader (tolerates **C4**) — the green control.
128
+ cat > "$SCRATCH/engine-fixed.js" <<'JS'
129
+ function openCyclesFromTodo(todoText) {
130
+ const re = /^[ \t]*-\s\[[ ~]\]\s\*{0,2}(C\d+)\*{0,2}\s—/gm
131
+ const out = []
132
+ let m
133
+ while ((m = re.exec(todoText))) out.push(m[1])
134
+ return out
135
+ }
136
+ JS
137
+ # The bug: drop \*{0,2} — blind to the bold form.
138
+ sed 's/\\\*{0,2}//g' "$SCRATCH/engine-fixed.js" > "$SCRATCH/engine-bold-blind.js"
139
+ # A stubbed writer that ticks nothing.
140
+ printf '#!/usr/bin/env bash\nexit 0\n' > "$SCRATCH/tick-stub.sh"; chmod +x "$SCRATCH/tick-stub.sh"
141
+
142
+ echo "-- control: fixed reader + real writer (must be GREEN)"
143
+ FAILED=0; run_checks "[control]" "$SCRATCH/engine-fixed.js" "$TICK_SH" || true
144
+ ctrl=$FAILED
145
+ echo "-- red proof (a): reader without \\*{0,2} (must go RED)"
146
+ FAILED=0; run_checks "[red-a]" "$SCRATCH/engine-bold-blind.js" "$TICK_SH" >/dev/null 2>&1 || true
147
+ reda=$FAILED
148
+ echo "-- red proof (b): do-tick stubbed to write nothing (must go RED)"
149
+ FAILED=0; run_checks "[red-b]" "$SCRATCH/engine-fixed.js" "$SCRATCH/tick-stub.sh" >/dev/null 2>&1 || true
150
+ redb=$FAILED
151
+
152
+ FAILED=0
153
+ [ "$ctrl" = 0 ] && ok "self-test control green" || fail "self-test control should be green"
154
+ [ "$reda" = 1 ] && ok "self-test red-proof (a) bold-blind reader goes RED" || fail "self-test (a) stayed green against a reintroduced bug"
155
+ [ "$redb" = 1 ] && ok "self-test red-proof (b) no-op writer goes RED" || fail "self-test (b) stayed green against a writer that writes nothing"
156
+ exit $FAILED
157
+ fi
158
+
159
+ run_checks "" "$ENGINE_JS" "$TICK_SH" || true
160
+ exit $FAILED