@oneie/claude 0.6.0 → 0.7.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 (111) hide show
  1. package/agents/w1-recon.md +9 -4
  2. package/agents/w2-decide.md +51 -28
  3. package/agents/w3-edit.md +70 -78
  4. package/agents/w4-verify.md +164 -69
  5. package/commands/browser.md +98 -35
  6. package/commands/cc-connect.md +6 -0
  7. package/commands/chat.md +13 -1
  8. package/commands/close.md +45 -19
  9. package/commands/create.md +20 -10
  10. package/commands/db-sync.md +5 -1
  11. package/commands/deploy.md +174 -87
  12. package/commands/do-autonomous.md +31 -20
  13. package/commands/do-improve.md +4 -4
  14. package/commands/do-show.md +4 -3
  15. package/commands/do.md +53 -13
  16. package/commands/go.md +75 -0
  17. package/commands/improve.md +4 -2
  18. package/commands/kill.md +6 -8
  19. package/commands/notify.md +15 -2
  20. package/commands/oo-push.md +24 -9
  21. package/commands/rag.md +6 -0
  22. package/commands/release.md +46 -25
  23. package/commands/restart.md +5 -4
  24. package/commands/see.md +45 -35
  25. package/commands/skill-create.md +38 -5
  26. package/commands/sync.md +21 -11
  27. package/hooks/scripts/auto-continue.sh +70 -0
  28. package/hooks/scripts/branch-pin.sh +85 -0
  29. package/hooks/scripts/git-add-guard.sh +16 -5
  30. package/hooks/scripts/post-edit-check.sh +16 -0
  31. package/hooks/scripts/session-start.sh +13 -8
  32. package/hooks/scripts/sync-priority-todo.sh +5 -0
  33. package/package.json +1 -1
  34. package/rules/api.md +9 -0
  35. package/rules/design.md +13 -3
  36. package/rules/documentation.md +30 -71
  37. package/rules/engine.md +8 -1
  38. package/rules/ui.md +5 -1
  39. package/scripts/__pycache__/do-rank.cpython-313.pyc +0 -0
  40. package/scripts/browser-check.mjs +44 -103
  41. package/scripts/cc-events-proof.sh +13 -12
  42. package/scripts/chrome.mjs +381 -0
  43. package/scripts/do-analyze.sh +109 -4
  44. package/scripts/do-auto.sh +350 -43
  45. package/scripts/do-consumer-sweep.sh +72 -0
  46. package/scripts/do-derives-check.sh +88 -0
  47. package/scripts/do-fleet.sh +50 -1
  48. package/scripts/do-killswitch-audit.py +33 -14
  49. package/scripts/do-promise-lint.sh +150 -4
  50. package/scripts/do-promise-settle.sh +131 -9
  51. package/scripts/do-prove.sh +197 -17
  52. package/scripts/do-rank.py +606 -20
  53. package/scripts/do-rubric.py +21 -2
  54. package/scripts/do-signal.sh +157 -17
  55. package/scripts/do-smoke.sh +9 -0
  56. package/scripts/do-substrate-check.sh +49 -0
  57. package/scripts/do-tasks-wire-check.sh +81 -0
  58. package/scripts/do-tier.sh +90 -5
  59. package/scripts/do-ui-gate.sh +332 -0
  60. package/scripts/do-untracked-gate.sh +231 -0
  61. package/scripts/do-walk.sh +344 -0
  62. package/scripts/factory-check.sh +747 -0
  63. package/scripts/factory-repo.sh +1361 -0
  64. package/scripts/factory-tasks-check.sh +85 -0
  65. package/scripts/outcome-pull.ts +279 -0
  66. package/scripts/promise-manifest.mjs +167 -0
  67. package/scripts/reconcile-allow.txt +5 -0
  68. package/scripts/rubric-weights.json +64 -0
  69. package/scripts/spine-canary.sh +20 -0
  70. package/scripts/typedb-cluster-status.sh +128 -0
  71. package/scripts/typedb-env.sh +182 -0
  72. package/scripts/typedb-flap-recorder.sh +85 -0
  73. package/scripts/typedb-probes/containment-probe.py +88 -0
  74. package/scripts/typedb-probes/panic-probe-version.py +92 -0
  75. package/scripts/typedb-probes/panic-probe.py +77 -0
  76. package/scripts/typedb-scratch.sh +73 -0
  77. package/scripts/urls-lint.sh +58 -0
  78. package/scripts/w4-rubric.ts +96 -12
  79. package/scripts/wf-check.mjs +75 -0
  80. package/skills/ai-ui/SKILL.md +1 -1
  81. package/skills/astro/SKILL.md +177 -132
  82. package/skills/astro/add-content-collection.md +29 -13
  83. package/skills/astro/check-build.md +19 -13
  84. package/skills/astro/create-component.md +36 -9
  85. package/skills/astro/create-page.md +39 -10
  86. package/skills/astro/optimize-performance.md +36 -8
  87. package/skills/cli/SKILL.md +254 -220
  88. package/skills/dev/SKILL.md +1 -1
  89. package/skills/directory-autofill/SKILL.md +68 -0
  90. package/skills/docs/SKILL.md +4 -4
  91. package/skills/mcp/SKILL.md +186 -156
  92. package/skills/promise-make/SKILL.md +2 -2
  93. package/skills/puck/SKILL.md +465 -250
  94. package/skills/react19/SKILL.md +196 -296
  95. package/skills/reactflow/SKILL.md +387 -412
  96. package/skills/sdk/SKILL.md +167 -106
  97. package/skills/shadcn/SKILL.md +245 -201
  98. package/skills/signal/SKILL.md +5 -0
  99. package/skills/sui/SKILL.md +6 -1
  100. package/skills/tutorial/SKILL.md +15 -0
  101. package/skills/typedb/SKILL.md +427 -217
  102. package/skills/typedb/examples/python-patterns.py +11 -4
  103. package/skills/typedb/examples/query-patterns.tql +8 -3
  104. package/skills/typedb/examples/schema-patterns.tql +4 -2
  105. package/skills/typedb/reference/migration-2x-3x.md +19 -1
  106. package/skills/typedb/reference/python-driver.md +17 -1
  107. package/skills/voice/SKILL.md +117 -0
  108. package/skills/writer/SKILL.md +29 -12
  109. package/templates/template-feature.md +26 -1
  110. package/templates/template-tests.md +4 -0
  111. package/templates/template-todo.md +65 -10
