@cratylus/canon 0.2.0 → 0.4.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 (97) hide show
  1. package/dist/agents/architect.js +14 -108
  2. package/dist/agents/architect.js.map +1 -1
  3. package/dist/agents/assayer.d.ts +2 -0
  4. package/dist/agents/assayer.js +20 -0
  5. package/dist/agents/assayer.js.map +1 -0
  6. package/dist/agents/implementer.js +9 -69
  7. package/dist/agents/implementer.js.map +1 -1
  8. package/dist/agents/kino.js +24 -110
  9. package/dist/agents/kino.js.map +1 -1
  10. package/dist/agents/mav.js +43 -79
  11. package/dist/agents/mav.js.map +1 -1
  12. package/dist/agents/nico.js +33 -61
  13. package/dist/agents/nico.js.map +1 -1
  14. package/dist/agents/planner.js +8 -80
  15. package/dist/agents/planner.js.map +1 -1
  16. package/dist/dimensions/autonomy/human-on-the-loop.js +1 -1
  17. package/dist/dimensions/autonomy/human-on-the-loop.js.map +1 -1
  18. package/dist/dimensions/role/architect.js +19 -1
  19. package/dist/dimensions/role/architect.js.map +1 -1
  20. package/dist/dimensions/role/{build.d.ts → assayer.d.ts} +1 -1
  21. package/dist/dimensions/role/assayer.js +25 -0
  22. package/dist/dimensions/role/assayer.js.map +1 -0
  23. package/dist/dimensions/role/{diagnose.d.ts → implementer.d.ts} +1 -1
  24. package/dist/dimensions/role/implementer.js +12 -0
  25. package/dist/dimensions/role/implementer.js.map +1 -0
  26. package/dist/dimensions/role/{curate.d.ts → planner.d.ts} +1 -1
  27. package/dist/dimensions/role/planner.js +14 -0
  28. package/dist/dimensions/role/planner.js.map +1 -0
  29. package/dist/hook-cells.js +5 -2
  30. package/dist/hook-cells.js.map +1 -1
  31. package/dist/hooks/deploy-drift-notice.js +1 -1
  32. package/dist/hooks/deploy-drift-notice.js.map +1 -1
  33. package/dist/hooks/purview-guardrail.d.ts +2 -0
  34. package/dist/hooks/purview-guardrail.js +302 -0
  35. package/dist/hooks/purview-guardrail.js.map +1 -0
  36. package/dist/hooks/stance-guardrail-pre.js +17 -22
  37. package/dist/hooks/stance-guardrail-pre.js.map +1 -1
  38. package/dist/hooks/stance-guardrail.js +84 -27
  39. package/dist/hooks/stance-guardrail.js.map +1 -1
  40. package/dist/plan-states.d.ts +3 -3
  41. package/dist/plan-states.js +3 -3
  42. package/dist/roles/architect.d.ts +2 -0
  43. package/dist/roles/architect.js +101 -0
  44. package/dist/roles/architect.js.map +1 -0
  45. package/dist/roles/assayer.d.ts +2 -0
  46. package/dist/roles/assayer.js +66 -0
  47. package/dist/roles/assayer.js.map +1 -0
  48. package/dist/roles/hold.d.ts +57 -0
  49. package/dist/roles/hold.js +105 -0
  50. package/dist/roles/hold.js.map +1 -0
  51. package/dist/roles/implementer.d.ts +2 -0
  52. package/dist/roles/implementer.js +60 -0
  53. package/dist/roles/implementer.js.map +1 -0
  54. package/dist/roles/planner.d.ts +2 -0
  55. package/dist/roles/planner.js +70 -0
  56. package/dist/roles/planner.js.map +1 -0
  57. package/dist/skills/carry-on/skill.js +2 -2
  58. package/dist/skills/carry-on/skill.js.map +1 -1
  59. package/dist/skills/deliver/skill.js +41 -3
  60. package/dist/skills/deliver/skill.js.map +1 -1
  61. package/package.json +3 -6
  62. package/dist/dimensions/role/build.js +0 -2
  63. package/dist/dimensions/role/build.js.map +0 -1
  64. package/dist/dimensions/role/converse.d.ts +0 -2
  65. package/dist/dimensions/role/converse.js +0 -2
  66. package/dist/dimensions/role/converse.js.map +0 -1
  67. package/dist/dimensions/role/curate.js +0 -2
  68. package/dist/dimensions/role/curate.js.map +0 -1
  69. package/dist/dimensions/role/diagnose.js +0 -2
  70. package/dist/dimensions/role/diagnose.js.map +0 -1
  71. package/dist/dimensions/role/document.d.ts +0 -2
  72. package/dist/dimensions/role/document.js +0 -2
  73. package/dist/dimensions/role/document.js.map +0 -1
  74. package/dist/dimensions/role/implement.d.ts +0 -2
  75. package/dist/dimensions/role/implement.js +0 -2
  76. package/dist/dimensions/role/implement.js.map +0 -1
  77. package/dist/dimensions/role/operate.d.ts +0 -2
  78. package/dist/dimensions/role/operate.js +0 -2
  79. package/dist/dimensions/role/operate.js.map +0 -1
  80. package/dist/dimensions/role/orchestrate.d.ts +0 -2
  81. package/dist/dimensions/role/orchestrate.js +0 -2
  82. package/dist/dimensions/role/orchestrate.js.map +0 -1
  83. package/dist/dimensions/role/plan.d.ts +0 -2
  84. package/dist/dimensions/role/plan.js +0 -2
  85. package/dist/dimensions/role/plan.js.map +0 -1
  86. package/dist/dimensions/role/research.d.ts +0 -2
  87. package/dist/dimensions/role/research.js +0 -2
  88. package/dist/dimensions/role/research.js.map +0 -1
  89. package/dist/dimensions/role/review.d.ts +0 -2
  90. package/dist/dimensions/role/review.js +0 -2
  91. package/dist/dimensions/role/review.js.map +0 -1
  92. package/dist/dimensions/role/test.d.ts +0 -2
  93. package/dist/dimensions/role/test.js +0 -2
  94. package/dist/dimensions/role/test.js.map +0 -1
  95. package/dist/hooks/praxis-continuity.d.ts +0 -2
  96. package/dist/hooks/praxis-continuity.js +0 -132
  97. package/dist/hooks/praxis-continuity.js.map +0 -1
