opensquid 0.5.412 → 0.5.432

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 (143) hide show
  1. package/dist/channels/env-token.js +2 -2
  2. package/dist/channels/env-token.js.map +1 -1
  3. package/dist/cli.js +4 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/functions/event.d.ts.map +1 -1
  6. package/dist/functions/event.js +30 -0
  7. package/dist/functions/event.js.map +1 -1
  8. package/dist/functions/index.d.ts +2 -0
  9. package/dist/functions/index.d.ts.map +1 -1
  10. package/dist/functions/index.js +2 -0
  11. package/dist/functions/index.js.map +1 -1
  12. package/dist/functions/read_rubric.d.ts +27 -0
  13. package/dist/functions/read_rubric.d.ts.map +1 -0
  14. package/dist/functions/read_rubric.js +53 -0
  15. package/dist/functions/read_rubric.js.map +1 -0
  16. package/dist/functions/rubric_pre_inject.d.ts +22 -0
  17. package/dist/functions/rubric_pre_inject.d.ts.map +1 -0
  18. package/dist/functions/rubric_pre_inject.js +58 -0
  19. package/dist/functions/rubric_pre_inject.js.map +1 -0
  20. package/dist/functions/session_status_manifest.d.ts.map +1 -1
  21. package/dist/functions/session_status_manifest.js +15 -0
  22. package/dist/functions/session_status_manifest.js.map +1 -1
  23. package/dist/functions/shell_parse.d.ts +41 -0
  24. package/dist/functions/shell_parse.d.ts.map +1 -0
  25. package/dist/functions/shell_parse.js +185 -0
  26. package/dist/functions/shell_parse.js.map +1 -0
  27. package/dist/mcp/server.js +14 -1
  28. package/dist/mcp/server.js.map +1 -1
  29. package/dist/mcp/tools/memorize.d.ts +3 -0
  30. package/dist/mcp/tools/memorize.d.ts.map +1 -1
  31. package/dist/mcp/tools/memorize.js +8 -0
  32. package/dist/mcp/tools/memorize.js.map +1 -1
  33. package/dist/mcp/tools/ralph.d.ts +21 -0
  34. package/dist/mcp/tools/ralph.d.ts.map +1 -0
  35. package/dist/mcp/tools/ralph.js +18 -0
  36. package/dist/mcp/tools/ralph.js.map +1 -0
  37. package/dist/mcp/tools/workgraph.d.ts +11 -0
  38. package/dist/mcp/tools/workgraph.d.ts.map +1 -1
  39. package/dist/mcp/tools/workgraph.js +7 -0
  40. package/dist/mcp/tools/workgraph.js.map +1 -1
  41. package/dist/rag/backends/libsql_store.d.ts.map +1 -1
  42. package/dist/rag/backends/libsql_store.js +13 -5
  43. package/dist/rag/backends/libsql_store.js.map +1 -1
  44. package/dist/rag/backends/perfile_source.d.ts.map +1 -1
  45. package/dist/rag/backends/perfile_source.js +6 -0
  46. package/dist/rag/backends/perfile_source.js.map +1 -1
  47. package/dist/rag/durability.d.ts +26 -0
  48. package/dist/rag/durability.d.ts.map +1 -0
  49. package/dist/rag/durability.js +34 -0
  50. package/dist/rag/durability.js.map +1 -0
  51. package/dist/rag/memory/store.d.ts.map +1 -1
  52. package/dist/rag/memory/store.js +11 -3
  53. package/dist/rag/memory/store.js.map +1 -1
  54. package/dist/rag/types.d.ts +3 -0
  55. package/dist/rag/types.d.ts.map +1 -1
  56. package/dist/rag/types.js.map +1 -1
  57. package/dist/runtime/agent_bridge/daemon.d.ts.map +1 -1
  58. package/dist/runtime/agent_bridge/daemon.js +9 -6
  59. package/dist/runtime/agent_bridge/daemon.js.map +1 -1
  60. package/dist/runtime/bootstrap.d.ts.map +1 -1
  61. package/dist/runtime/bootstrap.js +4 -0
  62. package/dist/runtime/bootstrap.js.map +1 -1
  63. package/dist/runtime/ralph/decision_classifier.d.ts +18 -0
  64. package/dist/runtime/ralph/decision_classifier.d.ts.map +1 -0
  65. package/dist/runtime/ralph/decision_classifier.js +72 -0
  66. package/dist/runtime/ralph/decision_classifier.js.map +1 -0
  67. package/dist/runtime/ralph/escalate_lap.d.ts +31 -0
  68. package/dist/runtime/ralph/escalate_lap.d.ts.map +1 -0
  69. package/dist/runtime/ralph/escalate_lap.js +22 -0
  70. package/dist/runtime/ralph/escalate_lap.js.map +1 -0
  71. package/dist/runtime/ralph/escalator.d.ts +34 -0
  72. package/dist/runtime/ralph/escalator.d.ts.map +1 -0
  73. package/dist/runtime/ralph/escalator.js +19 -0
  74. package/dist/runtime/ralph/escalator.js.map +1 -0
  75. package/dist/runtime/ralph/lap_outcome.d.ts +43 -0
  76. package/dist/runtime/ralph/lap_outcome.d.ts.map +1 -0
  77. package/dist/runtime/ralph/lap_outcome.js +119 -0
  78. package/dist/runtime/ralph/lap_outcome.js.map +1 -0
  79. package/dist/runtime/ralph/orchestrator.d.ts +71 -0
  80. package/dist/runtime/ralph/orchestrator.d.ts.map +1 -0
  81. package/dist/runtime/ralph/orchestrator.js +74 -0
  82. package/dist/runtime/ralph/orchestrator.js.map +1 -0
  83. package/dist/runtime/ralph/ralph_template.d.ts +18 -0
  84. package/dist/runtime/ralph/ralph_template.d.ts.map +1 -0
  85. package/dist/runtime/ralph/ralph_template.js +61 -0
  86. package/dist/runtime/ralph/ralph_template.js.map +1 -0
  87. package/dist/runtime/ralph/supervisor.d.ts +30 -0
  88. package/dist/runtime/ralph/supervisor.d.ts.map +1 -0
  89. package/dist/runtime/ralph/supervisor.js +24 -0
  90. package/dist/runtime/ralph/supervisor.js.map +1 -0
  91. package/dist/setup/cli/chat_actions.js +1 -1
  92. package/dist/setup/cli/chat_actions.js.map +1 -1
  93. package/dist/setup/cli/chat_actions_prompts_alias.js +4 -1
  94. package/dist/setup/cli/chat_actions_prompts_alias.js.map +1 -1
  95. package/dist/setup/cli/chat_actions_test_step.js +2 -2
  96. package/dist/setup/cli/chat_actions_test_step.js.map +1 -1
  97. package/dist/setup/cli/chat_state.d.ts.map +1 -1
  98. package/dist/setup/cli/chat_state.js +3 -2
  99. package/dist/setup/cli/chat_state.js.map +1 -1
  100. package/dist/setup/cli/mcp.d.ts +1 -1
  101. package/dist/setup/cli/mcp.d.ts.map +1 -1
  102. package/dist/setup/cli/mcp.js +7 -8
  103. package/dist/setup/cli/mcp.js.map +1 -1
  104. package/dist/setup/cli/ralph.d.ts +20 -0
  105. package/dist/setup/cli/ralph.d.ts.map +1 -0
  106. package/dist/setup/cli/ralph.js +180 -0
  107. package/dist/setup/cli/ralph.js.map +1 -0
  108. package/dist/setup/wizard/mcp-writer.d.ts +3 -3
  109. package/dist/setup/wizard/mcp-writer.d.ts.map +1 -1
  110. package/dist/setup/wizard/mcp-writer.js +17 -0
  111. package/dist/setup/wizard/mcp-writer.js.map +1 -1
  112. package/dist/setup/wizard/ralph_writer.d.ts +98 -0
  113. package/dist/setup/wizard/ralph_writer.d.ts.map +1 -0
  114. package/dist/setup/wizard/ralph_writer.js +114 -0
  115. package/dist/setup/wizard/ralph_writer.js.map +1 -0
  116. package/dist/workgraph/audience.d.ts +12 -0
  117. package/dist/workgraph/audience.d.ts.map +1 -0
  118. package/dist/workgraph/audience.js +10 -0
  119. package/dist/workgraph/audience.js.map +1 -0
  120. package/dist/workgraph/events.d.ts.map +1 -1
  121. package/dist/workgraph/events.js +34 -0
  122. package/dist/workgraph/events.js.map +1 -1
  123. package/dist/workgraph/store.d.ts.map +1 -1
  124. package/dist/workgraph/store.js +86 -12
  125. package/dist/workgraph/store.js.map +1 -1
  126. package/dist/workgraph/types.d.ts +33 -2
  127. package/dist/workgraph/types.d.ts.map +1 -1
  128. package/docs/rubric/author.md +17 -0
  129. package/docs/rubric/scope.md +16 -0
  130. package/package.json +4 -3
  131. package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +9 -0
  132. package/packs/builtin/coding-flow/skills/execute-gate/skill.yaml +19 -12
  133. package/packs/builtin/coding-flow/skills/scope-lifecycle/skill.yaml +67 -50
  134. package/packs/builtin/default-discipline/manifest.yaml +6 -3
  135. package/packs/builtin/default-discipline/skills/workflow/skill.yaml +7 -3
  136. package/packs/builtin/pack-architect/SKILL.md +10 -0
  137. package/packs/builtin/pack-architect/skills/skill-yaml-author-walkthrough/skill.yaml +7 -0
  138. package/packs/builtin/scope-architect/team.yaml +5 -2
  139. package/schemas/manifest.schema.json +45 -203
  140. package/schemas/models.schema.json +3 -13
  141. package/schemas/notifications.schema.json +6 -24
  142. package/schemas/skill.schema.json +33 -120
  143. package/schemas/team.schema.json +46 -0