@@ -333,10 +333,22 @@ def score_integration(src: str, files: list[Path]) -> tuple[float, list[str], li
333
333
 
334
334
 
335
335
  # ── Composite & report ────────────────────────────────────────────────────────
336
- WEIGHTS = {"security": 0.20, "stability": 0.20, "simplicity": 0.15, "integration": 0.10, "speed": 0.10}
337
- # goal-fit (0.30) is plan-specific omitted here; composite is over the 5 code axes normalised
336
+ # Weights come from rubric-weights.json the single source. This script scores a
337
+ # PACKAGE source tree, so it has no plan context and goal-fit (0.30) is genuinely
338
+ # unavailable: its omission is correct, not drift. What that costs is comparability
339
+ # — this number is a BASELINE, never a gate verdict, and it is emitted as
340
+ # `composite_partial` so nothing can compare it to a cycle composite that does
341
+ # carry goal-fit. See the `package` block in rubric-weights.json.
342
+ _WEIGHTS_PATH = Path(__file__).with_name("rubric-weights.json")
343
+ try:
344
+ _RUBRIC = json.loads(_WEIGHTS_PATH.read_text())
345
+ WEIGHTS = _RUBRIC["package"]["weights"]
346
+ except (OSError, KeyError, json.JSONDecodeError) as e:
347
+ raise SystemExit(f"do-rubric: cannot read {_WEIGHTS_PATH}: {e}")
348
+
338
349
 
339
350
  def composite(scores: dict) -> float:
351
+ """Partial composite over the code axes only, normalised. NOT a gate verdict."""
340
352
  total_w = sum(WEIGHTS.values())
341
353
  return round(sum(scores[k] * WEIGHTS[k] for k in WEIGHTS) / total_w, 2)
342
354
 
@@ -379,7 +391,14 @@ def run(target_str: str, as_json: bool = False) -> dict:
379
391
  "files": len(files),
380
392
  "loc": src.count("\n"),
381
393
  "scores": scores,
394
+ # `composite` is kept for the existing readers (do-auto.sh writes the W0
395
+ # baseline from it). The two fields beside it say what it actually is, so
396
+ # a reader can tell a partial baseline from a cycle verdict instead of
397
+ # assuming they are the same scale.
382
398
  "composite": comp,
399
+ "composite_partial": comp,
400
+ "rubric": "package",
401
+ "goal_fit_included": False,
383
402
  "findings": {
384
403
  "security": sec_f,
385
404
  "stability": sta_f,
@@ -1,12 +1,18 @@
1
1
  #!/usr/bin/env bash
2
- # Internal: emit world:do-event signals at /do phase boundaries.
3
- # Called by do-auto.sh at cycle open, close, and halt. Not a user command.
2
+ # Internal: emit world:do-event signals at /do phase boundaries, and drive the
3
+ # tasks:create/claim/link verbs so a /do plan's own lifecycle IS a substrate
4
+ # task's lifecycle (text/tasks-do-plan.md C1 — the shared-atom wire).
5
+ # Called by do-auto.sh at plan arm, cycle open, cycle close, and halt. Not a
6
+ # user command.
4
7
  #
5
8
  # Usage:
6
9
  # do-signal.sh [--print] <type> <slug> <tier> <cycle> [key=value ...]
10
+ # do-signal.sh [--print] --task-create <slug> <title> [tags=a,b,c] [notes=...]
11
+ # do-signal.sh [--print] --task-claim <slug>
12
+ # do-signal.sh [--print] --task-link <slug> [docs=a,b,c]
7
13
  #
8
14
  # --print: write JSON to stdout without POSTing (tests + dry runs).
9
- # Extra key=value pairs are merged into the payload.
15
+ # Extra key=value pairs are merged into the world:do-event payload.
10
16
  # axis=security,structure → adds s:security + s:structure tags + "axis" field
11
17
  # composite=0.82 → numeric field (no quotes)
12
18
  # wave=W3 → string field
@@ -14,24 +20,152 @@
14
20
  #
15
21
  # Env overrides:
16
22
  # DO_SIGNAL_URL default: https://one.ie
17
- # DO_ENV_FILE default: one.ie/web/.env (read SERVER_SECRET for auth)
23
+ # ONE_ENV_FILE default: one.ie/web/.env (read SERVER_SECRET for auth)
24
+ # DO_ENV_FILE accepted as an alias for ONE_ENV_FILE (kept for callers)
18
25
  #
19
26
  # Signal failure never blocks the build loop — callers wrap with `|| true`.
20
27
  set -euo pipefail
21
28
 
22
29
  PRINT=false
23
- DO_ENV_FILE="${DO_ENV_FILE:-one.ie/web/.env}"
30
+ # Credentials come from ONE_ENV_FILE, so the harness runs with no one.ie/web/
31
+ # on disk. DO_ENV_FILE stays an accepted alias — it predates the generalisation.
32
+ ONE_ENV_FILE="${ONE_ENV_FILE:-${DO_ENV_FILE:-one.ie/web/.env}}"
33
+ DO_ENV_FILE="$ONE_ENV_FILE"
24
34
  DO_SIGNAL_URL="${DO_SIGNAL_URL:-https://one.ie}"
25
35
 
36
+ _ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
37
+
38
+ # Monorepo fallbacks. Unquoted on purpose: they expand to nothing outside the
39
+ # monorepo, so the loop degrades to ONE_ENV_FILE alone rather than erroring.
40
+ _MONOREPO_ENV_CANDIDATES="$_ROOT/one.ie/web/.env $_ROOT/one.ie/web/.secrets.generated.local $_ROOT/one.ie/web/.dev.vars"
41
+
42
+ # Shared SERVER_SECRET lookup — /api/signal/world:do-event checks this (see
43
+ # world:do-event POST at the bottom of this file).
44
+ _server_secret() {
45
+ local _f _v
46
+ for _f in "$ONE_ENV_FILE" "$_ROOT/.env" $_MONOREPO_ENV_CANDIDATES; do
47
+ [ -f "$_f" ] || continue
48
+ _v=$(grep -E '^SERVER_SECRET=' "$_f" 2>/dev/null | head -1 | sed 's/^[^=]*=//;s/^"//;s/"$//' || true)
49
+ [ -n "$_v" ] && { printf '%s' "$_v"; return 0; }
50
+ done
51
+ printf ''
52
+ }
53
+
54
+ # GATEWAY_API_KEY lookup — /api/ask/<receiver> (the tasks:* verbs) checks THIS,
55
+ # not SERVER_SECRET (one.ie/web/src/pages/api/ask/[...receiver].ts isServiceCaller).
56
+ # Presenting SERVER_SECRET there resolves to no service caller, no ctx.ownerSlug,
57
+ # and every tasks:* resolver returns {error:"workspace required"} — verified
58
+ # 2026-07-17, zero do-signal task-create/claim/link calls had ever landed in prod.
59
+ _gateway_key() {
60
+ local _f _v
61
+ for _f in "$ONE_ENV_FILE" "$_ROOT/.env" $_MONOREPO_ENV_CANDIDATES; do
62
+ [ -f "$_f" ] || continue
63
+ _v=$(grep -E '^GATEWAY_API_KEY=' "$_f" 2>/dev/null | head -1 | sed 's/^[^=]*=//;s/^"//;s/"$//' || true)
64
+ [ -n "$_v" ] && { printf '%s' "$_v"; return 0; }
65
+ done
66
+ printf ''
67
+ }
68
+
69
+ # POST a JSON payload to /api/ask/<receiver> with bearer auth. Fire-and-forget:
70
+ # a failure is silent so the build loop is never blocked (same stance as the
71
+ # world:do-event POST below). Prints the response body (empty on failure).
72
+ _post_ask() {
73
+ local _receiver="$1" _payload="$2" _secret _authcfg _url _resp
74
+ _secret="$(_gateway_key)"
75
+ _url="${DO_SIGNAL_URL}/api/ask/${_receiver}"
76
+ if [ -n "$_secret" ]; then
77
+ _authcfg="$(mktemp)"; chmod 600 "$_authcfg"
78
+ printf 'header = "Authorization: Bearer %s"\n' "$_secret" > "$_authcfg"
79
+ _resp=$(curl -sf -X POST "$_url" -H "Content-Type: application/json" --config "$_authcfg" -d "$_payload" 2>/dev/null || true)
80
+ rm -f "$_authcfg"
81
+ else
82
+ _resp=$(curl -sf -X POST "$_url" -H "Content-Type: application/json" -d "$_payload" 2>/dev/null || true)
83
+ fi
84
+ printf '%s' "$_resp"
85
+ }
86
+
87
+ # Every task this harness files lands in one workspace — /u/one/tasks — so a
88
+ # human never has to hunt across slugs for Claude Code's own work. Overridable
89
+ # for a differently-branded harness fork; unset means "one" (same convention
90
+ # as CC_WORLD_SLUG in hooks/lib/signal.sh). ctx.ownerSlug resolves straight to
91
+ # this value (nominated via GATEWAY_API_KEY auth above), so every tasks:*
92
+ # resolver's actor/owner/workspace tag is this slug, not whatever ambient
93
+ # session happened to be attested.
94
+ CC_TASKS_WORKSPACE="${CC_TASKS_WORKSPACE:-one}"
95
+
96
+ # signal_tasks_create — plan arm: files an open task tagged slug:<slug>, so the
97
+ # plan appears in tasks:everywhere the moment it's armed (C1/C2 interface contract).
98
+ signal_tasks_create() { # signal_tasks_create <slug> <title> [tags=a,b,c] [notes=...]
99
+ local slug="$1" title="$2" tags="slug:${1}" notes=""
100
+ shift 2
101
+ for kv in "$@"; do
102
+ case "${kv%%=*}" in
103
+ tags) tags="${tags},${kv#*=}" ;;
104
+ notes) notes="${kv#*=}" ;;
105
+ esac
106
+ done
107
+ local tags_json; tags_json=$(printf '%s' "$tags" | awk -F',' '{for(i=1;i<=NF;i++){printf "%s\"%s\"", (i>1?",":""), $i}}')
108
+ local payload="{\"data\":{\"title\":\"${title}\",\"workspace\":\"${CC_TASKS_WORKSPACE}\",\"tags\":[${tags_json}]${notes:+,\"notes\":\"${notes}\"}}}"
109
+ if $PRINT; then printf '%s\n' "$payload"; return 0; fi
110
+ _post_ask "tasks:create" "$payload"
111
+ }
112
+
113
+ # signal_tasks_claim — cycle open: leases the plan's substrate task so a fleet
114
+ # run (or a human) never double-builds the same slug (do-fleet.sh mirrors this).
115
+ signal_tasks_claim() { # signal_tasks_claim <slug>
116
+ # Address the lease by the plan SLUG, not a guessed tid: create returns a random
117
+ # task:<traceId>, so "task:<slug>" never matched — claim silently 404'd for every /do
118
+ # run until this. The server resolves slug → tid (resolvers/tasks.ts resolveTaskBySlug).
119
+ local slug="$1"
120
+ local payload="{\"data\":{\"slug\":\"${slug}\",\"workspace\":\"${CC_TASKS_WORKSPACE}\"}}"
121
+ if $PRINT; then printf '%s\n' "$payload"; return 0; fi
122
+ _post_ask "tasks:claim" "$payload"
123
+ }
124
+
125
+ # signal_tasks_link — cycle close: records the handoff-completion back on the
126
+ # origin task (provenance line + task-status:done), same shape lifecycle.ts's
127
+ # tasks_link MCP tool posts.
128
+ signal_tasks_link() { # signal_tasks_link <slug> [docs=a,b,c]
129
+ # Slug-addressed like claim — the server resolves slug → the origin tid create wrote.
130
+ local slug="$1" docs=""
131
+ shift 1
132
+ for kv in "$@"; do
133
+ case "${kv%%=*}" in
134
+ docs) docs="${kv#*=}" ;;
135
+ esac
136
+ done
137
+ local docs_json=""
138
+ [ -n "$docs" ] && docs_json=$(printf '%s' "$docs" | awk -F',' '{for(i=1;i<=NF;i++){printf "%s\"%s\"", (i>1?",":""), $i}}')
139
+ local payload="{\"data\":{\"slug\":\"${slug}\",\"workspace\":\"${CC_TASKS_WORKSPACE}\"${docs_json:+,\"docs\":[${docs_json}]}}}"
140
+ if $PRINT; then printf '%s\n' "$payload"; return 0; fi
141
+ _post_ask "tasks:link" "$payload"
142
+ }
143
+
26
144
  while [ "$#" -gt 0 ]; do
