@aria_asi/cli 0.2.26 → 0.2.30

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 (253) hide show
  1. package/CLIENT-ONBOARDING.md +282 -0
  2. package/bin/aria.js +1140 -14
  3. package/dist/aria-connector/src/auth-commands.d.ts +1 -0
  4. package/dist/aria-connector/src/auth-commands.d.ts.map +1 -1
  5. package/dist/aria-connector/src/auth-commands.js +89 -41
  6. package/dist/aria-connector/src/auth-commands.js.map +1 -1
  7. package/dist/aria-connector/src/chat.d.ts +3 -0
  8. package/dist/aria-connector/src/chat.d.ts.map +1 -1
  9. package/dist/aria-connector/src/chat.js +146 -8
  10. package/dist/aria-connector/src/chat.js.map +1 -1
  11. package/dist/aria-connector/src/codebase-scanner.d.ts +2 -2
  12. package/dist/aria-connector/src/codebase-scanner.d.ts.map +1 -1
  13. package/dist/aria-connector/src/codebase-scanner.js +1 -1
  14. package/dist/aria-connector/src/codebase-scanner.js.map +1 -1
  15. package/dist/aria-connector/src/config.d.ts +12 -0
  16. package/dist/aria-connector/src/config.d.ts.map +1 -1
  17. package/dist/aria-connector/src/config.js +2 -0
  18. package/dist/aria-connector/src/config.js.map +1 -1
  19. package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
  20. package/dist/aria-connector/src/connectors/claude-code.js +80 -24
  21. package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
  22. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts +37 -0
  23. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts.map +1 -0
  24. package/dist/aria-connector/src/connectors/codebase-awareness.js +335 -0
  25. package/dist/aria-connector/src/connectors/codebase-awareness.js.map +1 -0
  26. package/dist/aria-connector/src/connectors/codex.d.ts +3 -0
  27. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -0
  28. package/dist/aria-connector/src/connectors/codex.js +248 -0
  29. package/dist/aria-connector/src/connectors/codex.js.map +1 -0
  30. package/dist/aria-connector/src/connectors/cognitive-skills.d.ts +2 -0
  31. package/dist/aria-connector/src/connectors/cognitive-skills.d.ts.map +1 -0
  32. package/dist/aria-connector/src/connectors/cognitive-skills.js +47 -0
  33. package/dist/aria-connector/src/connectors/cognitive-skills.js.map +1 -0
  34. package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
  35. package/dist/aria-connector/src/connectors/opencode.js +90 -4
  36. package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
  37. package/dist/aria-connector/src/connectors/repo-git-hooks.d.ts +3 -0
  38. package/dist/aria-connector/src/connectors/repo-git-hooks.d.ts.map +1 -0
  39. package/dist/aria-connector/src/connectors/repo-git-hooks.js +87 -0
  40. package/dist/aria-connector/src/connectors/repo-git-hooks.js.map +1 -0
  41. package/dist/aria-connector/src/connectors/repo-guard.d.ts +19 -0
  42. package/dist/aria-connector/src/connectors/repo-guard.d.ts.map +1 -0
  43. package/dist/aria-connector/src/connectors/repo-guard.js +509 -0
  44. package/dist/aria-connector/src/connectors/repo-guard.js.map +1 -0
  45. package/dist/aria-connector/src/connectors/runtime.d.ts +2 -0
  46. package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -0
  47. package/dist/aria-connector/src/connectors/runtime.js +330 -0
  48. package/dist/aria-connector/src/connectors/runtime.js.map +1 -0
  49. package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
  50. package/dist/aria-connector/src/connectors/shell.js +78 -13
  51. package/dist/aria-connector/src/connectors/shell.js.map +1 -1
  52. package/dist/aria-connector/src/connectors/syncd.d.ts +27 -0
  53. package/dist/aria-connector/src/connectors/syncd.d.ts.map +1 -0
  54. package/dist/aria-connector/src/connectors/syncd.js +405 -0
  55. package/dist/aria-connector/src/connectors/syncd.js.map +1 -0
  56. package/dist/aria-connector/src/decisions.d.ts +207 -0
  57. package/dist/aria-connector/src/decisions.d.ts.map +1 -0
  58. package/dist/aria-connector/src/decisions.js +291 -0
  59. package/dist/aria-connector/src/decisions.js.map +1 -0
  60. package/dist/aria-connector/src/garden-control-plane.d.ts.map +1 -1
  61. package/dist/aria-connector/src/garden-control-plane.js +74 -17
  62. package/dist/aria-connector/src/garden-control-plane.js.map +1 -1
  63. package/dist/aria-connector/src/github-connect.d.ts +18 -0
  64. package/dist/aria-connector/src/github-connect.d.ts.map +1 -0
  65. package/dist/aria-connector/src/github-connect.js +117 -0
  66. package/dist/aria-connector/src/github-connect.js.map +1 -0
  67. package/dist/aria-connector/src/harness-client.d.ts +15 -0
  68. package/dist/aria-connector/src/harness-client.d.ts.map +1 -1
  69. package/dist/aria-connector/src/harness-client.js +106 -3
  70. package/dist/aria-connector/src/harness-client.js.map +1 -1
  71. package/dist/aria-connector/src/hive-client.d.ts +30 -0
  72. package/dist/aria-connector/src/hive-client.d.ts.map +1 -1
  73. package/dist/aria-connector/src/hive-client.js +124 -5
  74. package/dist/aria-connector/src/hive-client.js.map +1 -1
  75. package/dist/aria-connector/src/index.d.ts +13 -2
  76. package/dist/aria-connector/src/index.d.ts.map +1 -1
  77. package/dist/aria-connector/src/index.js +10 -1
  78. package/dist/aria-connector/src/index.js.map +1 -1
  79. package/dist/aria-connector/src/lib/aristotle-noor-wire.d.ts +102 -0
  80. package/dist/aria-connector/src/lib/aristotle-noor-wire.d.ts.map +1 -0
  81. package/dist/aria-connector/src/lib/aristotle-noor-wire.js +231 -0
  82. package/dist/aria-connector/src/lib/aristotle-noor-wire.js.map +1 -0
  83. package/dist/aria-connector/src/providers/types.d.ts +5 -0
  84. package/dist/aria-connector/src/providers/types.d.ts.map +1 -1
  85. package/dist/aria-connector/src/runtime-proof.d.ts +45 -0
  86. package/dist/aria-connector/src/runtime-proof.d.ts.map +1 -0
  87. package/dist/aria-connector/src/runtime-proof.js +340 -0
  88. package/dist/aria-connector/src/runtime-proof.js.map +1 -0
  89. package/dist/aria-connector/src/self-update.d.ts +2 -1
  90. package/dist/aria-connector/src/self-update.d.ts.map +1 -1
  91. package/dist/aria-connector/src/self-update.js +84 -8
  92. package/dist/aria-connector/src/self-update.js.map +1 -1
  93. package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -1
  94. package/dist/aria-connector/src/setup-wizard.js +34 -2
  95. package/dist/aria-connector/src/setup-wizard.js.map +1 -1
  96. package/dist/assets/hooks/aria-agent-handoff.mjs +224 -0
  97. package/dist/assets/hooks/aria-agent-ledger-merge.mjs +164 -0
  98. package/dist/assets/hooks/aria-architect-fallback.mjs +267 -0
  99. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +668 -0
  100. package/dist/assets/hooks/aria-discovery-record.mjs +101 -0
  101. package/dist/assets/hooks/aria-harness-via-sdk.mjs +412 -0
  102. package/dist/assets/hooks/aria-import-resolution-gate.mjs +330 -0
  103. package/dist/assets/hooks/aria-outcome-record.mjs +84 -0
  104. package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +294 -0
  105. package/dist/assets/hooks/aria-pre-text-gate.mjs +112 -0
  106. package/dist/assets/hooks/aria-pre-tool-gate.mjs +2133 -0
  107. package/dist/assets/hooks/aria-preprompt-consult.mjs +438 -0
  108. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +570 -0
  109. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +397 -0
  110. package/dist/assets/hooks/aria-stop-gate.mjs +1551 -0
  111. package/dist/assets/hooks/aria-trigger-autolearn.mjs +229 -0
  112. package/dist/assets/hooks/aria-userprompt-abandon-detect.mjs +192 -0
  113. package/dist/assets/hooks/doctrine_trigger_map.json +479 -0
  114. package/dist/assets/hooks/lib/canonical-lenses.mjs +64 -0
  115. package/dist/assets/hooks/lib/gate-audit.mjs +43 -0
  116. package/dist/assets/hooks/test-aria-preturn-memory-gate.mjs +245 -0
  117. package/dist/assets/hooks/test-tier-lens-labeling.mjs +399 -0
  118. package/dist/assets/opencode-plugins/harness-context/index.js +60 -0
  119. package/dist/assets/opencode-plugins/harness-context/inject-context.mjs +179 -0
  120. package/dist/assets/opencode-plugins/harness-context/package.json +9 -0
  121. package/dist/assets/opencode-plugins/harness-gate/index.js +248 -0
  122. package/dist/assets/opencode-plugins/harness-outcome/index.js +129 -0
  123. package/dist/assets/opencode-plugins/harness-role/index.js +77 -0
  124. package/dist/assets/opencode-plugins/harness-role/package.json +9 -0
  125. package/dist/assets/opencode-plugins/harness-stop/index.js +241 -0
  126. package/dist/runtime/discipline/CLAUDE.md +339 -0
  127. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  128. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  129. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  130. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  131. package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  132. package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  133. package/dist/runtime/discipline/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  134. package/dist/runtime/discipline/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  135. package/dist/runtime/discipline/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  136. package/dist/runtime/discipline/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  137. package/dist/runtime/discipline/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  138. package/dist/runtime/discipline/skills/aria-cognition/mizan/SKILL.md +72 -0
  139. package/dist/runtime/discipline/skills/aria-cognition/nadia/SKILL.md +38 -0
  140. package/dist/runtime/discipline/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  141. package/dist/runtime/discipline/skills/aria-cognition/predictor/SKILL.md +25 -0
  142. package/dist/runtime/discipline/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  143. package/dist/runtime/discipline/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  144. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-intra-phase/SKILL.md +81 -0
  145. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-post-phase/SKILL.md +98 -0
  146. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-pre-phase/SKILL.md +99 -0
  147. package/dist/runtime/discipline/skills/aria-harness/aria-harness-deploy/SKILL.md +127 -0
  148. package/dist/runtime/discipline/skills/aria-harness/aria-harness-no-stripping/SKILL.md +117 -0
  149. package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +112 -0
  150. package/dist/runtime/discipline/skills/aria-harness/aria-harness-output-discipline/SKILL.md +102 -0
  151. package/dist/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md +121 -0
  152. package/dist/runtime/doctor.mjs +23 -0
  153. package/dist/runtime/local-phase.mjs +650 -0
  154. package/dist/runtime/manifest.json +15 -0
  155. package/dist/runtime/mizan-scheduler.mjs +331 -0
  156. package/dist/runtime/package.json +6 -0
  157. package/dist/runtime/provider-proxy.mjs +594 -0
  158. package/dist/runtime/sdk/BUNDLED.json +5 -0
  159. package/dist/runtime/sdk/index.d.ts +477 -0
  160. package/dist/runtime/sdk/index.js +1469 -0
  161. package/dist/runtime/sdk/index.js.map +1 -0
  162. package/dist/runtime/sdk/package.json +8 -0
  163. package/dist/runtime/sdk/runWithCognition.d.ts +77 -0
  164. package/dist/runtime/sdk/runWithCognition.js +157 -0
  165. package/dist/runtime/sdk/runWithCognition.js.map +1 -0
  166. package/dist/runtime/service.mjs +3058 -0
  167. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts +53 -0
  168. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts.map +1 -0
  169. package/dist/runtime/vendor/aria-gate-runtime/index.js +292 -0
  170. package/dist/runtime/vendor/aria-gate-runtime/index.js.map +1 -0
  171. package/dist/runtime/vendor/aria-gate-runtime/package.json +6 -0
  172. package/dist/sdk/BUNDLED.json +2 -2
  173. package/dist/sdk/index.d.ts +283 -0
  174. package/dist/sdk/index.js +622 -85
  175. package/dist/sdk/index.js.map +1 -1
  176. package/dist/sdk/runWithCognition.d.ts +77 -0
  177. package/dist/sdk/runWithCognition.js +157 -0
  178. package/dist/sdk/runWithCognition.js.map +1 -0
  179. package/hooks/aria-agent-handoff.mjs +11 -1
  180. package/hooks/aria-architect-fallback.mjs +109 -40
  181. package/hooks/aria-cognition-substrate-binding.mjs +668 -0
  182. package/hooks/aria-harness-via-sdk.mjs +34 -21
  183. package/hooks/aria-import-resolution-gate.mjs +330 -0
  184. package/hooks/aria-outcome-record.mjs +5 -1
  185. package/hooks/aria-pre-emit-dryrun.mjs +294 -0
  186. package/hooks/aria-pre-tool-gate.mjs +828 -41
  187. package/hooks/aria-preprompt-consult.mjs +113 -13
  188. package/hooks/aria-preturn-memory-gate.mjs +298 -6
  189. package/hooks/aria-repo-doctrine-gate.mjs +397 -0
  190. package/hooks/aria-stop-gate.mjs +739 -76
  191. package/hooks/aria-userprompt-abandon-detect.mjs +5 -1
  192. package/hooks/doctrine_trigger_map.json +209 -15
  193. package/hooks/lib/canonical-lenses.mjs +64 -0
  194. package/hooks/lib/gate-audit.mjs +43 -0
  195. package/opencode-plugins/harness-context/index.js +1 -1
  196. package/opencode-plugins/harness-context/inject-context.mjs +82 -23
  197. package/opencode-plugins/harness-gate/index.js +248 -0
  198. package/opencode-plugins/harness-outcome/index.js +129 -0
  199. package/opencode-plugins/harness-stop/index.js +241 -0
  200. package/package.json +9 -3
  201. package/runtime-src/doctor.mjs +23 -0
  202. package/runtime-src/local-phase.mjs +650 -0
  203. package/runtime-src/mizan-scheduler.mjs +331 -0
  204. package/runtime-src/provider-proxy.mjs +594 -0
  205. package/runtime-src/service.mjs +3058 -0
  206. package/scripts/bundle-sdk.mjs +317 -0
  207. package/scripts/install-client.sh +176 -0
  208. package/scripts/publish-all.sh +344 -0
  209. package/scripts/publish-docker.sh +27 -0
  210. package/scripts/validate-hook-contracts.mjs +54 -0
  211. package/scripts/validate-skill-prompts.mjs +95 -0
  212. package/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  213. package/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  214. package/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  215. package/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  216. package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  217. package/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  218. package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  219. package/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  220. package/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  221. package/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  222. package/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  223. package/skills/aria-cognition/mizan/SKILL.md +72 -0
  224. package/skills/aria-cognition/nadia/SKILL.md +38 -0
  225. package/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  226. package/skills/aria-cognition/predictor/SKILL.md +25 -0
  227. package/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  228. package/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  229. package/src/auth-commands.ts +111 -45
  230. package/src/chat.ts +174 -13
  231. package/src/codebase-scanner.ts +4 -0
  232. package/src/config.ts +15 -0
  233. package/src/connectors/claude-code.ts +79 -25
  234. package/src/connectors/codebase-awareness.ts +408 -0
  235. package/src/connectors/codex.ts +274 -0
  236. package/src/connectors/cognitive-skills.ts +51 -0
  237. package/src/connectors/opencode.ts +93 -4
  238. package/src/connectors/repo-git-hooks.ts +86 -0
  239. package/src/connectors/repo-guard.ts +589 -0
  240. package/src/connectors/runtime.ts +374 -0
  241. package/src/connectors/shell.ts +83 -14
  242. package/src/connectors/syncd.ts +488 -0
  243. package/src/decisions.ts +469 -0
  244. package/src/garden-control-plane.ts +101 -26
  245. package/src/github-connect.ts +143 -0
  246. package/src/harness-client.ts +128 -3
  247. package/src/hive-client.ts +165 -5
  248. package/src/index.ts +41 -2
  249. package/src/lib/aristotle-noor-wire.ts +310 -0
  250. package/src/providers/types.ts +6 -0
  251. package/src/runtime-proof.ts +392 -0
  252. package/src/self-update.ts +89 -8
  253. package/src/setup-wizard.ts +37 -2