@@ -89,6 +89,15 @@ rules:
89
89
  args:
90
90
  key: coding-flow-track
91
91
  value: trivial
92
+ # TR.B (wg-2d1d8698f563): deliver the coding-flow quality rubric to the agent BEFORE it authors — the
93
+ # fix for the blind-first-draft root cause. Placed AFTER enter-scoping so the cold kickoff turn's
94
+ # just-armed `scoping` is visible (rules walk file-order; handoff-rescope-nudge below relies on the same
95
+ # ordering). rubric_pre_inject reads the coding-flow FSM and, when in an active SCOPE/AUTHOR phase, returns
96
+ # an inject_context payload carrying the FULL rubric (scope + author — prompt_submit can't track mid-turn
97
+ # phase advances, so one injection must cover the whole uninterrupted turn). Inject-only; never blocks.
98
+ - id: inject-rubric
99
+ process:
100
+ - call: rubric_pre_inject
92
101
  # GF.1 (F9) — the re-scope NUDGE, surfaced on prompt_submit (where directives are
93
102
  # delivered) for the `scoping` state. Fires after `enter-scoping` advanced idle→scoping
94
103
  # (or after task-start's task_unscoped reset, or GF.7's phases_complete re-arm landed
@@ -15,18 +15,25 @@ triggers:
15
15
  rules:
16
16
  - id: phase-logged-before-commit
17
17
  process:
18
- - call: match_command
18
+ # T-GATE-MATCHER-SUBSTRING (GM.3, wg-52e57e2ed252): was a raw-string `match_command`
19
+ # regex `\bgit…commit\b` over the whole command — it false-fired on `git commit` inside a
20
+ # grep pattern, an echo arg, or a quoted subprocess prompt (`claude -p "…git commit…"`).
21
+ # `command_invokes` parses the command into real shell segments and matches only a genuine
22
+ # `git commit` INVOCATION (compounds like `cd x && git commit` still match; the owned git
23
+ # pre-commit hook stays the hard boundary). Recall-over-precision band-aid retired.
24
+ - call: command_invokes
19
25
  args:
20
- # T-FLOW-UNSKIPPABLE FU.1 (the real root cause): this was `^git…commit`, anchored
21
- # at START — so a COMPOUND command `cd <dir> && git commit …` (how every commit is
22
- # actually made, since the Bash tool resets cwd) starts with `cd` and EVADED the
23
- # gate entirely. The gate was dead for real commits. Match `git … commit` ANYWHERE
24
- # (`\b`, like the sibling no-verify matcher) so compounds can't slip past. A false
25
- # match on a string that merely mentions `git commit` is the safe direction (recall
26
- # over precision, same as no-verify-block).
27
- pattern: '\bgit\s+(?:-[cC]\s+\S+\s+)*commit\b'
28
- target: tool_args.command
26
+ program: git
27
+ subcommand: commit
29
28
  as: committing