27
145
  case "$1" in
28
146
  --print) PRINT=true; shift ;;
147
+ # Report WHICH file credentials resolved from, never the value. This is how
148
+ # factory-repo.sh --check-env-indirection proves the override is real
149
+ # behaviour rather than a grep for a variable name.
150
+ --print-env)
151
+ _which=""
152
+ for _f in "$ONE_ENV_FILE" "$_ROOT/.env" $_MONOREPO_ENV_CANDIDATES; do
153
+ [ -f "$_f" ] || continue
154
+ grep -qE '^(SERVER_SECRET|GATEWAY_API_KEY)=.' "$_f" 2>/dev/null || continue
155
+ _which="$_f"; break
156
+ done
157
+ printf 'resolved-from: %s\n' "${_which:-<none>}"
158
+ exit 0 ;;
159
+ --task-create) shift; signal_tasks_create "$@"; exit 0 ;;
160
+ --task-claim) shift; signal_tasks_claim "$@"; exit 0 ;;
161
+ --task-link) shift; signal_tasks_link "$@"; exit 0 ;;
29
162
  *) break ;;
30
163
  esac
31
164
  done
32
165
 
33
166
  if [ "$#" -lt 4 ]; then
34
167
  echo "usage: do-signal.sh [--print] <type> <slug> <tier> <cycle> [key=value ...]" >&2
