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