29
+ # T-commit-nudge-docsonly-parity (wg-3dcca3b29ed1): a docs-only commit the git-owned
30
+ # HARD gate ALLOWS (gate.ts isDocsOnly) must not be over-blocked by this in-session gate
31
+ # either — predicate parity, the SAME `staged_docs_only` primitive the default-discipline
32
+ # nudge uses. Reads the staged diff (fixed-argv git); fails toward `false` (= not docs-only
33
+ # = the gate still fires) on any error, so it can never falsely SUPPRESS a real code block.
34
+ - call: staged_docs_only
35
+ if: 'committing'
36
+ as: docs_only
30
37
  # T-FLOW-UNSKIPPABLE FU.1 (D1): the gates must COMPOSE. The "no active task ⇒
31
38
  # ad-hoc ⇒ allow" branch below was a SEAM — a blocked TaskCreate (AUTHOR gate)
32
39
  # leaves NO active task, so its code leaked out as an "ad-hoc" commit. Close it:
@@ -39,7 +46,7 @@ rules:
39
46
  if: 'committing'
40
47
  as: st
41
48
  - call: verdict
42
- if: 'committing && (st == "scoping" || st == "researching" || st == "researched" || st == "spec_authored")'
49
+ if: 'committing && !docs_only && (st == "scoping" || st == "researching" || st == "researched" || st == "spec_authored")'
43
50
  args:
44
51
  level: block
45
52
  message: >-
@@ -54,7 +61,7 @@ rules:
54
61
  if: 'committing && active.present == true'
55
62
  as: phases
56
63
  - call: verdict
57
- if: 'committing && active.present == true && phases.complete == false'
64
+ if: 'committing && active.present == true && phases.complete == false && !docs_only'
58
65
  args:
59
66
  level: block
60
67
  message: >-
@@ -43,7 +43,9 @@ rules:
43
43
  # checks run BEFORE the advance, and research_done is COUPLED to them — no
44
44
  # advisory pass. The advance fires ONLY when SCOPE content is complete:
45
45
  # GUESS_FREE (every claim cited-or-flagged + the BEST/simplest solution justified)
46
- # AND zero unresolved OPEN QUESTION markers AND research depth >= 3 this turn.
46
+ # AND zero unresolved open-question markers (an unchecked `- [ ] OPEN QUESTION:` task-list
47
+ # item — GM.3 made this a STRUCTURED marker so prose that merely mentions the phrase no
48
+ # longer false-blocks) AND research depth >= 3 this turn.
47
49
  - id: scope-advance
48
50
  process:
49
51
  - call: tool_name
@@ -94,8 +96,23 @@ rules:
94
96
  # latency (268s worst observed under subscription contention, 2026-06-10)
95
97
  # and 20s under the 360s outer hook cap the setup wizard writes — the
96
98
  # inner timer must die first so the gate fails with a readable error.
97
- - call: cached_audit
99
+ # TR.A (wg-2d1d8698f563): the rubric is single-sourced to docs/rubric/scope.md and interpolated as
100
+ # {{rubric}} below — NOT hardcoded here. read_rubric returns null on a missing/unreadable fragment.
101
+ - call: read_rubric
98
102
  if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-")'
103
+ args: { name: scope }
104
+ as: rubric
105
+ # FAIL-LOUD: never run the audit rubric-less (a rubric-less verdict would be a silent fallback worse
106
+ # than fail-open). docs/rubric/scope.md MUST ship (package.json files[]).
107
+ - call: verdict
108
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && rubric == null'
109
+ args:
110
+ level: block
111
+ message: >-
112
+ SCOPE audit cannot run: the canonical rubric (docs/rubric/scope.md) is unreadable — a
113
+ packaging/install fault, NOT a content failure. Verify the opensquid package ships docs/rubric/.
114
+ - call: cached_audit
115
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && rubric != null'
99
116
  on_error: continue
100
117
  args:
101
118
  cache_key: coding-flow-guess-audit-cache
@@ -103,16 +120,8 @@ rules:
103
120
  timeout_ms: 340000
104
121
  prompt: >-
105
122
  You are an adversarial reviewer enforcing the lexicon's Research/Audit flow