168
+ echo " do-signal.sh [--print] --task-create|--task-claim|--task-link <slug> ..." >&2
35
169
  exit 1
36
170
  fi
37
171
 
@@ -66,6 +200,18 @@ for kv in "$@"; do
66
200
  composite)
67
201
  EXTRA_JSON="${EXTRA_JSON},\"${k}\":${v}"
68
202
  ;;
203
+ worktags)
204
+ # comma-list → JSON array field. The plan's task-vocabulary tags (dept + topics);
205
+ # the reputation harvest credits these tag-nodes (text/reputation.md § Slice A).
206
+ _wt=""; _w="${v}"
207
+ while [ -n "$_w" ]; do
208
+ _witem="${_w%%,*}"
209
+ [ -n "$_witem" ] && _wt="${_wt:+${_wt},}\"${_witem}\""
210
+ [ "$_w" = "$_witem" ] && break
211
+ _w="${_w#*,}"
212
+ done
213
+ EXTRA_JSON="${EXTRA_JSON},\"worktags\":[${_wt}]"
214
+ ;;
69
215
  *)
70
216
  EXTRA_JSON="${EXTRA_JSON},\"${k}\":\"${v}\""
71
217
  ;;
@@ -79,18 +225,12 @@ if $PRINT; then
79
225
  exit 0