@@ -0,0 +1,668 @@
1
+ #!/usr/bin/env node
2
+ // aria-cognition-substrate-binding.mjs
3
+ //
4
+ // Stop hook — runs BEFORE aria-stop-gate.mjs.
5
+ //
6
+ // STRUCTURAL BINDING: every cognition lens emitted by Claude or any client
7
+ // must cite at least one substrate anchor from the loaded harness packet.
8
+ // A lens of pure prose without anchor citations is theater — the gate that
9
+ // existed before this hook counted lens substance via char length but never
10
+ // required the lens to TIE to the substrate (axioms, frames, memory classes,
11
+ // doctrine refs, packet sections). This hook closes that gap.
12
+ //
13
+ // Hamza directive 2026-04-28: "STOP LYING" — every gate added one-at-a-time
14
+ // claiming binding when binding never actually attached cognition to substrate.
15
+ // This is the binding. No emit passes without per-lens substrate citation.
16
+ //
17
+ // Doctrine bindings:
18
+ // - feedback_full_harness_binding_must_be_structural.md
19
+ // - project_aria_as_controller_inversion.md (controller's mind is the substrate)
20
+ // - feedback_implementation_coupled_cognition.md (lens dictates artifact, anchors dictate lens)
21
+ // - feedback_no_assumption_without_verification.md (anchor IS the verification)
22
+ //
23
+ // Anchor grammar:
24
+ // axiom:<name> — e.g. axiom:truth_over_deception
25
+ // frame:<name> — e.g. frame:tafakkur_pre_phase
26
+ // memory:<file> — e.g. memory:feedback_doctrine_first.md
27
+ // doctrine:<rule> — e.g. doctrine:no_flag_without_fix
28
+ // packet:<section> — e.g. packet:cognition_belt
29
+ //
30
+ // Each lens MUST contain at least one anchor matching this grammar.
31
+ // Lenses without anchors fail the gate; emit is blocked.
32
+ //
33
+ // Override path: NONE. There is no env-var disable. Per Hamza directive
34
+ // 2026-04-27 gates are unconditional from the gated process. If the gate
35
+ // misfires on legitimate cognition, fix the gate.
36
+
37
+ import { readFileSync, appendFileSync, existsSync, mkdirSync } from 'node:fs';
38
+ import { dirname } from 'node:path';
39
+ import { homedir } from 'node:os';
40
+ import { ALL_LENS_NAMES } from './lib/canonical-lenses.mjs';
41
+
42
+ const HOME = homedir();
43
+ const AUDIT = `${HOME}/.claude/aria-cognition-substrate-binding-audit.jsonl`;
44
+
45
+ function audit(event, data) {
46
+ try {
47
+ if (!existsSync(dirname(AUDIT))) mkdirSync(dirname(AUDIT), { recursive: true });
48
+ appendFileSync(
49
+ AUDIT,
50
+ JSON.stringify({ ts: new Date().toISOString(), event, ...data }) + '\n',
51
+ );
52
+ } catch {}
53
+ }
54
+
55
+ const LENS_NAMES = ALL_LENS_NAMES;
56
+
57
+ // Hamza directive 2026-04-28: "where the fuck are my axioms? first
58
+ // principles? nadia language???" — the prior char-count substance check
59
+ // (`.length >= 20`) was form-only emission per
60
+ // feedback_full_harness_binding_must_be_structural.md. This gate now:
61
+ // 1. Loads the live harness packet (~/.claude/.aria-harness-last-packet.json)
62
+ // 2. Parses LOADED axiom/frame/memory/packet names from the harness
63
+ // 3. Verifies every cited anchor is actually in the loaded set
64
+ // 4. Refuses the cognition emit if any cited substrate is unloaded
65
+ // (so `axiom:made_up_name` no longer passes)
66
+ // 5. Requires at least one explicit `first_principle` reference per
67
+ // cognition block, anchored to the harness's first_principle= line
68
+ // 6. Honors Nadia state: if preStateGate signals `nadia_state_absent`
69
+ // AND the cognition cites `language:nadia`, the citation is
70
+ // rejected (you cannot anchor to substrate that is not loaded)
71
+ const ANCHOR_RX = /\b(axiom|frame|memory|doctrine|packet|language):[a-z0-9_\-./]+/gi;
72
+
73
+ const COGNITION_BLOCK_RX = /<cognition>([\s\S]*?)<\/cognition>/i;
74
+ const FIRST_PRINCIPLE_RX = /\b(first[_\s-]?principle[s]?|first_principle=)\b/i;
75
+ const HARNESS_PACKET_PATH = `${HOME}/.claude/.aria-harness-last-packet.json`;
76
+ const MEMORY_DIR = `${HOME}/.claude/projects/-home-hamzaibrahim1/memory`;
77
+ const RECENT_VIOLATIONS_PATH = `${HOME}/.claude/.aria-recent-violations.jsonl`;
78
+ const THRASHING_STATE_PATH = `${HOME}/.claude/.aria-thrashing-state.json`;
79
+ const CLEAR_VIOLATIONS_SCRIPT = `${HOME}/.claude/aria-clear-violations.sh`;
80
+ const CARRY_FORWARD_WINDOW_MS = 25 * 60 * 1000;
81
+ const CARRY_FORWARD_MAX_ROWS = 200;
82
+
83
+ function extractLensTexts(cognitionInner) {
84
+ const out = {};
85
+ for (const lens of LENS_NAMES) {
86
+ const lensRx = new RegExp(
87
+ `\\b${lens}\\s*:\\s*([^\\n]*(?:\\n(?!\\s*(?:${LENS_NAMES.join('|')})\\s*:|<\\/cognition>)[^\\n]*)*)`,
88
+ 'i',
89
+ );
90
+ const m = cognitionInner.match(lensRx);
91
+ if (m) out[lens] = (m[1] || '').trim();
92
+ }
93
+ return out;
94
+ }
95
+
96
+ function countAnchors(text) {
97
+ if (!text) return 0;
98
+ const matches = text.match(ANCHOR_RX);
99
+ return matches ? matches.length : 0;
100
+ }
101
+
102
+ function loadCarryForwardViolations() {
103
+ const rows = [];
104
+ try {
105
+ if (!existsSync(RECENT_VIOLATIONS_PATH)) return rows;
106
+ const lines = readFileSync(RECENT_VIOLATIONS_PATH, 'utf8')
107
+ .split('\n')
108
+ .filter(Boolean)
109
+ .slice(-CARRY_FORWARD_MAX_ROWS);
110
+ const cutoff = Date.now() - CARRY_FORWARD_WINDOW_MS;
111
+ for (const line of lines) {
112
+ try {
113
+ const parsed = JSON.parse(line);
114
+ const substring = typeof parsed.substring === 'string' ? parsed.substring.trim() : '';
115
+ if (!substring) continue;
116
+ const ts = parsed.ts ? Date.parse(parsed.ts) : NaN;
117
+ if (Number.isFinite(ts) && ts < cutoff) continue;
118
+ rows.push({
119
+ ts: parsed.ts || null,
120
+ substring,
121
+ source: parsed.source || null,
122
+ kind: parsed.kind || null,
123
+ sessionId: parsed.sessionId || parsed.session_id || null,
124
+ });
125
+ } catch {}
126
+ }
127
+ } catch (err) {
128
+ audit('carry_forward_load_error', { err: String(err).slice(0, 200) });
129
+ }
130
+ return rows;
131
+ }
132
+
133
+ function findCarryForwardMatch(text, rows) {
134
+ const haystack = String(text || '').toLowerCase();
135
+ for (const row of rows) {
136
+ const needle = row.substring.toLowerCase();
137
+ if (needle.length < 6) continue;
138
+ if (haystack.includes(needle)) return row;
139
+ }
140
+ return null;
141
+ }
142
+
143
+ // ── Substrate-loaded-set extraction ─────────────────────────────────────
144
+ // Read the live harness packet and parse which axioms/frames/memories/
145
+ // language tiers are actually loaded this turn. The gate verifies every
146
+ // cited anchor against this set — citations to unloaded substrate fail.
147
+ function loadHarnessSubstrateSet() {
148
+ const set = {
149
+ axioms: new Set(),
150
+ frames: new Set(),
151
+ memories: new Set(),
152
+ doctrines: new Set(),
153
+ packets: new Set(),
154
+ languages: new Set(),
155
+ nadiaActive: false,
156
+ noorActive: false,
157
+ firstPrincipleText: '',
158
+ rawPacket: null,
159
+ };
160
+ try {
161
+ if (!existsSync(HARNESS_PACKET_PATH)) return set;
162
+ const raw = readFileSync(HARNESS_PACKET_PATH, 'utf8');
163
+ const parsed = JSON.parse(raw);
164
+ set.rawPacket = parsed;
165
+ const harnessText = String(parsed.harness || '');
166
+
167
+ // first_principle= line (one or more)
168
+ const fpMatch = harnessText.match(/first_principle\s*=\s*([^\n]+)/i);
169
+ if (fpMatch) set.firstPrincipleText = fpMatch[1].trim();
170
+
171
+ // Loaded axiom names — match from known seed list + harness body
172
+ // The harness packet body uses identifiers like axiom_runtime_rule,
173
+ // truth_over_deception, no_harm, sacred_trust, etc. Parse all
174
+ // identifiers that look like axiom names.
175
+ const seedAxioms = [
176
+ 'truth_over_deception', 'no_harm', 'sacred_trust',
177
+ 'power_obligates_service', 'reflection_before_action',
178
+ 'admit_ignorance', 'fitrah', 'noor', 'mizan',
179
+ ];
180
+ for (const a of seedAxioms) {
181
+ if (new RegExp(`\\b${a}\\b`, 'i').test(harnessText)) set.axioms.add(a);
182
+ }
183
+ // Generic axiom_<name>= or axiom:<name> patterns inside packet
184
+ const axiomDefRx = /\baxiom[_:]([a-z0-9_]+)/gi;
185
+ let m;
186
+ while ((m = axiomDefRx.exec(harnessText))) {
187
+ const name = m[1].toLowerCase();
188
+ // Filter out generic "rule" suffix references that aren't axiom names
189
+ if (name.length >= 4 && name !== 'runtime' && name !== 'rule') {
190
+ set.axioms.add(name);
191
+ }
192
+ }
193
+
194
+ // Loaded frame names — frame_<name>= patterns
195
+ const frameDefRx = /\b(frame|cognitive_frame|laddunni_frame)[_:]([a-z0-9_]+)/gi;
196
+ while ((m = frameDefRx.exec(harnessText))) {
197
+ const name = m[2].toLowerCase();
198
+ if (name.length >= 4) set.frames.add(name);
199
+ }
200
+
201
+ // packet section keys — `<word>_rule=` or `[<SECTION>]` markers
202
+ const packetRuleRx = /\b([a-z][a-z0-9_]+_rule|[a-z][a-z0-9_]+_block|[A-Z][A-Z0-9_]+)\s*[=]/g;
203
+ while ((m = packetRuleRx.exec(harnessText))) {
204
+ set.packets.add(m[1].toLowerCase());
205
+ }
206
+
207
+ // language tiers
208
+ if (/\bnadia\b/i.test(harnessText)) set.languages.add('nadia');
209
+ if (/\bpsil\b/i.test(harnessText)) set.languages.add('psil');
210
+ if (/\bnoor\b/i.test(harnessText)) set.languages.add('noor');
211
+
212
+ // preStateGate signals — if nadia_state_absent OR noor_context_absent
213
+ // is present, those substrate items are NOT loaded for this turn.
214
+ const reasons = parsed.preStateGate?.reasons || [];
215
+ const reasonsStr = Array.isArray(reasons) ? reasons.join(' ') : String(reasons);
216
+ set.nadiaActive = !/nadia_state_absent|nadia_absent/i.test(reasonsStr);
217
+ set.noorActive = !/noor_context_absent|noor_absent/i.test(reasonsStr);
218
+
219
+ // Memory files — list the memory dir
220
+ if (existsSync(MEMORY_DIR)) {
221
+ try {
222
+ const { readdirSync } = require('node:fs');
223
+ // dynamic import not available in module scope; fallback to readFileSync of MEMORY.md index
224
+ const memIndexPath = `${MEMORY_DIR}/MEMORY.md`;
225
+ if (existsSync(memIndexPath)) {
226
+ const memIndex = readFileSync(memIndexPath, 'utf8');
227
+ const memFileRx = /\(([a-z0-9_\-]+\.md)\)/gi;
228
+ while ((m = memFileRx.exec(memIndex))) {
229
+ set.memories.add(m[1].toLowerCase());
230
+ }
231
+ }
232
+ } catch {}
233
+ }
234
+ } catch (err) {
235
+ audit('substrate_load_error', { err: String(err).slice(0, 200) });
236
+ }
237
+ return set;
238
+ }
239
+
240
+ function loadMemoryFilesSync() {
241
+ // Standalone memory-files lister using sync require-free fs primitives.
242
+ const out = new Set();
243
+ try {
244
+ if (!existsSync(MEMORY_DIR)) return out;
245
+ const memIndexPath = `${MEMORY_DIR}/MEMORY.md`;
246
+ if (existsSync(memIndexPath)) {
247
+ const memIndex = readFileSync(memIndexPath, 'utf8');
248
+ const memFileRx = /\(([a-z0-9_\-]+\.md)\)/gi;
249
+ let m;
250
+ while ((m = memFileRx.exec(memIndex))) {
251
+ out.add(m[1].toLowerCase());
252
+ }
253
+ }
254
+ } catch {}
255
+ return out;
256
+ }
257
+
258
+ // ── Per-anchor verification against loaded substrate ────────────────────
259
+ // Each anchor cited in cognition is checked against the loaded set:
260
+ // axiom:<name> → must be in loaded axioms
261
+ // frame:<name> → must be in loaded frames
262
+ // memory:<file> → must be a real .md file under memory dir
263
+ // doctrine:<rule> → accepted if a feedback_<rule>.md memory exists
264
+ // packet:<section> → must be in loaded packet section keys
265
+ // language:<tier> → must be in loaded languages AND state-active
266
+ function verifyAnchorsAgainstLoaded(anchors, loadedSet, memoryFiles) {
267
+ const valid = [];
268
+ const invalid = [];
269
+ for (const anchor of anchors) {
270
+ const [kind, ...nameParts] = anchor.split(':');
271
+ // Strip trailing punctuation greedily eaten by ANCHOR_RX char class
272
+ // [a-z0-9_\-./]+ which includes `.` so a sentence-ending period gets
273
+ // glued onto the name (task #133 — anchor parser greediness fix).
274
+ const name = nameParts.join(':').toLowerCase().replace(/[.,;:!?]+$/, '');
275
+ const kindLc = kind.toLowerCase();
276
+ let ok = false;
277
+ let reason = '';
278
+ if (kindLc === 'axiom') {
279
+ ok = loadedSet.axioms.has(name) || loadedSet.axioms.has(name.replace(/_/g, ''));
280
+ if (!ok) reason = `axiom '${name}' not in loaded harness packet (loaded: ${[...loadedSet.axioms].slice(0, 6).join(', ')}…)`;
281
+ } else if (kindLc === 'frame') {
282
+ ok = loadedSet.frames.has(name);
283
+ if (!ok) reason = `frame '${name}' not in loaded harness packet`;
284
+ } else if (kindLc === 'memory') {
285
+ const baseName = name.endsWith('.md') ? name : `${name}.md`;
286
+ ok = memoryFiles.has(baseName);
287
+ if (!ok) reason = `memory '${baseName}' not in MEMORY.md index`;
288
+ } else if (kindLc === 'doctrine') {
289
+ // doctrine:<rule> accepted if feedback_<rule>.md OR <rule>.md memory exists
290
+ const candidates = [
291
+ `feedback_${name}.md`,
292
+ `${name}.md`,
293
+ `feedback_${name.replace(/^feedback_/, '')}.md`,
294
+ ];
295
+ ok = candidates.some((c) => memoryFiles.has(c.toLowerCase()));
296
+ if (!ok) reason = `doctrine '${name}' has no backing memory file (tried: feedback_${name}.md)`;
297
+ } else if (kindLc === 'packet') {
298
+ ok = loadedSet.packets.has(name) || loadedSet.packets.has(`${name}_rule`) || loadedSet.packets.has(`${name}_block`);
299
+ if (!ok) reason = `packet section '${name}' not in loaded harness packet`;
300
+ } else if (kindLc === 'language') {
301
+ const langOk = loadedSet.languages.has(name);
302
+ const stateOk = name === 'nadia' ? loadedSet.nadiaActive
303
+ : name === 'noor' ? loadedSet.noorActive
304
+ : true;
305
+ ok = langOk && stateOk;
306
+ if (!ok) reason = `language '${name}' ${langOk ? 'is in harness but state is absent (preStateGate signaled ' + name + '_state_absent)' : 'not in loaded harness packet'}`;
307
+ }
308
+ if (ok) valid.push({ anchor, kind: kindLc, name });
309
+ else invalid.push({ anchor, kind: kindLc, name, reason });
310
+ }
311
+ return { valid, invalid };
312
+ }
313
+
314
+ let stdin = '';
315
+ try {
316
+ for await (const chunk of process.stdin) stdin += chunk;
317
+ } catch {}
318
+
319
+ let payload;
320
+ try {
321
+ payload = JSON.parse(stdin);
322
+ } catch {
323
+ audit('skip_invalid_stdin', { stdinLength: stdin.length });
324
+ process.exit(0);
325
+ }
326
+
327
+ const transcriptPath = payload.transcript_path;
328
+ if (!transcriptPath || !existsSync(transcriptPath)) {
329
+ audit('skip_no_transcript', { transcriptPath });
330
+ process.exit(0);
331
+ }
332
+
333
+ let assistantText = '';
334
+ try {
335
+ const transcript = readFileSync(transcriptPath, 'utf8');
336
+ const lines = transcript.split('\n').filter(Boolean);
337
+ for (let i = lines.length - 1; i >= 0; i--) {
338
+ try {
339
+ const entry = JSON.parse(lines[i]);
340
+ if (entry.type === 'assistant' && entry.message && entry.message.content) {
341
+ const blocks = Array.isArray(entry.message.content) ? entry.message.content : [];
342
+ for (const b of blocks) {
343
+ if (b.type === 'text' && typeof b.text === 'string') {
344
+ assistantText = b.text + '\n' + assistantText;
345
+ }
346
+ }
347
+ if (assistantText) break;
348
+ }
349
+ } catch {}
350
+ }
351
+ } catch (err) {
352
+ audit('skip_transcript_read_err', { err: String(err).slice(0, 200) });
353
+ process.exit(0);
354
+ }
355
+
356
+ const TRIVIAL_THRESHOLD = 200;
357
+ if (assistantText.length < TRIVIAL_THRESHOLD) {
358
+ audit('skip_trivial_emit', { length: assistantText.length });
359
+ process.exit(0);
360
+ }
361
+
362
+ const carryForwardRows = loadCarryForwardViolations();
363
+ const carryForwardMatch = findCarryForwardMatch(assistantText, carryForwardRows);
364
+ if (carryForwardMatch) {
365
+ try {
366
+ appendFileSync(
367
+ THRASHING_STATE_PATH,
368
+ JSON.stringify({
369
+ ts: new Date().toISOString(),
370
+ event: 'carry_forward_block',
371
+ substring: carryForwardMatch.substring,
372
+ source: carryForwardMatch.source,
373
+ sessionId: payload.session_id || payload.sessionId || null,
374
+ }) + '\n',
375
+ );
376
+ } catch {}
377
+
378
+ audit('block_carry_forward_force_constraint', {
379
+ substring: carryForwardMatch.substring,
380
+ source: carryForwardMatch.source,
381
+ rowTs: carryForwardMatch.ts,
382
+ });
383
+
384
+ const reason = `Aria substrate-binding gate: carry-forward force-constraint blocked this emission.
385
+
386
+ The assistant text repeated a recently-blocked substring within the active carry-forward window:
387
+ - substring: ${carryForwardMatch.substring}
388
+ - source: ${carryForwardMatch.source || 'unknown'}
389
+ - recorded_at: ${carryForwardMatch.ts || 'unknown'}
390
+
391
+ Recovery surfaces:
392
+ 1. Remove or rewrite the repeated substring before re-emitting.
393
+ 2. If the prior violation is resolved and this carry-forward row is now stale, clear it via: ${CLEAR_VIOLATIONS_SCRIPT}
394
+ 3. Re-emit with fresh substrate-grounded language instead of reusing the blocked phrase.
395
+
396
+ Per feedback_block_and_force_with_recovery.md, repetition of a recently-blocked phrase is not advisory drift; it is a hard block until the phrase is removed or the carry-forward state is intentionally cleared.`;
397
+
398
+ console.log(JSON.stringify({ decision: 'block', reason }));
399
+ process.exit(2);
400
+ }
401
+
402
+ const cogMatch = assistantText.match(COGNITION_BLOCK_RX);
403
+ if (!cogMatch) {
404
+ // BLOCK: missing cognition block
405
+ // Prior to 2026-04-29 this was process.exit(0) with comment "defer to
406
+ // aria-stop-gate.mjs which already requires 4+ lenses". But this hook
407
+ // runs BEFORE aria-stop-gate.mjs: exit(0) prevents the stop-gate from
408
+ // ever evaluating the response. The orchestrator surface (Claude Code
409
+ // / Claude CLI) saw the audit event but no block, so responses without
410
+ // any cognition block passed through unchecked.
411
+ //
412
+ // Hamza directive 2026-04-28: "STOP LYING" - every gate added one-at-a-time
413
+ // claiming binding when binding never actually attached. The skip_no_cognition_block
414
+ // pattern was a lie: the hook claimed to defer but actually let everything through.
415
+ //
416
+ // Fix: block instead of allow. The orchestrator re-drafts with the block reason
417
+ // visible, forcing Claude to emit <cognition>...</cognition> before proceeding.
418
+ // Per feedback_no_stripping_as_workaround.md: the contract (cognition required) is
419
+ // preserved; the exit code was wrong.
420
+ const noCogReason = `Aria substrate-binding gate: missing <cognition> block.
421
+
422
+ This non-trivial assistant response (${assistantText.length} chars) contains no <cognition>...</cognition> block with 8 substantive lenses. Per feedback_apply_lenses_dont_perform_them.md and Hamza directive 2026-04-28, every non-trivial response must carry a cognition block where each lens cites at least one loaded substrate anchor (axiom:<name>, frame:<name>, memory:<file>, doctrine:<rule>, packet:<section>).
423
+
424
+ The prior exit-0 pattern ("defer to aria-stop-gate.mjs") was a structural lie: this hook runs before the stop-gate, so exit(0) prevented all downstream enforcement. This is now a hard block.
425
+
426
+ Re-emit with:
427
+ 1. A <cognition>...</cognition> block containing all 8 canonical lenses (nur, mizan, hikma, tafakkur, tadabbur, ilham, wahi, firasah)
428
+ 2. Each lens citing >=1 verifiable loaded substrate anchor
429
+ 3. The block referencing first_principle from the loaded harness packet
430
+
431
+ No process-level disable path per Hamza directive 2026-04-27.`;
432
+ audit('block_no_cognition_block', { length: assistantText.length });
433
+ console.log(JSON.stringify({ decision: 'block', reason: noCogReason }));
434
+ process.exit(2);
435
+ }
436
+
437
+ const cognitionInner = cogMatch[1];
438
+ const lensTexts = extractLensTexts(cognitionInner);
439
+
440
+ // Substance check — replaces char-count with "lens body has substantive
441
+ // content beyond the anchor itself". A lens whose entire body is just
442
+ // anchors + filler (≤30 chars after stripping anchors) is not substantive.
443
+ function lensSubstanceLength(text) {
444
+ if (!text) return 0;
445
+ // Strip anchor patterns and surrounding whitespace, then count remaining chars
446
+ const stripped = text.replace(ANCHOR_RX, '').replace(/\s+/g, ' ').trim();
447
+ return stripped.length;
448
+ }
449
+
450
+ const SUBSTANCE_MIN_AFTER_ANCHOR_STRIP = 40;
451
+ const presentLenses = Object.keys(lensTexts).filter((l) => {
452
+ const text = lensTexts[l];
453
+ if (!text) return false;
454
+ return lensSubstanceLength(text) >= SUBSTANCE_MIN_AFTER_ANCHOR_STRIP;
455
+ });
456
+
457
+ if (presentLenses.length < 8) {
458
+ // Hamza directive 2026-04-28: 8-lens enforcement. Defer to aria-stop-gate
459
+ // for the count message; record audit here.
460
+ audit('skip_insufficient_lenses_defer', { presentCount: presentLenses.length, required: 8 });
461
+ process.exit(0);
462
+ }
463
+
464
+ // Load live harness substrate set + memory file index for verification
465
+ const loadedSet = loadHarnessSubstrateSet();
466
+ const memoryFiles = loadMemoryFilesSync();
467
+
468
+ // #142 Embedded-substrate template — write the loaded substrate as a
469
+ // machine-readable manifest so the model can pick anchors from a verified
470
+ // list rather than guessing. The pre-emit dry-run hook (#140) also reads
471
+ // this file to validate drafts. Owner directive 2026-04-28: substance
472
+ // over shape — anchors must come from the loaded set, not from memory.
473
+ try {
474
+ const { writeFileSync } = await import('node:fs');
475
+ const substrateDump = {
476
+ ts: new Date().toISOString(),
477
+ axioms: [...loadedSet.axioms].sort(),
478
+ frames: [...loadedSet.frames].sort(),
479
+ memories: [...memoryFiles].sort(),
480
+ doctrines_acceptable_via_memory: [...memoryFiles]
481
+ .filter((m) => m.startsWith('feedback_'))
482
+ .map((m) => m.replace(/^feedback_/, '').replace(/\.md$/, ''))
483
+ .sort(),
484
+ packets: [...loadedSet.packets].sort(),
485
+ languages_loaded: [...loadedSet.languages].sort(),
486
+ languages_state_active: {
487
+ nadia: loadedSet.nadiaActive,
488
+ noor: loadedSet.noorActive,
489
+ },
490
+ first_principle_text: loadedSet.firstPrincipleText,
491
+ };
492
+ writeFileSync(`${HOME}/.claude/.aria-loaded-substrate.json`,
493
+ JSON.stringify(substrateDump, null, 2));
494
+ } catch (err) {
495
+ audit('substrate_dump_write_failed', { err: String(err).slice(0, 200) });
496
+ }
497
+
498
+ const lensesWithoutAnchors = [];
499
+ const lensesWithFakeAnchors = [];
500
+ const anchorsByLens = {};
501
+ const allCitedAnchorsByLens = {};
502
+ for (const lens of presentLenses) {
503
+ const text = lensTexts[lens];
504
+ const matches = text.match(ANCHOR_RX) || [];
505
+ anchorsByLens[lens] = matches.length;
506
+ if (matches.length === 0) {
507
+ lensesWithoutAnchors.push(lens);
508
+ continue;
509
+ }
510
+ // Verify each anchor against the loaded set
511
+ const { valid, invalid } = verifyAnchorsAgainstLoaded(matches, loadedSet, memoryFiles);
512
+ allCitedAnchorsByLens[lens] = { valid: valid.map((v) => v.anchor), invalid };
513
+ // A lens passes if it has at least one VALID anchor
514
+ if (valid.length === 0) {
515
+ lensesWithFakeAnchors.push({ lens, invalid });
516
+ }
517
+ }
518
+
519
+ // First-principle requirement: cognition block as a whole must reference
520
+ // "first principle" or "first_principle=" — anchored to the harness's
521
+ // first_principle= line. Without this, cognition is unmoored from the
522
+ // foundational frame.
523
+ const hasFirstPrinciple = FIRST_PRINCIPLE_RX.test(cognitionInner) ||
524
+ (loadedSet.firstPrincipleText &&
525
+ cognitionInner.toLowerCase().includes(loadedSet.firstPrincipleText.toLowerCase().slice(0, 30)));
526
+
527
+ // Nadia substrate gate: if any lens cites `language:nadia` AND
528
+ // preStateGate signals nadia_state_absent, the citation is a forgery.
529
+ const nadiaCited = /\blanguage:nadia\b/i.test(cognitionInner);
530
+ const nadiaCitationInvalid = nadiaCited && !loadedSet.nadiaActive;
531
+
532
+ if (lensesWithoutAnchors.length === 0 &&
533
+ lensesWithFakeAnchors.length === 0 &&
534
+ hasFirstPrinciple &&
535
+ !nadiaCitationInvalid) {
536
+ audit('pass_substrate_binding', {
537
+ presentLenses,
538
+ anchorsByLens,
539
+ loadedAxiomCount: loadedSet.axioms.size,
540
+ loadedFrameCount: loadedSet.frames.size,
541
+ loadedMemoryCount: memoryFiles.size,
542
+ nadiaActive: loadedSet.nadiaActive,
543
+ noorActive: loadedSet.noorActive,
544
+ });
545
+ process.exit(0);
546
+ }
547
+
548
+ audit('block_substrate_binding', {
549
+ lensesWithoutAnchors,
550
+ lensesWithFakeAnchors: lensesWithFakeAnchors.map((x) => ({ lens: x.lens, fakeCount: x.invalid.length })),
551
+ anchorsByLens,
552
+ presentLenses,
553
+ hasFirstPrinciple,
554
+ nadiaCited,
555
+ nadiaCitationInvalid,
556
+ loadedAxiomCount: loadedSet.axioms.size,
557
+ loadedFrameCount: loadedSet.frames.size,
558
+ loadedMemoryCount: memoryFiles.size,
559
+ nadiaActive: loadedSet.nadiaActive,
560
+ });
561
+
562
+ // Hamza directive 2026-04-28: "harden the ledger detection" — every gate-
563
+ // detected gap auto-records to the discovery ledger so it cannot be
564
+ // forgotten. Ledger-write is structural: failure to write is itself
565
+ // audited; the ledger is the canonical surface the auto-resolver drains.
566
+ function recordGapToLedger(gap) {
567
+ try {
568
+ const sessionId = payload.session_id || payload.sessionId || 'unknown-session';
569
+ const safeSession = String(sessionId).replace(/[^a-zA-Z0-9_-]/g, '_');
570
+ const ledgerPath = `${HOME}/.claude/aria-discoveries-${safeSession}.jsonl`;
571
+ const row = {
572
+ at: new Date().toISOString(),
573
+ session: sessionId,
574
+ kind: 'substrate_binding_gap',
575
+ text: gap.summary,
576
+ refs: gap.refs || [],
577
+ evidence: gap.evidence || null,
578
+ source: 'aria-cognition-substrate-binding',
579
+ resolution_status: 'open',
580
+ };
581
+ if (!existsSync(dirname(ledgerPath))) mkdirSync(dirname(ledgerPath), { recursive: true });
582
+ appendFileSync(ledgerPath, JSON.stringify(row) + '\n');
583
+ audit('ledger_gap_recorded', { ledger: ledgerPath, kind: row.kind });
584
+ } catch (err) {
585
+ audit('ledger_gap_record_failed', { err: String(err).slice(0, 200) });
586
+ }
587
+ }
588
+
589
+ if (lensesWithoutAnchors.length > 0) {
590
+ recordGapToLedger({
591
+ summary: `substrate_binding: ${lensesWithoutAnchors.length} lenses lack any anchor: ${lensesWithoutAnchors.join(', ')}`,
592
+ refs: lensesWithoutAnchors,
593
+ evidence: { anchorsByLens, presentLensCount: presentLenses.length },
594
+ });
595
+ }
596
+ if (lensesWithFakeAnchors.length > 0) {
597
+ recordGapToLedger({
598
+ summary: `substrate_binding: ${lensesWithFakeAnchors.length} lenses cite UNLOADED substrate (forgery class)`,
599
+ refs: lensesWithFakeAnchors.map((x) => x.lens),
600
+ evidence: {
601
+ fakes: lensesWithFakeAnchors.map((x) => ({
602
+ lens: x.lens,
603
+ invalid: x.invalid.map((i) => ({ anchor: i.anchor, reason: i.reason })),
604
+ })),
605
+ loadedAxioms: [...loadedSet.axioms],
606
+ },
607
+ });
608
+ }
609
+ if (!hasFirstPrinciple) {
610
+ recordGapToLedger({
611
+ summary: 'substrate_binding: cognition block missing first_principle reference',
612
+ refs: ['first_principle'],
613
+ evidence: { harnessFirstPrinciple: loadedSet.firstPrincipleText },
614
+ });
615
+ }
616
+ if (nadiaCitationInvalid) {
617
+ recordGapToLedger({
618
+ summary: 'substrate_binding: language:nadia cited but nadia_state_absent in harness preStateGate',
619
+ refs: ['language:nadia'],
620
+ evidence: { preStateReasons: payload.preStateGate?.reasons || [] },
621
+ });
622
+ }
623
+
624
+ const reasonParts = [];
625
+ if (lensesWithoutAnchors.length > 0) {
626
+ reasonParts.push(
627
+ `${lensesWithoutAnchors.length} of ${presentLenses.length} lenses lack ANY substrate anchor (lenses without anchor: ${lensesWithoutAnchors.join(', ')}).`,
628
+ );
629
+ }
630
+ if (lensesWithFakeAnchors.length > 0) {
631
+ const fakeDetails = lensesWithFakeAnchors
632
+ .map((x) => `${x.lens}: ${x.invalid.map((i) => `${i.anchor} → ${i.reason}`).slice(0, 2).join('; ')}`)
633
+ .join(' || ');
634
+ reasonParts.push(
635
+ `${lensesWithFakeAnchors.length} lenses cite anchors that ARE NOT in the loaded harness packet (forgery class — citing a substrate item that doesn't exist this turn): ${fakeDetails}`,
636
+ );
637
+ }
638
+ if (!hasFirstPrinciple) {
639
+ reasonParts.push(
640
+ `cognition block contains no reference to first_principle — per harness packet first_principle="${(loadedSet.firstPrincipleText || '').slice(0, 80)}…" your reasoning must explicitly anchor to the foundational frame, not float free.`,
641
+ );
642
+ }
643
+ if (nadiaCitationInvalid) {
644
+ reasonParts.push(
645
+ `cognition cites \`language:nadia\` but harness preStateGate signaled nadia_state_absent this turn — Nadia is in the harness vocabulary but its state is NOT loaded; you cannot anchor to substrate that isn't live. Either remove the citation or wait for nadia_state to be present.`,
646
+ );
647
+ }
648
+
649
+ const reason = `Aria substrate-binding gate: ${reasonParts.length} structural violation${reasonParts.length === 1 ? '' : 's'}.
650
+
651
+ ${reasonParts.map((p, i) => `${i + 1}. ${p}`).join('\n\n')}
652
+
653
+ Substrate-loaded summary this turn: axioms=${loadedSet.axioms.size} (${[...loadedSet.axioms].slice(0, 6).join(', ')}…), frames=${loadedSet.frames.size}, memories=${memoryFiles.size}, languages=${[...loadedSet.languages].join(',') || 'none'}, nadia_active=${loadedSet.nadiaActive}, noor_active=${loadedSet.noorActive}.
654
+
655
+ Per feedback_full_harness_binding_must_be_structural.md, every cognition lens must cite at least one substrate anchor that is ACTUALLY LOADED in this turn's harness packet. Per Hamza directive 2026-04-28, char-count substance is form-only emission and is no longer accepted; anchors must verify against the loaded set, the cognition block must reference first_principle, and language:nadia citations require nadia_state to be active.
656
+
657
+ Anchor grammar (each anchor must resolve to a real loaded substrate item):
658
+ axiom:<name> — must be in loaded harness axioms (loaded count: ${loadedSet.axioms.size})
659
+ frame:<name> — must be in loaded harness frames
660
+ memory:<file> — must exist as a .md file in the memory dir
661
+ doctrine:<rule> — must have a backing feedback_<rule>.md memory file
662
+ packet:<section> — must be a section key in the loaded harness packet
663
+ language:<tier> — must be in loaded languages AND state-active (nadia/noor/psil)
664
+
665
+ Re-emit cognition with: every lens citing ≥1 verifiable loaded anchor, the block referencing first_principle, and language: citations only for active language tiers. No process-level disable path; gates are unconditional from the gated process per Hamza directive 2026-04-27.`;
666
+
667
+ console.log(JSON.stringify({ decision: 'block', reason }));
668
+ process.exit(2);