106
- principles (docs/lexicon.md never-guess, best-solution, full-fix-over-patch, teach-back depth) on a
107
- pre-research / scope artifact. (1) NEVER-GUESS: a claim is acceptable ONLY if DERIVED from cited
108
- evidence (a file:line, a memory, or the user's own words) OR explicitly flagged
109
- as an OPEN QUESTION to ask the user. (2) BEST-SOLUTION: the artifact must show
110
- the best solution was found — alternatives weighed against the criteria and the
111
- SIMPLEST correct one chosen per docs/lexicon.md (no proliferating special-cases).
112
- (3) FULL-FIX: the chosen solution must be the FULL fix — when the existing shape
113
- is the cause, it is re-architected, NOT a local patch that bolts on a special-case
114
- to dodge the rework (that patch is itself the proliferating-special-case
115
- overcomplication the Full-fix-over-patch guideline forbids).
123
+ principles on a pre-research / scope artifact. Apply EXACTLY this rubric (the single
124
+ canonical source, docs/rubric/scope.md):\n\n{{rubric}}\n\n
116
125
  Begin your response with EXACTLY one line — `VERDICT: GUESS_FREE` ONLY if every
117
126
  claim is cited-or-flagged-open AND the simplest-correct solution is justified
118
127
  against alternatives AND is a full fix, not a patch around a design that should be
@@ -124,18 +133,20 @@ rules:
124
133
  # audit-string trichotomy below is a clean partition over its own subspace.
125
134
  # BLOCK: unresolved open questions — answer them in SCOPE (this is the interactive phase).
126
135
  - call: verdict
127
- if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && contains(effective, "OPEN QUESTION")'
136
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && match(effective, "(?im)^\\s*-\\s*\\[ \\]\\s*open[ _-]?question")'
128
137
  args:
129
138
  level: block
130
139
  message: >-
131
- SCOPE incomplete: the pre-research still has unresolved OPEN QUESTION(s).
132
- SCOPE is the interactive phase ASK the user (AskUserQuestion), record + cite
133
- the answer, and remove the marker BEFORE authoring. Everything after scope is
134
- automated; a question must not survive into it.
140
+ SCOPE incomplete: the pre-research still has an unresolved open-question marker
141
+ (an unchecked `- [ ] OPEN QUESTION: …` task-list item). SCOPE is the interactive
142
+ phase — ASK the user (AskUserQuestion), record + cite the answer, then CHECK THE BOX
143
+ (`- [x]`) or remove the item BEFORE authoring. Everything after scope is automated;
144
+ a question must not survive into it. (Merely MENTIONING the phrase in prose is fine —
145
+ only an unchecked checkbox marker blocks.)
135
146
  # BLOCK: shallow research (folded DPC.5 depth). Ordered after open-question; its
136
- # !OPEN QUESTION guard keeps the two preconditions mutually exclusive.
147
+ # !match(OQ-marker) guard keeps the two preconditions mutually exclusive.
137
148
  - call: verdict
138
- if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && !contains(effective, "OPEN QUESTION") && depth.count < 3'
149
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && !match(effective, "(?im)^\\s*-\\s*\\[ \\]\\s*open[ _-]?question") && depth.count < 3'
139
150
  args:
140
151
  level: block
141
152
  message: >-
@@ -145,17 +156,17 @@ rules:
145
156
  # {GUESS_FREE, UNRESOLVED, no-VERDICT} is a total partition of the audit string.
146
157
  # PASS → advance.
147
158
  - call: advance_fsm
148
- if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !contains(effective, "OPEN QUESTION") && contains(audit, "VERDICT: GUESS_FREE")'
159
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !match(effective, "(?im)^\\s*-\\s*\\[ \\]\\s*open[ _-]?question") && contains(audit, "VERDICT: GUESS_FREE")'
149
160
  args:
150
161
  event: research_done
151
162
  # CONTENT-FAIL → loop-back + warn (keyed POSITIVELY on UNRESOLVED, not !GUESS_FREE — so an
152
163
  # audit that could not run no longer masquerades as "guesses found").
153
164
  - call: advance_fsm
154
- if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !contains(effective, "OPEN QUESTION") && contains(audit, "VERDICT: UNRESOLVED")'
165
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !match(effective, "(?im)^\\s*-\\s*\\[ \\]\\s*open[ _-]?question") && contains(audit, "VERDICT: UNRESOLVED")'
155
166
  args:
156
167
  event: guess_found
157
168
  - call: verdict
158
- if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !contains(effective, "OPEN QUESTION") && contains(audit, "VERDICT: UNRESOLVED")'
169
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !match(effective, "(?im)^\\s*-\\s*\\[ \\]\\s*open[ _-]?question") && contains(audit, "VERDICT: UNRESOLVED")'
159
170
  args:
160
171
  level: warn
161
172
  message: >-
@@ -166,7 +177,7 @@ rules:
166
177
  # AUDIT-UNAVAILABLE → terminal BLOCK, NO advance (stays scoping, re-armable). F0c fix:
167
178
  # the audit subagent could not spawn (a no-VERDICT response, incl. the bound err message).
168
179
  - call: verdict
169
- if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !contains(effective, "OPEN QUESTION") && !contains(audit, "VERDICT:")'
180
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/research/") && contains(targs.file_path, "-pre-research-") && depth.count >= 3 && !match(effective, "(?im)^\\s*-\\s*\\[ \\]\\s*open[ _-]?question") && rubric != null && !contains(audit, "VERDICT:")'
170
181
  args:
171
182
  level: block
172
183
  message: >-
@@ -214,25 +225,29 @@ rules:
214
225
  # SCOPE audit — a re-fire on an UNCHANGED spec reuses the verdict, no spawn.
215
226
  # timeout_ms (T-AUDIT-SPAWN-FIX): 340s, same measured sizing as the SCOPE
216
227
  # audit above (268s worst observed; 20s under the 360s outer hook cap).
217
- - call: cached_audit
228
+ # TR.A (wg-2d1d8698f563): the AUTHOR rubric is single-sourced to docs/rubric/author.md,
229
+ # interpolated as {{rubric}} below — NOT hardcoded. Fail-loud on a missing fragment (below).
230
+ - call: read_rubric
218
231
  if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/tasks/T-") && endsWith(targs.file_path, ".md")'
232
+ args: { name: author }
233
+ as: rubric
234
+ - call: verdict
235
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/tasks/T-") && endsWith(targs.file_path, ".md") && rubric == null'
236
+ args:
237
+ level: block
238
+ message: >-
239
+ AUTHOR audit cannot run: the canonical rubric (docs/rubric/author.md) is unreadable — a
240
+ packaging/install fault, NOT a content failure. Verify the opensquid package ships docs/rubric/.
241
+ - call: cached_audit
242
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/tasks/T-") && endsWith(targs.file_path, ".md") && rubric != null'
219
243
  on_error: continue
220
244
  args:
221
245
  cache_key: coding-flow-spec-audit-cache
222
246
  model: reasoning
223
247
  timeout_ms: 340000
224
248
  prompt: >-
225
- You are an adversarial reviewer enforcing THREE author-completeness rules.
226
- (1) 11-FIELD CONTRACT: EVERY "### Task" block has all 11 fields (Required
227
- skills, Deliverable, Depends on, Files affected, Key code shapes, Test
228
- fixtures, Acceptance criteria, Risk callouts, References, Verification
229
- commands, 7-phase steps), every Key-code-shapes block is REAL code (not
230
- pseudocode), every 7-phase step names concrete files/decisions. (2) 100%
231
- DESIGN COVERAGE: the task set covers EVERY element of the SCOPE design below —
232
- no design item is left without a task. (3) SIMPLICITY (per docs/lexicon.md):
233
- the design is the SIMPLEST correct solution — no proliferating special-cases
234
- (that signals a missed decomposition), every lifecycle an explicit
235
- total-transition FSM, every transform a pure function, no implicit state.
249
+ You are an adversarial reviewer enforcing THREE author-completeness rules. Apply EXACTLY
250
+ this rubric (the single canonical source, docs/rubric/author.md):\n\n{{rubric}}\n\n
236
251
  Begin your response with EXACTLY one line — `VERDICT: SPEC_COMPLETE` ONLY if
237
252
  ALL THREE hold; otherwise `VERDICT: INCOMPLETE` + one bullet per offending
238
253
  block, uncovered design element, OR over-complected smell. Never say
@@ -260,7 +275,7 @@ rules:
260
275
  Audit:\n\n{{spec_audit}}
261
276
  # AUDIT-UNAVAILABLE → BLOCK, no advance (stays spec_authored, re-armable). F0c fix.
262
277
  - call: verdict
263
- if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/tasks/T-") && endsWith(targs.file_path, ".md") && !contains(spec_audit, "VERDICT:")'
278
+ if: '(tool == "Write" || tool == "Edit") && contains(targs.file_path, "docs/tasks/T-") && endsWith(targs.file_path, ".md") && rubric != null && !contains(spec_audit, "VERDICT:")'
264
279
  args:
265
280
  level: block
266
281
  message: >-
@@ -303,25 +318,27 @@ rules:
303
318
  code commit/push that has not completed the flow.)