80
226
  fi
81
227
 
82
- # Read SERVER_SECRET for the Authorization header — first file that has it wins.
83
- # .env historically lacks it; .secrets.generated.local is what push-prod-secrets.sh
84
- # pushed to the worker, so it matches prod. Without a matching secret the POST is
85
- # silently 401/403-dropped at the gateway (verified 2026-07-04 zero do-* threads
86
- # had ever landed in prod D1).
87
- _ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
88
- SERVER_SECRET=""
89
- for _f in "$DO_ENV_FILE" "$_ROOT/one.ie/web/.env" "$_ROOT/one.ie/web/.secrets.generated.local" "$_ROOT/one.ie/web/.dev.vars"; do
90
- [ -f "$_f" ] || continue
91
- SERVER_SECRET=$(grep -E '^SERVER_SECRET=' "$_f" 2>/dev/null | head -1 | sed 's/^[^=]*=//;s/^"//;s/"$//' || true)
92
- [ -n "$SERVER_SECRET" ] && break
93
- done
228
+ # SERVER_SECRET for the Authorization header — _server_secret() (defined above)
229
+ # is the one lookup, shared with the tasks:* verbs. .env historically lacks it;
230
+ # .secrets.generated.local is what push-prod-secrets.sh pushed to the worker, so
231
+ # it matches prod. Without a matching secret the POST is silently 401/403-dropped
232
+ # at the gateway (verified 2026-07-04 — zero do-* threads had ever landed in prod D1).
233
+ SERVER_SECRET="$(_server_secret)"
94
234
 
95
235
  # POST — fire-and-forget; failures are silent so the build loop is never blocked.
96
236
  _url="${DO_SIGNAL_URL}/api/signal/world:do-event"
@@ -21,6 +21,15 @@ echo "== C7 do-tier =="
21
21
  "$S/do-tier.sh" pay/contracts/sui/sources/one_token.move | grep -q '"tier":"SCHEMA"' && ok "contract source → SCHEMA" || no "contract source"
22
22
  "$S/do-tier.sh" pay/contracts/sui/tests/gateway_tests.move | grep -q '"tier":"FIX"' && ok "contract test → FIX (not SCHEMA)" || no "contract test"
23
23
  "$S/do-tier.sh" --intent "add billing" one.ie/web/src/api/b.ts | grep -q '"tier":"FEATURE"' && ok "intent → FEATURE" || no "FEATURE"
24
+ # Absence of recon must not read as simplicity: no paths → UNSIZED, never PATCH.
25
+ # Intent is deliberately ignored here — "add a null check" and "add a settings page"
26
+ # share a verb, so a sentence alone can't size anything. Exit 3 is the contract.
27
+ # Capture before grepping: UNSIZED exits 3, and under `pipefail` that propagates
28
+ # through the pipe even when grep matches — the same trap any caller piping do-tier
29
+ # into grep/jq will hit now that it can exit non-zero.
30
+ UNS=$(echo "" | "$S/do-tier.sh" --intent "add a settings page" 2>/dev/null); UNS_RC=$?
31
+ case "$UNS" in *'"tier":"UNSIZED"'*) ok "no paths → UNSIZED (not PATCH)" ;; *) no "UNSIZED" ;; esac
32
+ [ "$UNS_RC" -eq 3 ] && ok "UNSIZED → exit 3" || no "UNSIZED exit code (got $UNS_RC)"
24
33
 
25
34
  echo "== C2 do-reconcile — substrate canon =="