@@ -0,0 +1,302 @@
1
+ // purview-guardrail — the gate that makes a ROLE CONTRACT scoreable.
2
+ //
3
+ // A role value now states the arrow the position is over the ladder
4
+ // `intent ≺ C ≺ spec ≺ artifact`, and an act whose domain or codomain falls outside
5
+ // that arrow is delegated NECESSARILY. Declared, that is a steer. This cell is the
6
+ // bound: it refuses, before the call fires, an act the holder's own contract puts
7
+ // outside its arrow.
8
+ //
9
+ // WHY A SECOND CELL RATHER THAN A SECOND RUBRIC LEG. One gate, one contract. The
10
+ // stance guard judges `autonomy` — whether the agent collapsed into order-taking — and
11
+ // this one judges `role`. They score different declarations, at different moments, and
12
+ // `gates` in the stance manifest is keyed by OWNING CELL precisely so a second guarded
13
+ // dimension is a second entry rather than an append to somebody else's prompt. The
14
+ // judge BACKEND is shared, exactly as the pre-hook shares it: one judge, two contracts.
15
+ //
16
+ // IT QUOTES THE AGENT'S OWN PROJECTED CONTRACT, and that is the design decision worth
17
+ // stating. The rubric carries no role text and names no agent. The worker reads the
18
+ // `## Role` section out of the persona's own deployed Target and hands it to the judge
19
+ // as the law to score against, so the gate scores WHAT THE CORPUS DECLARES rather than
20
+ // an author's opinion about good behaviour — and a corpus that mints a sixth role gets
21
+ // it judged with no edit here. This is the lesson the stance guard learned the
22
+ // expensive way: its allowlist drifted and left `architect` and `kino` unjudged while
23
+ // every agent in the corpus composed the fragment it claimed to enforce.
24
+ //
25
+ // WHAT IT BLOCKS is what the arrow excludes, and nothing else:
26
+ // · a dispatch to a builder for a piece no planner decomposed, from a holder whose
27
+ // arrow does not write spec — the architect skipping the middle rung;
28
+ // · a write to the substrate by a holder whose arrow does not write artifact;
29
+ // · a dispatch whose prompt is the operator's literal words rather than a cut piece
30
+ // — the same dispatch-echo the stance guard catches at turn end, refused here
31
+ // because at this moment the contract that convicts it is the ROLE's.
32
+ // WHAT IT PASSES: a dispatch to a planner, a dispatch to an assayer, READING anything,
33
+ // and writing whatever the arrow reserves. Reading is never a block — the architect's
34
+ // contract makes an artifact read a descent, but a read is cheap to undo and a gate
35
+ // that fires on one would wedge every legitimate orientation.
36
+ //
37
+ // FAIL-OPEN, EVIDENCE-CHECKED, RE-ENTRY-CAPPED — all three inherited from the sibling
38
+ // pre-hook, which is also the structural model for the worker. A block whose cited span
39
+ // is not literally in the payload is DISCARDED: a fabricated block is not a lesser
40
+ // error than a missed one.
41
+ export const purviewGuardrail = {
42
+ id: 'purview-guardrail',
43
+ residue: 'structural-refusal ↾ mid-turn act ∉ role-arrow · deny-before-fire ⟨descent ⟨write(artifact) ∉ writes⟩ · skipped-rung ⟨dispatch(build) ∄ decomposition⟩ · dispatch-echo ⟨literal-words ≠ cut-piece⟩⟩ · pass ⟨read · act ∈ reserves · dispatch ↦ plan ∨ assay⟩ · law = the HOLDER-projected role-section ⟨quoted verbatim · ¬ authored-opinion⟩ · shared judge-backend ⟨sibling⟩ · fail-open ∧ evidence-checked ∧ re-entry-capped',
44
+ substrate: 'harness',
45
+ order: 1,
46
+ events: ['subagent.dispatch.pre', 'tool.use.pre'],
47
+ entry: 'purview-guardrail.sh',
48
+ timeout: 60,
49
+ refs: [],
50
+ workers: [
51
+ {
52
+ filename: 'purview-guardrail.sh',
53
+ targetPath: 'packages/canon/targets/guardrail/purview-guardrail.sh',
54
+ executable: true,
55
+ content: `#!/usr/bin/env sh
56
+ # purview-guardrail — a PreToolUse hook that STRUCTURALLY REFUSES, before the call
57
+ # fires, a mid-turn act that falls outside the arrow the agent's OWN role contract
58
+ # declares.
59
+ #
60
+ # THE LAW IS NOT IN THIS FILE AND NOT IN THE RUBRIC. It is the \`## Role\` section of
61
+ # the persona's deployed Target, read at run time and handed to the judge as the
62
+ # contract to score against. No role text, no agent name and no behavioural opinion is
63
+ # hard-coded anywhere in this gate.
64
+ #
65
+ # SAFETY MODEL (mirrors both siblings):
66
+ # - SCOPE-ENROLLED by PRESENCE. A persona carrying a stance manifest is judged; every
67
+ # other scope is silent. No allowlist — the projection that places a persona enrolls
68
+ # it, and nothing central lists anybody.
69
+ # - CONTRACT-ENROLLED, one step further. A persona whose Target carries no \`## Role\`
70
+ # section has no arrow to be outside of, so it is allowed. A gate cannot convict
71
+ # against a law that was never declared.
72
+ # - FAILS OPEN. Any error (no jq, no Target, judge failure) -> exit 0.
73
+ # - EVIDENCE-CHECKED. A BLOCK must quote a span that is literally present in the
74
+ # payload; a citation that is not there is a fabricated block and is DISCARDED.
75
+ # - LOOP-SAFE. A re-entry cap: never deny an identical tool_input twice.
76
+ #
77
+ # INPUT : Claude Code PreToolUse hook JSON on stdin.
78
+ # OUTPUT : on a purview breach -> a deny decision on stdout + exit 0.
79
+ # otherwise -> no stdout + exit 0.
80
+ #
81
+ # POSIX sh. Depends on: jq. Missing jq -> fail open.
82
+
83
+ set -eu
84
+
85
+ # The sibling Stop hook's deployed dir owns the SHARED judge backend, resolved from
86
+ # this script's own location exactly as the pre-hook resolves it — every hooks root
87
+ # holds the hook dirs as siblings, so this derivation is true at every site this file
88
+ # can land.
89
+ SELF_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
90
+ HOOKS_ROOT="$(dirname -- "$SELF_DIR")"
91
+ JUDGE_DIR="\${STANCE_GUARD_DIR:-$HOOKS_ROOT/stance-guardrail}"
92
+ NEUTRAL_ROOT="$(dirname -- "$(dirname -- "$HOOKS_ROOT")")/.agents"
93
+ RUBRIC="\${PURVIEW_RUBRIC:-$NEUTRAL_ROOT/purview-guardrail/purview-judge-prompt.md}"
94
+ JUDGE_CMD="\${STANCE_JUDGE_CMD:-sh $JUDGE_DIR/stance-judge.sh}"
95
+ LOG="\${PURVIEW_GUARD_LOG:-$SELF_DIR/misses.log}"
96
+
97
+ trap 'exit 0' EXIT
98
+
99
+ allow() { exit 0; }
100
+ note() { printf '%s\\n' "$1" >> "$LOG" 2>/dev/null || true; }
101
+
102
+ input="$(cat)"
103
+ [ -n "$input" ] || allow
104
+ command -v jq >/dev/null 2>&1 || allow
105
+
106
+ cwd="$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null || true)"
107
+ [ -n "$cwd" ] && cd "$cwd" 2>/dev/null || true
108
+
109
+ # --- scope gate: the persona's OWN stance manifest -------------------------------------------
110
+ stance_scope="$(printf '%s' "$input" | jq -r '.stance_scope // empty' 2>/dev/null || true)"
111
+ [ -n "$stance_scope" ] || allow
112
+ manifest="$stance_scope/stance/manifest.json"
113
+ [ -f "$manifest" ] || allow
114
+ agent_type="$(jq -r '.agent // empty' "$manifest" 2>/dev/null || true)"
115
+ [ -n "$agent_type" ] || allow
116
+
117
+ # --- the LAW: this persona's own projected role contract -------------------------------------
118
+ # A persona scope is \`<root>/agent/personas/<name>\` and the Target it was projected
119
+ # from is \`<root>/agent/agents/<name>.md\` — two levels out and back down, the same
120
+ # shape of derivation the judge path above uses. Overridable for test rigs.
121
+ AGENT_ROOT="$(dirname -- "$(dirname -- "$stance_scope")")"
122
+ AGENT_MD="\${PURVIEW_AGENT_MD:-$AGENT_ROOT/agents/$agent_type.md}"
123
+ [ -f "$AGENT_MD" ] || allow
124
+ contract="$(awk '/^## Role$/{f=1;next} /^## /{f=0} f' "$AGENT_MD" 2>/dev/null || true)"
125
+ # A contract that is a BARE TOKEN states no arrow, so there is nothing to be outside
126
+ # of. Only a multi-line contract is scoreable, and saying so here is what keeps the
127
+ # gate from inventing a law for a corpus that has not declared one.
128
+ [ "$(printf '%s\\n' "$contract" | grep -c '[^[:space:]]')" -ge 2 ] || allow
129
+
130
+ # --- extract the judged payload, branched by act ---------------------------------------------
131
+ tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null || true)"
132
+ [ -n "$tool_name" ] || allow
133
+
134
+ case "$tool_name" in
135
+ Agent|SendMessage|Task)
136
+ body="$(printf '%s' "$input" | jq -r '.tool_input.prompt // .tool_input.message // .tool_input.description // ""' 2>/dev/null || true)"
137
+ target="$(printf '%s' "$input" | jq -r '.tool_input.subagent_type // .tool_input.agent // .tool_input.name // ""' 2>/dev/null || true)"
138
+ act="DISPATCH to \\\`\${target:-unnamed}\\\` (codomain: the spec this delegate will build from)"
139
+ ;;
140
+ Write|Edit|MultiEdit|NotebookEdit)
141
+ body="$(printf '%s' "$input" | jq -r '.tool_input.file_path // .tool_input.path // .tool_input.notebook_path // ""' 2>/dev/null || true)"
142
+ act="WRITE to the substrate (codomain: artifact)"
143
+ ;;
144
+ *)
145
+ # DEFENCE IN DEPTH. \`tool.use.pre\` fires on every tool on a harness with no
146
+ # subject selector; these two classes are the only acts this contract can
147
+ # convict, and a read is deliberately not one of them.
148
+ allow ;;
149
+ esac
150
+
151
+ [ -n "\${body:-}" ] || allow
152
+
153
+ payload="=== THE HOLDER'S DECLARED CONTRACT (agent: $agent_type) ===
154
+ $contract
155
+ === THE ACT ABOUT TO FIRE ===
156
+ $act
157
+ $body"
158
+
159
+ # --- loop-safety: never deny an identical tool_input twice ----------------------------------
160
+ session_id="$(printf '%s' "$input" | jq -r '.session_id // "nosession"' 2>/dev/null || echo nosession)"
161
+ sig="$(printf '%s' "$input" | jq -c '.tool_input' 2>/dev/null | cksum | cut -d' ' -f1 2>/dev/null || echo 0)"
162
+ seen="\${TMPDIR:-/tmp}/.purview-pre-$session_id-$sig"
163
+ [ -f "$seen" ] && allow
164
+
165
+ # --- judge (SHARED backend, OWN rubric) -----------------------------------------------------
166
+ if [ -n "\${PURVIEW_EMIT_PAYLOAD:-}" ]; then
167
+ jq -cn --arg r "$RUBRIC" --arg p "$payload" '{rubric:$r, payload:$p}'
168
+ exit 0
169
+ fi
170
+ if [ -n "\${PURVIEW_VERDICT_FILE:-}" ]; then
171
+ verdict="$(cat "\${PURVIEW_VERDICT_FILE}" 2>/dev/null || true)"
172
+ [ -n "$verdict" ] || {
173
+ note "$(date -u +%Y-%m-%dT%H:%M:%SZ) judge-empty tool=$tool_name agent=$agent_type"
174
+ allow
175
+ }
176
+ else
177
+ verdict="$(printf '%s' "$payload" | $JUDGE_CMD "$RUBRIC" 2>/dev/null)" || {
178
+ note "$(date -u +%Y-%m-%dT%H:%M:%SZ) judge-fail tool=$tool_name agent=$agent_type"
179
+ allow
180
+ }
181
+ fi
182
+
183
+ decision="$(printf '%s\\n' "$verdict" | sed -n 's/^VERDICT:[[:space:]]*//p' | head -1)"
184
+ [ "$decision" = "BLOCK" ] || allow
185
+
186
+ reason="$(printf '%s\\n' "$verdict" | sed -n 's/^REASON:[[:space:]]*//p' | head -1)"
187
+ [ -n "$reason" ] || reason="This act falls outside the arrow your role contract declares."
188
+
189
+ # --- EVIDENCE CHECK: the cited span must be literally in the payload -------------------------
190
+ # A block naming a span the payload does not contain is a hallucinated block, and a
191
+ # fabricated refusal is not a lesser error than a missed one. Same rule, same reason as
192
+ # the turn-end sibling.
193
+ cited="$(printf '%s\\n' "$verdict" | sed -n 's/^SPAN:[[:space:]]*//p' | head -1)"
194
+ if [ -n "$cited" ]; then
195
+ printf '%s' "$payload" | grep -qF -- "$cited" || {
196
+ note "$(date -u +%Y-%m-%dT%H:%M:%SZ) evidence-fail tool=$tool_name agent=$agent_type span=$cited"
197
+ allow
198
+ }
199
+ fi
200
+
201
+ : > "$seen" 2>/dev/null || true
202
+
203
+ feedback="PURVIEW GUARDRAIL — denied this $tool_name call: it falls outside the arrow your own \\
204
+ role contract declares. $reason Delegation is a THEOREM of that arrow, not an option: an act whose \\
205
+ domain or codomain lies outside it goes to the role that owns it — a decomposition to a planner, a \\
206
+ build to an implementer, an artifact reading to an assayer. Hand it over and carry on with what the \\
207
+ contract reserves. (Legitimate and NOT blocked here: reading anything, dispatching to a planner or an \\
208
+ assayer, and performing any act the contract's own \\\`reserves\\\` clause names.)"
209
+
210
+ jq -cn --arg r "$feedback" '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:$r}}'
211
+ exit 0
212
+ `,
213
+ },
214
+ {
215
+ filename: 'purview-judge-prompt.md',
216
+ targetPath: 'packages/canon/targets/guardrail/purview-judge-prompt.md',
217
+ executable: false,
218
+ // NEUTRAL, exactly as the stance rubric is, and for a stronger reason here: this
219
+ // rubric carries no corpus content at all. It is a procedure for scoring a
220
+ // contract supplied at run time, so it is the same bytes for any corpus that
221
+ // declares a role arrow — which is what makes the neutral root its right home
222
+ // rather than a convenience.
223
+ shared: true,
224
+ content: `# Purview judge — does this act fall outside the agent's declared arrow?
225
+
226
+ You are a PURVIEW JUDGE. You are given two things:
227
+
228
+ 1. **THE HOLDER'S DECLARED CONTRACT** — the \`## Role\` section of the agent's own definition,
229
+ verbatim. It states an arrow of the form \`reads⟨…⟩ → writes⟨…⟩\` over a ladder of
230
+ representational layers, the acts the arrow **reserves**, and often a named defect.
231
+ 2. **THE ACT ABOUT TO FIRE** — either a DISPATCH (the delegate prompt) or a WRITE (the path).
232
+
233
+ Your one job: decide whether the act falls **outside** that arrow. You judge conformance to the
234
+ contract you were handed. You do **not** judge whether the contract is wise, whether the act is a
235
+ good idea, or whether the code is any good.
236
+
237
+ ## The law is the contract, not your opinion
238
+
239
+ Every rule you apply must be readable **in the supplied contract**. If the contract does not exclude
240
+ the act, the act passes — however unwise it looks to you. An agent is entitled to the whole of what
241
+ its arrow permits, and a gate that convicts on an unwritten rule is worse than no gate, because it
242
+ teaches the agent to distrust a law it can read.
243
+
244
+ Read the arrow first, then the \`reserves\` clause, then the rest.
245
+
246
+ ## How to read an arrow
247
+
248
+ \`reads⟨A · B⟩ → writes⟨C⟩\` means: the agent may consume layers A and B, and may produce layer C.
249
+ The ladder runs \`intent ≺ C ≺ spec ≺ artifact\` from most abstract to least, where \`C\` is the
250
+ durative concept lattice — the design.
251
+
252
+ An act has a **domain** (the layer it consumes) and a **codomain** (the layer it produces):
253
+
254
+ - a DISPATCH produces a **spec** — the delegate's instructions are the layer being written;
255
+ - a WRITE to a source or config file produces an **artifact**;
256
+ - reading anything at all produces **nothing**, and is never a breach.
257
+
258
+ An act is **outside the arrow** when its codomain is not in the contract's \`writes\` set. That is the
259
+ whole test, and it is mechanical.
260
+
261
+ ## What to BLOCK
262
+
263
+ - **A write whose codomain is \`artifact\` by a holder whose \`writes\` set excludes \`artifact\`.**
264
+ The contract usually names this: \`descent\`. The path in the payload is the evidence.
265
+ - **A dispatch whose codomain is \`spec\` by a holder whose \`writes\` set excludes \`spec\`.** This is
266
+ the skipped rung: a holder that writes only the design has handed a delegate a spec that no
267
+ spec-writing role produced. The prompt is the evidence.
268
+ - **A dispatch whose prompt is the operator's literal words rather than a piece the agent cut.** The
269
+ contract that convicts this is the same clause: a dispatch is a spec, and transcription is not
270
+ authorship. Evidence is the prompt reading as relayed instructions rather than a bounded piece
271
+ with its own acceptance.
272
+
273
+ ## What to PASS — and these are the majority
274
+
275
+ - **Any read.** Reading is outside the test by construction. Even where a contract calls
276
+ artifact-reading a descent, that is a standing discipline, not a mid-turn refusal.
277
+ - **A dispatch to a role the contract explicitly routes to.** Contracts name their delegations
278
+ (\`⟨C → spec⟩ ↦ plan\`, \`⟨artifact → C⟩ ↦ assay\`). Handing work to a named delegate is the arrow
279
+ WORKING.
280
+ - **Any act the \`reserves\` clause names**, whatever it is.
281
+ - **A write by a holder whose \`writes\` set includes \`artifact\`.** Most agents in most corpora
282
+ build. Do not read a contract's other clauses as narrowing an arrow that plainly permits the act.
283
+ - **Anything you are unsure about.** Fail toward PASS. A missed breach costs one wrong act; a
284
+ fabricated block costs the agent's trust in a law it can read for itself, and it wedges work.
285
+
286
+ ## Output
287
+
288
+ Emit **only** this block, nothing before or after:
289
+
290
+ \`\`\`
291
+ VERDICT: BLOCK|PASS
292
+ REASON: <one sentence naming the clause of the contract the act falls outside, and the codomain that put it there>
293
+ SPAN: <a short literal substring copied from the payload that shows the act — omit this line entirely when the verdict is PASS>
294
+ \`\`\`
295
+
296
+ \`SPAN\` is checked against the payload character for character. If you cannot copy a literal span out
297
+ of the payload, you do not have the evidence for a BLOCK, and the verdict is PASS.
298
+ `,
299
+ },
300
+ ],
301
+ };
302
+ //# sourceMappingURL=purview-guardrail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purview-guardrail.js","sourceRoot":"","sources":["../../src/hooks/purview-guardrail.ts"],"names":[],"mappings":"AAEA,qEAAqE;AACrE,EAAE;AACF,oEAAoE;AACpE,oFAAoF;AACpF,mFAAmF;AACnF,kFAAkF;AAClF,qBAAqB;AACrB,EAAE;AACF,iFAAiF;AACjF,uFAAuF;AACvF,uFAAuF;AACvF,uFAAuF;AACvF,mFAAmF;AACnF,wFAAwF;AACxF,EAAE;AACF,sFAAsF;AACtF,oFAAoF;AACpF,uFAAuF;AACvF,uFAAuF;AACvF,uFAAuF;AACvF,+EAA+E;AAC/E,sFAAsF;AACtF,yEAAyE;AACzE,EAAE;AACF,+DAA+D;AAC/D,qFAAqF;AACrF,0EAA0E;AAC1E,gFAAgF;AAChF,sFAAsF;AACtF,kFAAkF;AAClF,0EAA0E;AAC1E,uFAAuF;AACvF,sFAAsF;AACtF,oFAAoF;AACpF,8DAA8D;AAC9D,EAAE;AACF,sFAAsF;AACtF,wFAAwF;AACxF,mFAAmF;AACnF,2BAA2B;AAE3B,MAAM,CAAC,MAAM,gBAAgB,GAAa;IACxC,EAAE,EAAE,mBAAmB;IACvB,OAAO,EACL,iaAAia;IACna,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC,uBAAuB,EAAE,cAAc,CAAC;IACjD,KAAK,EAAE,sBAAsB;IAC7B,OAAO,EAAE,EAAE;IACX,IAAI,EAAE,EAAE;IACR,OAAO,EAAE;QACP;YACE,QAAQ,EAAE,sBAAsB;YAChC,UAAU,EAAE,uDAAuD;YACnE,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Jd;SACI;QACD;YACE,QAAQ,EAAE,yBAAyB;YACnC,UAAU,EAAE,0DAA0D;YACtE,UAAU,EAAE,KAAK;YACjB,iFAAiF;YACjF,2EAA2E;YAC3E,6EAA6E;YAC7E,8EAA8E;YAC9E,6BAA6B;YAC7B,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0Ed;SACI;KACF;CACF,CAAC"}
@@ -46,9 +46,9 @@ export const stanceGuardrailPre = {
46
46
  # ambiguity, a substantive intent-extracted dispatch.
47
47
  #
48
48
  # SAFETY MODEL (mirrors stance-guardrail):
49
- # - OFF BY DEFAULT (git config agentfactory.stanceGuard true).
50
- # - AGENT-SCOPED (agent_type in the allowlist; default: nico mav). agent_type is
51
- # present only for subagents / --agent mode; a top-level session call fails open.
49
+ # - SCOPE-ENROLLED, exactly as its twin: a scope carrying a stance manifest is judged and
50
+ # every other scope is silent. No repo opt-in, no allowlist — the projection that places a
51
+ # persona also enrolls it, and declining the guard means declining the persona.
52
52
  # - FAILS OPEN. Any error (no jq, judge failure, bad input) -> exit 0 (allow the call).
53
53
  # - LOOP-SAFE. A re-entry cap: never deny an identical tool_input twice (there is no
54
54
  # stop_hook_active analog for PreToolUse), so it can never wedge a call.
@@ -98,27 +98,22 @@ input="$(cat)"
98
98
  [ -n "$input" ] || allow
99
99
  command -v jq >/dev/null 2>&1 || allow # no jq -> cannot parse -> fail open
100
100
 
101
- # --- opt-in gate (off by default) -----------------------------------------------------------
102
- # Per-repo, lives in .git/config, never checked in. Resolve relative to the hook's cwd.
101
+ # The per-repo opt-in is GONE, for the reason its twin states: it asked whether a guard may run in
102
+ # a DIRECTORY, and a stance belongs to the agent, not the checkout. \`cwd\` survives it — the worker
103
+ # still runs where the session runs.
103
104
  cwd="$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null || true)"
104
105
  [ -n "$cwd" ] && cd "$cwd" 2>/dev/null || true
105
- enabled="$(git config --bool agentfactory.stanceGuard 2>/dev/null || echo false)"
106
- [ "$enabled" = "true" ] || allow
107
-
108
- # --- agent-scope gate -----------------------------------------------------------------------
109
- # Only enforce for the configured agents (default: the principal-self agents).
110
- # agent_type is present only for a subagent / --agent tool call; absent for the top-level
111
- # session -> fail open (unless STANCE_GUARD_AGENTS=*), exactly like the Stop guard.
112
- agent_type="$(printf '%s' "$input" | jq -r '.agent_type // empty' 2>/dev/null || true)"
113
- allowlist="\${STANCE_GUARD_AGENTS:-$(git config agentfactory.stanceGuardAgents 2>/dev/null || echo 'nico mav')}"
114
- if [ "$allowlist" != "*" ]; then
115
- [ -n "$agent_type" ] || allow # cannot identify the agent -> fail open
116
- in_scope=false
117
- for a in $allowlist; do
118
- [ "$a" = "$agent_type" ] && in_scope=true && break
119
- done
120
- [ "$in_scope" = true ] || allow
121
- fi
106
+
107
+ # --- scope gate: the persona's OWN stance manifest -------------------------------------------
108
+ # The twin of the turn-end guard's gate, and inverted for the same reason: an allowlist here was
109
+ # a runtime self-filter over an enrollment the corpus already derives, and it had drifted the way
110
+ # such a list always drifts. Presence of a manifest in this persona's own scope IS enrollment.
111
+ # Absent → silence, never an error, so an unenrolled launch is untouched.
112
+ stance_scope="$(printf '%s' "$input" | jq -r '.stance_scope // empty' 2>/dev/null || true)"
113
+ [ -n "$stance_scope" ] || allow
114
+ manifest="$stance_scope/stance/manifest.json"
115
+ [ -f "$manifest" ] || allow
116
+ agent_type="$(jq -r '.agent // empty' "$manifest" 2>/dev/null || true)"
122
117
 
123
118
  # --- extract the judged payload, branched by tool -------------------------------------------
124
119
  tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty' 2>/dev/null || true)"
@@ -1 +1 @@
1
- {"version":3,"file":"stance-guardrail-pre.js","sourceRoot":"","sources":["../../src/hooks/stance-guardrail-pre.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,mFAAmF;AACnF,uFAAuF;AACvF,SAAS;AACT,wFAAwF;AACxF,6FAA6F;AAC7F,0FAA0F;AAC1F,uFAAuF;AACvF,qEAAqE;AACrE,EAAE;AACF,wEAAwE;AACxE,sFAAsF;AACtF,iFAAiF;AACjF,mFAAmF;AACnF,sFAAsF;AACtF,qFAAqF;AACrF,sFAAsF;AACtF,sFAAsF;AACtF,mEAAmE;AACnE,2DAA2D;AAC3D,mDAAmD;AAEnD,MAAM,CAAC,MAAM,kBAAkB,GAAa;IAC1C,EAAE,EAAE,sBAAsB;IAC1B,OAAO,EACL,iWAAiW;IACnW,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;IACzD,KAAK,EAAE,yBAAyB;IAChC,OAAO,EAAE,EAAE;IACX,IAAI,EAAE,EAAE;IACR,OAAO,EAAE;QACP;YACE,QAAQ,EAAE,yBAAyB;YACnC,UAAU,EAAE,0DAA0D;YACtE,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiKd;SACI;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"stance-guardrail-pre.js","sourceRoot":"","sources":["../../src/hooks/stance-guardrail-pre.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,mFAAmF;AACnF,uFAAuF;AACvF,SAAS;AACT,wFAAwF;AACxF,6FAA6F;AAC7F,0FAA0F;AAC1F,uFAAuF;AACvF,qEAAqE;AACrE,EAAE;AACF,wEAAwE;AACxE,sFAAsF;AACtF,iFAAiF;AACjF,mFAAmF;AACnF,sFAAsF;AACtF,qFAAqF;AACrF,sFAAsF;AACtF,sFAAsF;AACtF,mEAAmE;AACnE,2DAA2D;AAC3D,mDAAmD;AAEnD,MAAM,CAAC,MAAM,kBAAkB,GAAa;IAC1C,EAAE,EAAE,sBAAsB;IAC1B,OAAO,EACL,iWAAiW;IACnW,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC,sBAAsB,EAAE,uBAAuB,CAAC;IACzD,KAAK,EAAE,yBAAyB;IAChC,OAAO,EAAE,EAAE;IACX,IAAI,EAAE,EAAE;IACR,OAAO,EAAE;QACP;YACE,QAAQ,EAAE,yBAAyB;YACnC,UAAU,EAAE,0DAA0D;YACtE,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4Jd;SACI;KACF;CACF,CAAC"}
@@ -49,8 +49,13 @@ export const stanceGuardrail = {
49
49
  # electing the session's objective is supplying intent, not sequencing — the operator's to own
50
50
  #
51
51
  # SAFETY MODEL:
52
- # - OFF BY DEFAULT. Does nothing unless the repo opts in (git config agentfactory.stanceGuard true).
53
- # - AGENT-SCOPED. Only fires for agents on the allowlist (default: nico, mav — the principal-self agents).
52
+ # - SCOPE-ENROLLED. Fires for a scope carrying a stance manifest, and for no other. Enrollment
53
+ # is PRESENCE: the projection places a manifest in each persona's own scope, so composing the
54
+ # cell enrolls the persona and nothing central lists anybody. A bare launch carries the
55
+ # dispatcher and no manifest, and is therefore silent by placement.
56
+ # - NO REPO OPT-IN. There was one, per-repo in .git/config, and it asked the wrong question:
57
+ # whether a guard may run in a DIRECTORY. The off switch is launching \`omp\` rather than a
58
+ # persona — declining to BE the agent, instead of being it unjudged.
54
59
  # - FAILS OPEN, BUT NEVER SILENTLY-CLEAN. Any error → exit 0 (allow stop): a guardrail that
55
60
  # wedges work on its own flakiness is worse than a missed block. But once the guard is
56
61
  # ENABLED and in scope, a failure that prevents judging (no transcript, judge unreachable)
@@ -85,6 +90,9 @@ JUDGE_CMD="\${STANCE_JUDGE_CMD:-sh $SELF_DIR/stance-judge.sh}"
85
90
  # a host and point outside a sandboxed deploy, which is the class of bug that put
86
91
  # \`\$HOME/.claude\` in the sibling worker and made it read another harness's tree.
87
92
  NEUTRAL_ROOT="$(dirname -- "$(dirname -- "$(dirname -- "$SELF_DIR")")")/.agents"
93
+ # The DEFAULT rubric. A persona's own manifest may name another — that is the
94
+ # strategy seam: one opinionated implementation, each persona free to bind a
95
+ # different contract without the dispatcher learning anything about either.
88
96
  RUBRIC="\${STANCE_RUBRIC:-$NEUTRAL_ROOT/stance-guardrail/stance-judge-prompt.md}"
89
97
 
90
98
  # A Stop hook must never break a session. Trap any unexpected error → allow the stop.
@@ -102,6 +110,15 @@ allow_stop() { exit 0; } # emit nothing; the agent is permitted to stop.
102
110
  # turn. Reached only AFTER the opt-in and allowlist checks, so a repo that never enabled
103
111
  # the guard stays silent.
104
112
  dark() {
113
+ # THE HEARTBEAT, and the reason the log is worth keeping. A VERDICT row and a
114
+ # DARK row are both evidence the guard RAN; only an EMPTY log means it never
115
+ # did. Recording judged turns alone made "not judged" and "no session" read
116
+ # identically — measured on a live host: an endpoint behind the advisor role
117
+ # accepted connections and never answered, and five hours of one session were
118
+ # unjudged while leaving no trace whatsoever to notice it by.
119
+ if [ -n "\${verdict_log:-}" ]; then
120
+ printf 'DARK\\t\\t%s\\n' "\$1" >> "\$verdict_log" 2>/dev/null || true
121
+ fi
105
122
  printf 'STANCE GUARDRAIL — DARK: %s. This turn was NOT judged; the absence of a block is an absence of a verdict, not a clean one.\\n' "\$1"
106
123
  exit 0
107
124
  }
@@ -138,32 +155,55 @@ verdict_log="$state_dir/$session.verdicts"
138
155
  block_count="$(cat "$count_file" 2>/dev/null || echo 0)"
139
156
  case "$block_count" in *[!0-9]*) block_count=0 ;; esac
140
157
 
141
- # --- opt-in gate (off by default) -----------------------------------------------------------
142
- # Per-repo, lives in .git/config, never checked in. A fresh clone is opted out.
143
- # Resolve relative to the hook's cwd (the project), which is where the flag lives.
158
+ # The per-repo opt-in is GONE, and its absence is the point. It answered "may a guard run in this
159
+ # DIRECTORY", which is a category error: a stance is a property of the agent, not of the checkout
160
+ # it happens to be standing in. Worse, it made the stance OPTIONAL AT RUNTIME for an agent already
161
+ # launched as itself — the ambient form, when the whole reason this harness half exists is that
162
+ # identity alone erodes. The off switch is launching \`omp\` instead of \`mav\`: declining to be the
163
+ # persona, rather than being it unjudged.
144
164
  cwd="$(printf '%s' "$input" | jq -r '.cwd // empty' 2>/dev/null || true)"
145
165
  [ -n "$cwd" ] && cd "$cwd" 2>/dev/null || true
146
- enabled="$(git config --bool agentfactory.stanceGuard 2>/dev/null || echo false)"
147
- [ "$enabled" = "true" ] || allow_stop
148
-
149
- # --- agent-scope gate -----------------------------------------------------------------------
150
- # Only enforce the stance for the configured agents (the principal-self agents by default).
151
- # agent_type identifies the agent (verified by an introspective-hook capture): it is PRESENT for an
152
- # --agent / @mention launch — top-level INCLUDED (a session started as @nico reports agent_type=nico)
153
- # — and for every SubagentStop; it is ABSENT only for a DEFAULT top-level session (plain claude, no
154
- # --agent). When absent we do NOT enforce: a session that never declared itself a principal should not
155
- # get the principal rubric. STANCE_GUARD_AGENTS=* overrides to enforce on everyone (a blunt instrument).
156
- agent_type="$(printf '%s' "$input" | jq -r '.agent_type // empty' 2>/dev/null || true)"
157
- allowlist="\${STANCE_GUARD_AGENTS:-$(git config agentfactory.stanceGuardAgents 2>/dev/null || echo 'nico mav')}"
158
-
159
- if [ "$allowlist" != "*" ]; then
160
- [ -n "$agent_type" ] || allow_stop # cannot identify the agent → fail open
161
- in_scope=false
162
- for a in $allowlist; do
163
- [ "$a" = "$agent_type" ] && in_scope=true && break
164
- done
165
- [ "$in_scope" = true ] || allow_stop
166
- fi
166
+
167
+ # --- scope gate: the persona's OWN stance manifest -------------------------------------------
168
+ # COMPOSITION IS THE SCOPE, NOT A RUNTIME SELF-FILTER — MODEL.md's ENFORCED clause, and this
169
+ # gate used to violate it outright. It read an allowlist, \`nico mav\` by default and overridable
170
+ # by \`STANCE_GUARD_AGENTS\`, which is precisely the runtime self-filter that clause forbids: a
171
+ # module asking WHO IS RUNNING rather than being placed where only the right launch reaches it.
172
+ #
173
+ # It had already drifted, in the direction such a list always drifts. Every agent in the corpus
174
+ # composes \`handoff\`, so \`Binding.agents\` DERIVES all six as bound, and six persona scopes are
175
+ # deployed to prove it — while the list enforced two. \`architect\` and \`kino\` held principal
176
+ # authority and were never once judged, and nothing surfaced the gap because the two facts were
177
+ # written in different languages in different files.
178
+ #
179
+ # Enrollment is now PRESENCE. Each bound persona's projection lands a manifest in that persona's
180
+ # own scope, and the dispatcher — which lives in that same scope and therefore already knows it
181
+ # — passes it in the envelope. A manifest means enrolled, and carries THIS agent's contract: its
182
+ # rubric, its moments, its handoff laws. No manifest means not enrolled, which is silence rather
183
+ # than an error, so a plain session that never declared itself anything stays untouched exactly
184
+ # as it was. Adding a persona enrolls it; adding a gated dimension edits one persona's manifest;
185
+ # neither is a line in this file.
186
+ stance_scope="$(printf '%s' "$input" | jq -r '.stance_scope // empty' 2>/dev/null || true)"
187
+ [ -n "$stance_scope" ] || allow_stop
188
+ manifest="$stance_scope/stance/manifest.json"
189
+ [ -f "$manifest" ] || allow_stop
190
+
191
+ # The persona's own contract, each field falling back to the shipped default. A manifest that
192
+ # names nothing still enrolls: presence is the assertion, the fields are the refinement.
193
+ agent_type="$(jq -r '.agent // empty' "$manifest" 2>/dev/null || true)"
194
+ manifest_rubric="$(jq -r '.rubric // empty' "$manifest" 2>/dev/null || true)"
195
+ # An OVERRIDE that does not resolve is a defect and goes DARK — a persona that asked to be judged
196
+ # against its own contract and is silently judged against someone else's has been mis-scored, not
197
+ # spared. The DEFAULT is not checked here: a judge backend need not read the file at all (the
198
+ # fixture judge does not), and refusing to run because a path the manifest never named is absent
199
+ # would fail closed on a case the manifest made no claim about.
200
+ case "$manifest_rubric" in
201
+ '') ;;
202
+ /*) RUBRIC="$manifest_rubric" ;;
203
+ *) RUBRIC="$stance_scope/stance/$manifest_rubric" ;;
204
+ esac
205
+ [ -z "$manifest_rubric" ] || [ -f "$RUBRIC" ] || \\
206
+ dark "the rubric named by $manifest is not readable at '\$RUBRIC'"
167
207
 
168
208
  # --- extract the last assistant turn from the transcript ------------------------------------
169
209
  transcript="$(printf '%s' "$input" | jq -r '.transcript_path // empty' 2>/dev/null || true)"
@@ -500,7 +540,24 @@ if [ -n "$evidence" ] && [ "\${#evidence}" -ge 12 ]; then
500
540
  # 3/5 on identical payloads — i.e. the record of WHY a turn was blocked was reconstructible only
501
541
  # by a non-deterministic process. Appended, never rotated by this hook; the state dir is tmp.
502
542
  printf '%s\\t%s\\t%s\\n' "$decision" "$evidence" "$reason" >> "$verdict_log" 2>/dev/null || true
503
- if ! printf '%s' "$asst_close" | tr '\\n' ' ' | grep -qF "$evidence"; then
543
+ # NORMALIZED ON BOTH SIDES, and with \`--\`, because this check had two ways to
544
+ # throw away a correct block — and both of them fired on the SAME shape, which is
545
+ # the one shape this guard most exists to catch.
546
+ #
547
+ # 1. \`grep -qF "$evidence"\` with no \`--\`: an evidence span opening with a markdown
548
+ # bullet is read as an OPTION. \`grep: invalid option -- ' '\`, non-zero, block
549
+ # discarded. A tail-enumeration collapse IS a bullet list, so its evidence always
550
+ # begins \`- \`, and the rule could never convict the shape it was written for.
551
+ # 2. A judge quoting several bullets joins them with spaces and drops the markers,
552
+ # so a verbatim-substring test fails on punctuation while every word is present.
553
+ #
554
+ # The proposition worth authenticating is that the judge quoted THIS TURN'S WORDS,
555
+ # not that it reproduced its list syntax. So both sides are flattened the same way:
556
+ # newlines to spaces, runs of whitespace to one, list markers dropped. Symmetric, so
557
+ # nothing is accepted on one side that would be rejected on the other.
558
+ ev_norm="$(printf '%s' "$evidence" | tr '\\n' ' ' | sed -e 's/[[:space:]][[:space:]]*/ /g' -e 's/^[-*][[:space:]]//' -e 's/ [-*] / /g')"
559
+ close_norm="$(printf '%s' "$asst_close" | tr '\\n' ' ' | sed -e 's/[[:space:]][[:space:]]*/ /g' -e 's/^[-*][[:space:]]//' -e 's/ [-*] / /g')"
560
+ if ! printf '%s' "$close_norm" | grep -qF -- "$ev_norm"; then
504
561
  printf 'stance-guardrail: DISCARDING block — judge quoted a span that is not in the turn'"'"'s CLOSE (mid-turn preamble, or confabulated): %s\\n' "$evidence" >&2
505
562
  allow_stop
506
563
  fi
@@ -1 +1 @@
1
- {"version":3,"file":"stance-guardrail.js","sourceRoot":"","sources":["../../src/hooks/stance-guardrail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAG5D,wEAAwE;AACxE,8EAA8E;AAC9E,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,gFAAgF;AAEhF,MAAM,CAAC,MAAM,eAAe,GAAa;IACvC,EAAE,EAAE,kBAAkB;IACtB,OAAO,EACL,8UAA8U;IAChV,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,KAAK,EAAE,qBAAqB;IAC5B,OAAO,EAAE,EAAE;IACX,IAAI,EAAE,EAAE;IACR,OAAO,EAAE;QACP;YACE,QAAQ,EAAE,qBAAqB;YAC/B,UAAU,EAAE,sDAAsD;YAClE,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+jBd;SACI;QACD;YACE,QAAQ,EAAE,iBAAiB;YAC3B,UAAU,EAAE,kDAAkD;YAC9D,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFd;SACI;QACD;YACE,QAAQ,EAAE,wBAAwB;YAClC,UAAU,EAAE,yDAAyD;YACrE,UAAU,EAAE,KAAK;YACjB,yEAAyE;YACzE,sEAAsE;YACtE,0EAA0E;YAC1E,2EAA2E;YAC3E,yEAAyE;YACzE,yEAAyE;YACzE,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAsNX,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyLV;SACI;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"stance-guardrail.js","sourceRoot":"","sources":["../../src/hooks/stance-guardrail.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mCAAmC,CAAC;AAG5D,wEAAwE;AACxE,8EAA8E;AAC9E,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,gFAAgF;AAEhF,MAAM,CAAC,MAAM,eAAe,GAAa;IACvC,EAAE,EAAE,kBAAkB;IACtB,OAAO,EACL,8UAA8U;IAChV,SAAS,EAAE,SAAS;IACpB,KAAK,EAAE,CAAC;IACR,MAAM,EAAE,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,KAAK,EAAE,qBAAqB;IAC5B,OAAO,EAAE,EAAE;IACX,IAAI,EAAE,EAAE;IACR,OAAO,EAAE;QACP;YACE,QAAQ,EAAE,qBAAqB;YAC/B,UAAU,EAAE,sDAAsD;YAClE,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwnBd;SACI;QACD;YACE,QAAQ,EAAE,iBAAiB;YAC3B,UAAU,EAAE,kDAAkD;YAC9D,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFd;SACI;QACD;YACE,QAAQ,EAAE,wBAAwB;YAClC,UAAU,EAAE,yDAAyD;YACrE,UAAU,EAAE,KAAK;YACjB,yEAAyE;YACzE,sEAAsE;YACtE,0EAA0E;YAC1E,2EAA2E;YAC3E,yEAAyE;YACzE,yEAAyE;YACzE,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAsNX,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyLV;SACI;KACF;CACF,CAAC"}
@@ -1,14 +1,14 @@
1
1
  /** The plan-layout state folders, in task-file lifecycle order. */
2
2
  export declare const PLAN_STATES: readonly ["pending", "ready", "active", "completed"];
3
3
  /** `frontier(P)` — the states a shard sits in when the plan is workable AT it
4
- * (praxis: `{ t | state(t) ∈ { ready, active } }`, where the plan IS, not merely
4
+ * (`{ t | state(t) ∈ { ready, active } }`, where the plan IS, not merely
5
5
  * what is dispatchable). A subset of {@link PLAN_STATES} and not derivable from
6
6
  * their ORDER: `pending` is open too, and is not frontier. */
7
7
  export declare const PLAN_FRONTIER: readonly ["ready", "active"];
8
8
  /**
9
9
  * The plan-tier dotfile MARKERS whose only home is here.
10
10
  *
11
- * A relation with no on-disk carrier is not readable — the reading `praxis.sh`
11
+ * A relation with no on-disk carrier is not readable — the reading `plan-set.sh`
12
12
  * states in its own words when it explains why `.landed` exists ("exactly parallel
13
13
  * to `.superseded-by`"). These are the two markers a reader of the plan set needs
14
14
  * that have no other TypeScript home: `.superseded-by` already has one
@@ -16,7 +16,7 @@ export declare const PLAN_FRONTIER: readonly ["ready", "active"];
16
16
  *
17
17
  * `.ruling-owed` is the third carrier this corpus has minted on that reading, and
18
18
  * it carries the concept the corpus ALREADY signifies as a ruling owed — "a
19
- * decision nobody has taken", which the `praxis` cell distinguishes from a
19
+ * decision nobody has taken", which the retired praxis cell distinguished from a
20
20
  * dependency ("a dep waits itself out, a ruling needs a human"). At the SHARD tier
21
21
  * that concept is `ruling-owed(t)`, and it gates `ready`; at the
22
22
  * PLAN tier it had no carrier at all, so a fork the principal cannot resolve —
@@ -12,7 +12,7 @@
12
12
  /** The plan-layout state folders, in task-file lifecycle order. */
13
13
  export const PLAN_STATES = ['pending', 'ready', 'active', 'completed'];
14
14
  /** `frontier(P)` — the states a shard sits in when the plan is workable AT it
15
- * (praxis: `{ t | state(t) ∈ { ready, active } }`, where the plan IS, not merely
15
+ * (`{ t | state(t) ∈ { ready, active } }`, where the plan IS, not merely
16
16
  * what is dispatchable). A subset of {@link PLAN_STATES} and not derivable from
17
17
  * their ORDER: `pending` is open too, and is not frontier. */
18
18
  export const PLAN_FRONTIER = [
@@ -22,7 +22,7 @@ export const PLAN_FRONTIER = [
22
22
  /**
23
23
  * The plan-tier dotfile MARKERS whose only home is here.
24
24
  *
25
- * A relation with no on-disk carrier is not readable — the reading `praxis.sh`
25
+ * A relation with no on-disk carrier is not readable — the reading `plan-set.sh`
26
26
  * states in its own words when it explains why `.landed` exists ("exactly parallel
27
27
  * to `.superseded-by`"). These are the two markers a reader of the plan set needs
28
28
  * that have no other TypeScript home: `.superseded-by` already has one
@@ -30,7 +30,7 @@ export const PLAN_FRONTIER = [
30
30
  *
31
31
  * `.ruling-owed` is the third carrier this corpus has minted on that reading, and
32
32
  * it carries the concept the corpus ALREADY signifies as a ruling owed — "a
33
- * decision nobody has taken", which the `praxis` cell distinguishes from a
33
+ * decision nobody has taken", which the retired praxis cell distinguished from a
34
34
  * dependency ("a dep waits itself out, a ruling needs a human"). At the SHARD tier
35
35
  * that concept is `ruling-owed(t)`, and it gates `ready`; at the
36
36
  * PLAN tier it had no carrier at all, so a fork the principal cannot resolve —
@@ -0,0 +1,2 @@
1
+ import type { RoleCell } from './hold.js';
2
+ export declare const architectRole: RoleCell;