304
319
  # ── verify-skip detector: the ONE closed opt-out token (GF.3) ────────────────
305
320
  # The git gate (GF.2) is bypassable only by passing the verify-skipping flag to `git
306
- # commit/push` (or `git commit -n`) — a single explicit opt-out (not a denylist over an
307
- # open set), so it is a hard BLOCK. `git push -n` is --dry-run, NOT a verify-skip, so -n
308
- # is matched for `commit` only. KNOWN LIMITATION (matching a shell string is imperfect):
309
- # the flag literal appearing inside a `-m "…"` commit MESSAGE also matches → over-block.
310
- # That direction is INTENTIONAL and safe for a bypass-detector: a false positive (an
311
- # annoying block on a message that mentions the flag) beats a false negative (letting a
312
- # real bypass through). Do NOT "fix" this by anchoring to flag position that
313
- # reintroduces a false negative on `git commit -m msg <flag>`.
321
+ # commit/push` — a single explicit opt-out (not a denylist over an open set), so it is a
322
+ # hard BLOCK. `git push -n` is --dry-run, NOT a verify-skip, so `-n` is matched for `commit`
323
+ # ONLY (git-commit(1) `-n,--no-verify`; git-push(1) `-n,--dry-run`).
324
+ # T-GATE-MATCHER-SUBSTRING (GM.3, wg-52e57e2ed252): was a raw-string `match_command` that
325
+ # false-fired on a read-only `grep -n "…git commit…"` (the prose `git commit` + grep's `-n`).
326
+ # `command_invokes` parses real shell segments and detects the flag only WITHIN a genuine git
327
+ # invocation so a real `git commit --no-verify`/`-n` still blocks (recall preserved within the
328
+ # invocation), but prose/grep/echo no longer trip it. The old "over-block is intentional"
329
+ # band-aid is retired: blocking read-only work gave zero bypass-protection value.
314
330
  - id: no-verify-block