26
35
  echo "uses a node in the colony" | "$S/do-reconcile.sh" substrate >/dev/null 2>&1 && no "dead name should fail substrate" || ok "dead name → substrate exit 1"
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+ # do-substrate-check.sh — can this machine reach TypeDB DIRECTLY, right now?
3
+ #
4
+ # Distinct from `do-rank.py --board-check`, which asks whether the *gateway*
5
+ # accepts us as a service caller. The two fail independently and the difference
6
+ # decides what is provable:
7
+ # board-check RED + substrate GREEN → board logic is testable against the real
8
+ # cluster by calling resolvers directly; anything routed through api.one.ie
9
+ # or one.ie is not.
10
+ # substrate RED → no test that touches the graph can prove anything, and a
11
+ # `describe.skipIf(...)` suite will PASS while proving nothing (a skipped
12
+ # test is a green test — never let an accept: rest on one).
13
+ #
14
+ # Reads TYPEDB_URL / TYPEDB_USERNAME / TYPEDB_PASSWORD from one.ie/web/.env.
15
+ # Never echoes the password or the returned token.
16
+ #
17
+ # Usage: do-substrate-check.sh [--quiet]
18
+ # Exit: 0 = reachable and authenticating · 1 = unreachable/refused · 2 = no creds
19
+ set -uo pipefail
20
+ cd "$(dirname "${BASH_SOURCE[0]}")/../.." || exit 2
21
+
22
+ QUIET=false
23
+ [ "${1:-}" = "--quiet" ] && QUIET=true
24
+ say() { $QUIET || echo "$@"; }
25
+
26
+ ENVF=one.ie/web/.env
27
+ val() { grep -h "^$1=" "$ENVF" 2>/dev/null | head -1 | sed 's/^[^=]*=//; s/^"//; s/"$//'; }
28
+
29
+ URL=$(val TYPEDB_URL); USER=$(val TYPEDB_USERNAME); PASS=$(val TYPEDB_PASSWORD)
30
+ if [ -z "$URL" ] || [ -z "$USER" ] || [ -z "$PASS" ]; then
31
+ say "[substrate] NO CREDS — TYPEDB_URL/USERNAME/PASSWORD missing from $ENVF"
32
+ exit 2
33
+ fi
34
+
35
+ # Credentials ride a temp file, never argv: a bare --data on the command line is
36
+ # visible to any local user via `ps` for the life of the process.
37
+ BODY=$(mktemp); chmod 600 "$BODY"
38
+ printf '{"username":"%s","password":"%s"}' "$USER" "$PASS" > "$BODY"
39
+ CODE=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 20 \
40
+ -X POST "$URL/v1/signin" -H 'Content-Type: application/json' \
41
+ --data-binary @"$BODY" 2>/dev/null)
42
+ rm -f "$BODY"
43
+
44
+ if [ "$CODE" = "200" ]; then
45
+ say "[substrate] GREEN — ${URL} authenticates"
46
+ exit 0
47
+ fi
48
+ say "[substrate] RED — ${URL}/v1/signin → HTTP ${CODE:-no-response}"
49
+ exit 1
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ # do-tasks-wire-check — assert the /do ⇄ substrate-task wires are actually connected.
3
+ #
4
+ # Why this exists: text/tasks-do.md's deliverables were all `grep -q '<receiver-name>'`
5
+ # checks. Every one stayed GREEN for months while three of the wires were dead in prod —
6
+ # a grep for "tasks:everywhere" passes just as happily when the call authenticates with
7
+ # the wrong secret, parses the wrong envelope key, and addresses the lease by a tid that
8
+ # can never resolve. Presence is not connection. These checks assert the SHAPE that was
9
+ # broken, so a regression to any of the four known-bad forms turns the promise red.
10
+ #
11
+ # Usage: bash .claude/scripts/do-tasks-wire-check.sh → exit 0 all wired, 1 otherwise.
12
+
13
+ set -uo pipefail
14
+ ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
15
+ cd "$ROOT" || exit 1
16
+
17
+ FAIL=0
18
+ ok() { printf ' ✓ %s\n' "$1"; }
19
+ bad() { printf ' ✗ %s\n' "$1"; FAIL=1; }
20
+ check() { if eval "$2" >/dev/null 2>&1; then ok "$1"; else bad "$1 — $3"; fi; }
21
+
22
+ echo "[tasks-wire] /do ⇄ substrate task wires"
23
+
24
+ # 1. The ranker authenticates as a SERVICE CALLER.
25
+ # /api/ask/<receiver> decides isServiceCaller off env.GATEWAY_API_KEY. SERVER_SECRET
26
+ # is what /api/signal/ checks — the two routes do not share a secret.
27
+ check "do-rank reads the board with GATEWAY_API_KEY" \
28
+ "grep -q 'secret = _gateway_key()' .claude/scripts/do-rank.py" \
29
+ "fetch_tasks_everywhere must call _gateway_key(), not _server_secret()"
30
+
31
+ # 2. The ranker nominates a workspace. A service call carries no session actor, so
32
+ # ctx.ownerSlug comes solely from payload.slug|payload.workspace. Omit it and
33
+ # tasks:everywhere answers {ok:false,error:"forbidden: authentication required"}.
34
+ check "do-rank nominates a workspace on the board read" \
35
+ "grep -q '\"workspace\": ws' .claude/scripts/do-rank.py" \
36
+ "the tasks:everywhere payload must carry workspace"
37
+
38
+ # 3. The ranker parses the /api/ask ENVELOPE. Answers are wrapped
39
+ # {outcome, result:{ok, tasks}, signalId, receiver} — reading ok/tasks off the top
40
+ # level makes even a good answer look like a failure, so the merge never fires.
41
+ check "do-rank unwraps the /api/ask result envelope" \
42
+ "grep -qF 'payload.get(\"result\")' .claude/scripts/do-rank.py" \
43
+ "must read ok/tasks from payload['result'], not the top level"
44
+
45
+ # 4. Cloudflare answers the stock urllib User-Agent with 403 "error code: 1010" before
46
+ # the Worker ever runs. curl is unaffected, which is why only the ranker hit this.
47
+ check "do-rank sends a real User-Agent (CF bot block)" \
48
+ "grep -q 'User-Agent' .claude/scripts/do-rank.py" \
49
+ "the board request must set a User-Agent header"
50
+
51
+ # 5. The fleet addresses the lease by SLUG. Real tids are minted task:<traceId>
52
+ # (resolvers/tasks.ts insertTaskRow), so a guessed "task:<slug>" resolves not_found
53
+ # for every plan and the cross-agent lease can never gate.
54
+ check "do-fleet claims the lease by slug, not a guessed tid" \
55
+ "grep -qF '\\\"slug\\\":\\\"\$slug\\\"' .claude/scripts/do-fleet.sh" \
56
+ "claim_task must post data.slug"
57
+ check "do-fleet no longer guesses tid=task:<slug>" \
58
+ "! grep -qF 'tid=\"task:\${1}\"' .claude/scripts/do-fleet.sh" \
59
+ "the guessed-tid form is back"
60
+
61
+ # 6. The fleet lease actually GATES. Every non-already-claimed response used to degrade
62
+ # to "ok to launch", so two runs could both think they owned the same task.
63
+ check "do-fleet distinguishes not_found from already-claimed" \
64
+ "grep -q 'error\":\"not_found' .claude/scripts/do-fleet.sh" \
65
+ "claim_task must branch on not_found separately from the generic error case"
66
+
67
+ # 7. /close routes the task close through a route that EXISTS. There is no
68
+ # /api/tasks/{id}/complete anywhere in one.ie/web/src/pages/api/.
69
+ check "/close has no dead /api/tasks/{id}/complete POST" \
70
+ "! grep -qE 'POST .(http://localhost:4321)?/api/tasks/\{id\}/complete' .claude/commands/close.md" \
71
+ "close.md still posts to a route that does not exist"
72
+ check "/close closes the task through tasks:status" \
73
+ "grep -q 'api/ask/tasks:status' .claude/commands/close.md" \
74
+ "close.md must use the tasks:status door"
75
+
76
+ if [ "$FAIL" -eq 0 ]; then
77
+ echo "[tasks-wire] OK — all wires connected"
78
+ else
79
+ echo "[tasks-wire] BROKEN — see ✗ above"
80
+ fi
81
+ exit "$FAIL"
@@ -6,12 +6,70 @@
6
6
  # Usage: do-tier.sh [--intent "text"] <path>... | git diff --name-only | do-tier.sh [--intent ...]
7
7
  set -euo pipefail
8
8
 
9
+ # --self-test — the tier fixtures, runnable as one command (same idiom as
10
+ # do-reconcile.sh / do-promise-settle.sh / do-promise-lint.sh). Exists so an
11
+ # acceptance check can be `do-tier.sh --self-test` instead of a fragile inline
12
+ # pipeline: do-tier now exits 3 on UNSIZED, and under `pipefail` that propagates
13
+ # through `… | grep` even when the grep matches.
14
+ if [ "${1:-}" = "--self-test" ]; then
15
+ S="$(cd "$(dirname "$0")" && pwd)"; f=0
16
+ chk() { # chk <expected-tier> <args...>
17
+ want="$1"; shift
18
+ got="$("$S/do-tier.sh" "$@" 2>/dev/null)" || true
19
+ case "$got" in *"\"tier\":\"$want\""*) printf ' ok %s → %s\n' "$*" "$want" ;;
20
+ *) printf ' FAIL %s → expected %s, got %s\n' "$*" "$want" "$got"; f=1 ;; esac
21
+ }
22
+ chk PATCH text/x.md
23
+ chk SCHEMA schema/one.tql
24
+ chk SCHEMA channels/migrations/0001_init.sql
25
+ chk SCHEMA pay/contracts/sui/sources/one_token.move
26
+ chk FIX pay/contracts/sui/tests/gateway_tests.move
27
+ chk FEATURE --intent "add billing" one.ie/web/src/api/b.ts
28
+ # Absence of recon must not read as simplicity — no paths → UNSIZED, never PATCH.
29
+ # Intent is ignored by design: "add a null check" and "add a settings page" share a verb.
30
+ # set +e around this: UNSIZED exits 3 BY CONTRACT, and this script runs under
31
+ # `set -e`, so the assignment below would abort the self-test before it asserted
32
+ # anything — silently "passing" a build with the guard removed (caught doing
33
+ # exactly that during authoring).
34
+ set +e
35
+ out="$(printf '' | "$S/do-tier.sh" --intent "add a settings page" 2>/dev/null)"; rc=$?
36
+ set -e
37
+ case "$out" in *'"tier":"UNSIZED"'*) printf ' ok no paths → UNSIZED (not PATCH)\n' ;;
38
+ *) printf ' FAIL no paths → expected UNSIZED, got %s\n' "$out"; f=1 ;; esac
39
+ [ "$rc" -eq 3 ] && printf ' ok UNSIZED → exit 3\n' || { printf ' FAIL UNSIZED exit: expected 3, got %s\n' "$rc"; f=1; }
40
+ [ "$f" -eq 0 ] && echo "do-tier: self-test green" || echo "do-tier: self-test FAILED"
41
+ exit "$f"
42
+ fi
43
+
9
44
  intent=""
10
45
  if [ "${1:-}" = "--intent" ]; then intent="$2"; shift 2; fi
11
46
 
12
47
  paths=()
13
48
  if [ "$#" -gt 0 ]; then paths=("$@"); else while IFS= read -r l; do [ -n "$l" ] && paths+=("$l"); done; fi