315
331
  process:
316
332
  - call: tool_name
317
333
  as: tool
318
- - call: match_command
319
- args:
320
- pattern: '(?:\bgit\s+commit\b.*(?:--no-verify|\s-n\b))|(?:\bgit\s+push\b.*--no-verify)'
321
- target: tool_args.command
322
- as: skipping
334
+ - call: command_invokes
335
+ args: { program: git, subcommand: commit, flag_any: ['--no-verify', '-n'] }
336
+ as: skip_commit
337
+ - call: command_invokes
338
+ args: { program: git, subcommand: push, flag_any: ['--no-verify'] }
339
+ as: skip_push
323
340
  - call: verdict
324
- if: '(tool == "Bash") && skipping'
341
+ if: '(tool == "Bash") && (skip_commit || skip_push)'
325
342
  args:
326
343
  level: block
327
344
  message: >-
@@ -34,13 +34,16 @@ evolves: true
34
34
  # command-boundary prefixes survive).
35
35
  guards:
36
36
  # git hygiene (block_tool)
37
+ # T-GATE-MATCHER-SUBSTRING (GM.3, wg-52e57e2ed252): structural — matches a real
38
+ # `git commit --amend` invocation, not the substring inside a grep/echo/prompt.
37
39
  - name: never-amend
38
40
  on: tool_call
39
41
  detect:
40
- call: match_command
42
+ call: command_invokes
41
43
  args:
42
- pattern: '(?:^|[;&|\n(])\s*git\s+(?:-[cC]\s+\S+\s+)*commit\b[^\n]*\s--amend\b'
43
- target: tool_args.command
44
+ program: git
45
+ subcommand: commit
46
+ flag_any: ['--amend']
44
47
  when: hit