14
- [ "${#paths[@]}" -eq 0 ] && paths=("")
49
+
50
+ # UNSIZED — zero real paths. do-tier sizes a DIFF, never a sentence.
51
+ #
52
+ # The downward bias below ("an over-built PATCH is burnt") is right when the paths are
53
+ # known and dangerous when there are none: with no paths every input fell through to
54
+ # PATCH/TRIVIAL/5k, so an unrecon'd idea read as a typo. That silently mis-sized real
55
+ # work twice on record — text/learnings.md:485 ("correctly returned PATCH on a bare slug
56
+ # with no changes yet") and text/remote-suspend-todo.md:5 ("do-tier mis-sized as FIX").
57
+ # The caller at .claude/commands/do.md Step 1 passes $(git diff --name-only), which is
58
+ # empty on a clean tree — i.e. at AIM, every single time, before any edit exists.
59
+ #
60
+ # Absence of recon must not read as simplicity. Intent text alone can't decide either
61
+ # ("add a null check" and "add a settings page" share a verb), so UNSIZED ignores
62
+ # --intent by design: run recon, then size the paths it found.
63
+ # NB: guard the expansion — `"${paths[@]}"` on an empty array is an unbound-variable
64
+ # error under `set -u` on macOS's bash 3.2, not an empty list.
65
+ real=0
66
+ if [ "${#paths[@]}" -gt 0 ]; then
67
+ for p in "${paths[@]}"; do [ -n "$p" ] && real=$((real + 1)); done
68
+ fi
69
+ if [ "$real" -eq 0 ]; then
70
+ printf '{"tier":"UNSIZED","spine":"recon","classifier":"UNKNOWN","ceiling_tokens":0,"w2_model":"none"}\n'
71
+ exit 3
72
+ fi
15
73
 
16
74
  n=${#paths[@]}
17
75
  schema=false; code=false; doconly=true
@@ -46,11 +104,38 @@ fi
46
104
  # still override per-cycle.
47
105
  W2_SCHEMA=opus
48
106
  [ "${FABLE_AVAILABLE:-off}" = "on" ] && W2_SCHEMA=fable
107
+ # spine — the pruned stop list, in the tokens do.md Step 1 has always used. These are
108
+ # the SAME stops as do.md's uppercase spine block (§ "The pruned spine by tier"), in
109
+ # their original lowercase names: spec=DESIGN · todo=PLAN · code=BUILD · tests=TEST ·
110
+ # proof=PROVE · teach=TEACH · release=SHIP. At genesis (ff72af398, 2026-06-04) do.md
111
+ # printed this exact lowercase list; 6b5b89f04 renamed the doc's tokens and 98c20f586
112
+ # (2026-06-18) reordered them docs-first, and neither change reached here — so the
113
+ # strings below sat two commits behind canon from 2026-06-18. Realigned 2026-08-02.
114
+ #
115
+ # What the drift actually was (the trailing `docs` was NOT the bug):
116
+ # - `proof docs release` was already canon — that `docs` is TEACH (reconcile the docs
117
+ # against shipped reality), which correctly follows PROVE. It is renamed to `teach`
118
+ # below only so the one pre-BUILD `docs` stop can own the word `docs` unambiguously.
119
+ # - The real defect was an OMISSION: there was no pre-BUILD `docs` stop on any tier,
120
+ # so the deterministic spine never scheduled the doc set that .claude/rules/
121
+ # documentation.md makes the spec ("docs are the spec, written FIRST"). A conductor
122
+ # reading this spine walked straight to `code`.
123
+ # - FEATURE/SCHEMA also still emitted `code tests` where canon is TEST → BUILD: the
124
+ # tests are drawn FROM the docs, before the code exists.
125
+ # - FIX had neither a `docs` stop nor a `learn` stop that do.md:222 gives it.
126
+ # The `docs` insert + `teach` rename are prescribed verbatim by text/do-evolve.md § 8.
127
+ # `verify` and `learn` go beyond that string — they come straight from do.md:222-223,
128
+ # which lists VERIFY (FEATURE/SCHEMA, between BUILD and PROVE) and LEARN (all but PATCH).
129
+ # Noted because do-evolve § 8 is a live design doc: the delta from its string is deliberate.
130
+ #
131
+ # Deliberately absent: `aim` (AIM is the stop that RUNS do-tier — emitting it here would
132
+ # be the output naming its own caller) and `investigate` (do.md:231 — it rides any tier
133
+ # on a condition, so it is not tier-pruned). `reconcile` stays SCHEMA-only per do.md:224.
49
134
  case "$tier" in
50
- PATCH) spine="code verify"; cls=TRIVIAL; ceil=5000; w2=opus ;;
51
- FIX) spine="survey code tests proof"; cls=SIMPLE; ceil=30000; w2=opus ;;
52
- FEATURE) spine="promise survey spec clarify todo analyze code tests proof docs release"; cls=COMPLEX; ceil=150000; w2=opus ;;
53
- SCHEMA) spine="promise survey spec reconcile clarify todo analyze code tests proof docs release"; cls=COMPLEX; ceil=200000; w2="$W2_SCHEMA" ;;
135
+ PATCH) spine="code verify"; cls=TRIVIAL; ceil=5000; w2=opus ;;
136
+ FIX) spine="survey docs code tests proof learn"; cls=SIMPLE; ceil=30000; w2=opus ;;
137
+ FEATURE) spine="promise survey spec clarify docs todo analyze tests code verify proof teach release learn"; cls=COMPLEX; ceil=150000; w2=opus ;;
138
+ SCHEMA) spine="promise survey spec reconcile clarify docs todo analyze tests code verify proof teach release learn"; cls=COMPLEX; ceil=200000; w2="$W2_SCHEMA" ;;
54
139
  esac
55
140
 
56
141
  printf '{"tier":"%s","spine":"%s","classifier":"%s","ceiling_tokens":%s,"w2_model":"%s"}\n' "$tier" "$spine" "$cls" "$ceil" "$w2"