45
48
  level: block
46
49
  message: >-
@@ -34,10 +34,14 @@ rules:
34
34
 
35
35
  - id: phase-logged-before-commit
36
36
  process:
37
- - call: match_command
37
+ # T-GATE-MATCHER-SUBSTRING (GM.3, wg-52e57e2ed252): was a raw-string regex over the whole
38
+ # command (boundary-anchored, but still matched `; git commit` inside a quoted echo/prompt).
39
+ # `command_invokes` parses real shell segments → matches only a genuine `git commit`
40
+ # invocation (compounds like `cd … && git commit` still match).
41
+ - call: command_invokes
38
42
  args:
39
- pattern: '(?:^|[;&|\n(])\s*git\s+(?:-[cC]\s+\S+\s+)*commit\b' # FU.14: command-boundary (start or after ; && || | newline ( ) — catches `cd … && git commit`
40
- target: tool_args.command
43
+ program: git
44
+ subcommand: commit
41
45
  as: committing
42
46
  - call: read_state
43
47
  if: committing
@@ -35,6 +35,16 @@ but the chain-handoff stalls.
35
35
  | `skill-yaml-author-walkthrough` | PreToolUse Edit/Write of `packs/*/skills/*/skill.yaml` | Surface verdict with skill-field checklist (cites pack-runtime.md §2 + skill-grammar-guide.md) |
36
36
  | `fsm-author-walkthrough` | PreToolUse Edit/Write of `packs/*/fsm.yaml` | Surface verdict with FSM checklist (cites pack-fsm-architecture.md; coding-flow example) |
37
37
 
38
+ ### Discipline: gates teach their rubric (TR.C, wg-2d1d8698f563)
39
+
40
+ A gate is not just a blocker — it must TEACH its bar. If a skill emits a block/audit verdict with a content
41
+ rubric (like coding-flow's guess/spec audits), it must ship a companion that DELIVERS that rubric to the
42
+ agent BEFORE the gated action, sourced from the gate's single canonical rubric (e.g. `read_rubric` →
43
+ `inject_context` at `prompt_submit`). Otherwise the agent authors content-blind and learns the bar only by
44
+ tripping the gate. Light block/warn gates (no heavy content rubric) teach via a remedy-naming message
45
+ instead. The `skill-yaml-author-walkthrough` checklist enforces this; the coding-flow pairing is the
46
+ reference implementation.
47
+
38
48
  ## The 4-phase pack-authoring workflow (when spawned as profession)
39
49
 
40
50
  1. **Identify scope + persona** — kind/usage decisions, detected_by
@@ -45,6 +45,13 @@ rules:
45
45
  functions per skill-grammar-guide.md
46
46
  [ ] Final step emits a verdict
47
47
  (pass / block / warn / surface / directive)
48
+ [ ] GATES TEACH (TR.C, wg-2d1d8698f563): if a rule emits a
49
+ block/audit verdict (a GATE with a content rubric), ship a
50
+ companion that DELIVERS that gate's rubric to the agent BEFORE
51
+ the gated action, sourced from the gate's canonical rubric
52
+ (e.g. read_rubric → inject_context at prompt_submit). A gate
53
+ teaches its bar; it does not only block. (Light block/warn
54
+ gates teach via a remedy-naming message instead.)
48
55
  [ ] For directive verdict: next_action is skill XOR tool
49
56
  XOR profession (exactly one)
50
57
  [ ] No vendor model names anywhere (use model_alias)
@@ -61,5 +61,8 @@ roles:
61
61
  weighed against the criteria with the SIMPLEST-correct chosen;
62
62
  failure-modes / inversion; an empirical-spike note; a scope boundary;
63
63
  and EVERY claim cited (file:line / memory / the user's words) or
64
- flagged OPEN QUESTION and an OPEN QUESTION must be resolved with the
65
- user IN SCOPE before the write, never carried into the artifact.
64
+ flagged as an unchecked `- [ ] OPEN QUESTION: …` task-list marker and
65
+ such a marker must be resolved with the user IN SCOPE before the write
66
+ (check the box `- [x]` once answered), never carried unresolved into the
67
+ artifact. (Merely discussing an open question in prose is fine; only an
68
+ unchecked checkbox marker blocks the advance.)