@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,245 @@
1
+ #!/usr/bin/env node
2
+ // Test: aria-preturn-memory-gate.mjs — Enforcement Layer #49
3
+ //
4
+ // Runs the hook as a child process with simulated stdin events.
5
+ // Asserts correct output shape for both the block and allow paths.
6
+ //
7
+ // Usage: node hooks/test-aria-preturn-memory-gate.mjs
8
+ // Exit code: 0 = all assertions passed, 1 = failure
9
+
10
+ import { spawnSync, execSync } from 'node:child_process';
11
+ import { writeFileSync, mkdirSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
12
+ import { tmpdir } from 'node:os';
13
+ import * as path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
17
+ const HOOK = path.join(HERE, 'aria-preturn-memory-gate.mjs');
18
+ const HOME = process.env.HOME || '/tmp';
19
+ const CLAUDE_DIR = `${HOME}/.claude`;
20
+ const SESSION_ID = `test-preturn-${Date.now()}`;
21
+
22
+ // ── Helpers ────────────────────────────────────────────────────────────
23
+
24
+ let passed = 0;
25
+ let failed = 0;
26
+
27
+ function assert(label, condition, actual) {
28
+ if (condition) {
29
+ console.log(` PASS ${label}`);
30
+ passed++;
31
+ } else {
32
+ console.error(` FAIL ${label}`);
33
+ if (actual !== undefined) console.error(` got: ${JSON.stringify(actual)}`);
34
+ failed++;
35
+ }
36
+ }
37
+
38
+ function turnStatePath(sid) {
39
+ const safe = String(sid).replace(/[^a-zA-Z0-9_-]/g, '_');
40
+ return `${CLAUDE_DIR}/aria-turn-state-${safe}.json`;
41
+ }
42
+
43
+ function cleanTurnState(sid) {
44
+ const p = turnStatePath(sid);
45
+ if (existsSync(p)) unlinkSync(p);
46
+ }
47
+
48
+ function buildEvent(overrides = {}) {
49
+ return {
50
+ tool_name: 'Bash',
51
+ tool_input: { command: 'ls .' },
52
+ session_id: SESSION_ID,
53
+ transcript_path: null,
54
+ ...overrides,
55
+ };
56
+ }
57
+
58
+ function buildTranscriptWith(lines, filePath) {
59
+ const dir = path.dirname(filePath);
60
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
61
+ writeFileSync(filePath, lines.map((l) => JSON.stringify(l)).join('\n') + '\n', 'utf-8');
62
+ }
63
+
64
+ function runHook(eventObj, env = {}) {
65
+ const result = spawnSync('node', [HOOK], {
66
+ input: JSON.stringify(eventObj),
67
+ encoding: 'utf-8',
68
+ env: { ...process.env, HOME, ...env },
69
+ });
70
+ let parsed = null;
71
+ try { parsed = JSON.parse(result.stdout.trim()); } catch { /* non-JSON stdout */ }
72
+ return { exitCode: result.status, stdout: result.stdout, stderr: result.stderr, parsed };
73
+ }
74
+
75
+ // ── Setup ──────────────────────────────────────────────────────────────
76
+
77
+ if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
78
+
79
+ // ── Test 1: no transcript → gate fires, block + recovery payload ───────
80
+ console.log('\nTest 1: no transcript (no context signals) → block with recovery');
81
+ {
82
+ cleanTurnState(SESSION_ID);
83
+ const event = buildEvent({ transcript_path: null });
84
+ const { exitCode, parsed } = runHook(event);
85
+
86
+ assert('exit code is 2 (block)', exitCode === 2, exitCode);
87
+ assert('decision is "block"', parsed?.decision === 'block', parsed?.decision);
88
+ assert('reason is a non-empty string', typeof parsed?.reason === 'string' && parsed.reason.length > 0, parsed?.reason);
89
+ assert(
90
+ 'hookSpecificOutput.recovery.action is "run_context_loader"',
91
+ parsed?.hookSpecificOutput?.recovery?.action === 'run_context_loader',
92
+ parsed?.hookSpecificOutput?.recovery?.action,
93
+ );
94
+ assert(
95
+ 'hookSpecificOutput.recovery.target is session ID',
96
+ parsed?.hookSpecificOutput?.recovery?.target === SESSION_ID,
97
+ parsed?.hookSpecificOutput?.recovery?.target,
98
+ );
99
+ assert(
100
+ 'hookSpecificOutput.recovery.expectedContext includes "harness_packet"',
101
+ Array.isArray(parsed?.hookSpecificOutput?.recovery?.expectedContext) &&
102
+ parsed.hookSpecificOutput.recovery.expectedContext.includes('harness_packet'),
103
+ parsed?.hookSpecificOutput?.recovery?.expectedContext,
104
+ );
105
+ assert(
106
+ 'hookSpecificOutput.recovery.expectedContext includes "memory_files"',
107
+ Array.isArray(parsed?.hookSpecificOutput?.recovery?.expectedContext) &&
108
+ parsed.hookSpecificOutput.recovery.expectedContext.includes('memory_files'),
109
+ parsed?.hookSpecificOutput?.recovery?.expectedContext,
110
+ );
111
+ assert(
112
+ 'hookSpecificOutput.recovery.expectedContext includes "binding_plan"',
113
+ Array.isArray(parsed?.hookSpecificOutput?.recovery?.expectedContext) &&
114
+ parsed.hookSpecificOutput.recovery.expectedContext.includes('binding_plan'),
115
+ parsed?.hookSpecificOutput?.recovery?.expectedContext,
116
+ );
117
+ }
118
+
119
+ // ── Test 2: transcript WITH all three signals → allow ─────────────────
120
+ console.log('\nTest 2: transcript with all three context signals → allow');
121
+ {
122
+ cleanTurnState(SESSION_ID);
123
+ const transcriptFile = path.join(tmpdir(), `test-transcript-preturn-${Date.now()}.jsonl`);
124
+
125
+ const assistantTextWithAllSignals = [
126
+ '🔐 Aria Harness — Session Packet loaded for turn.',
127
+ '[ARIA_DIRECTION] Proceed with implementation of Phase 8 research-first context injection.',
128
+ '<cognition>',
129
+ ' nur: Current task is implementing the pre-turn memory gate per feedback_no_graceful_degradation.md and project_aria_as_controller_inversion.md.',
130
+ ' mizan: Risk is that blocking without recovery creates dead-letter state. Soft-gate pattern resolves this.',
131
+ ' hikma: feedback_no_flag_without_fix.md requires inline fix for any discovery.',
132
+ ' tafakkur: The gate must deduplicate within 60s to prevent orchestrator retry loops.',
133
+ '</cognition>',
134
+ ].join('\n');
135
+
136
+ buildTranscriptWith(
137
+ [
138
+ {
139
+ role: 'user',
140
+ message: {
141
+ role: 'user',
142
+ content: [{ type: 'text', text: 'Implement phase 8.' }],
143
+ },
144
+ },
145
+ {
146
+ role: 'assistant',
147
+ message: {
148
+ role: 'assistant',
149
+ content: [{ type: 'text', text: assistantTextWithAllSignals }],
150
+ },
151
+ },
152
+ ],
153
+ transcriptFile,
154
+ );
155
+
156
+ const event = buildEvent({ transcript_path: transcriptFile });
157
+ const { exitCode, parsed } = runHook(event);
158
+
159
+ assert('exit code is 0 (allow)', exitCode === 0, exitCode);
160
+ assert('no block decision in stdout (stdout is empty or non-block)', !parsed || parsed.decision !== 'block', parsed?.decision);
161
+
162
+ // Cleanup
163
+ try { unlinkSync(transcriptFile); } catch {}
164
+ }
165
+
166
+ // ── Test 3: deduplication — gate already fired within 60s → skip ───────
167
+ console.log('\nTest 3: gate already fired this turn (< 60s ago) → skip (exit 0)');
168
+ {
169
+ // Pre-write a turn-state showing the gate fired 5 seconds ago
170
+ const statePath = turnStatePath(SESSION_ID);
171
+ writeFileSync(statePath, JSON.stringify({ lastTurnGateFiredAt: Date.now() - 5000, lastDecision: 'block', signals: {} }), 'utf-8');
172
+
173
+ const event = buildEvent({ transcript_path: null });
174
+ const { exitCode } = runHook(event);
175
+
176
+ assert('exit code is 0 (dedup skip — no re-fire within 60s)', exitCode === 0, exitCode);
177
+
178
+ cleanTurnState(SESSION_ID);
179
+ }
180
+
181
+ // ── Test 4: non-action tool (Read) → gate skips entirely ───────────────
182
+ console.log('\nTest 4: non-action tool (Read) → gate skips (exit 0)');
183
+ {
184
+ cleanTurnState(SESSION_ID);
185
+ const event = buildEvent({ tool_name: 'Read', transcript_path: null });
186
+ const { exitCode } = runHook(event);
187
+ assert('exit code is 0 (ungated read tool)', exitCode === 0, exitCode);
188
+ }
189
+
190
+ // ── Test 5: REMOVED 2026-04-27 — env-var kill-switch stripped per Hamza
191
+ // directive ("those should've been my choice to give you to turn off
192
+ // not free for you to access"). Gates are unconditional from the gated
193
+ // process. Disable = remove hook entry from ~/.claude/settings.json.
194
+
195
+ // ── Test 6: only ONE signal present → still blocks ────────────────────
196
+ console.log('\nTest 6: only harness packet signal (missing direction + memory ref) → block');
197
+ {
198
+ cleanTurnState(SESSION_ID);
199
+ const transcriptFile = path.join(tmpdir(), `test-transcript-preturn-partial-${Date.now()}.jsonl`);
200
+
201
+ const assistantTextPartial = '🔐 Aria Harness — partial context only. No direction. No memory refs.';
202
+
203
+ buildTranscriptWith(
204
+ [
205
+ {
206
+ role: 'user',
207
+ message: {
208
+ role: 'user',
209
+ content: [{ type: 'text', text: 'Do the thing.' }],
210
+ },
211
+ },
212
+ {
213
+ role: 'assistant',
214
+ message: {
215
+ role: 'assistant',
216
+ content: [{ type: 'text', text: assistantTextPartial }],
217
+ },
218
+ },
219
+ ],
220
+ transcriptFile,
221
+ );
222
+
223
+ const event = buildEvent({ transcript_path: transcriptFile });
224
+ const { exitCode, parsed } = runHook(event);
225
+
226
+ assert('exit code is 2 (block on partial signals)', exitCode === 2, exitCode);
227
+ assert('decision is "block"', parsed?.decision === 'block', parsed?.decision);
228
+ assert(
229
+ 'reason mentions missing aria_direction',
230
+ typeof parsed?.reason === 'string' && parsed.reason.includes('aria_direction'),
231
+ parsed?.reason?.slice(0, 200),
232
+ );
233
+
234
+ try { unlinkSync(transcriptFile); } catch {}
235
+ cleanTurnState(SESSION_ID);
236
+ }
237
+
238
+ // ── Summary ────────────────────────────────────────────────────────────
239
+ console.log(`\n${passed + failed} tests: ${passed} passed, ${failed} failed`);
240
+ if (failed > 0) {
241
+ process.exit(1);
242
+ } else {
243
+ console.log('All assertions passed.');
244
+ process.exit(0);
245
+ }
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env node
2
+ // Smoke test: Phase 11 #59 — tier-aware lens labeling
3
+ //
4
+ // Verifies that aria-pre-tool-gate.mjs and aria-stop-gate.mjs:
5
+ // 1. Show canonical Arabic lens names (nur, mizan, etc.) when the harness
6
+ // packet has isHamza/hamza=true (OWNER tier).
7
+ // 2. Show neutral generic labels (perception, balance, etc.) when the
8
+ // harness packet is client-surface (CLIENT tier).
9
+ // 3. Gate enforcement (block on insufficient cognition) fires in both
10
+ // tiers — label swap does not change gate substance.
11
+ //
12
+ // Usage: node hooks/test-tier-lens-labeling.mjs
13
+ // Exit: 0 = all assertions passed, 1 = failure
14
+
15
+ import { spawnSync } from 'node:child_process';
16
+ import { writeFileSync, mkdirSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
17
+ import * as path from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
21
+ const PRE_TOOL_HOOK = path.join(HERE, 'aria-pre-tool-gate.mjs');
22
+ const STOP_HOOK = path.join(HERE, 'aria-stop-gate.mjs');
23
+ const HOME = process.env.HOME || '/tmp';
24
+ const CLAUDE_DIR = `${HOME}/.claude`;
25
+ const PACKET_PATH = `${CLAUDE_DIR}/.aria-harness-last-packet.json`;
26
+
27
+ // ── Canonical vs generic label sets ────────────────────────────────────────
28
+ const CANONICAL = ['nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah'];
29
+ const GENERIC = ['perception', 'balance', 'wisdom', 'reflection', 'foresight', 'insight', 'revelation', 'discernment'];
30
+
31
+ // ── Assertion helpers ───────────────────────────────────────────────────────
32
+ let passed = 0;
33
+ let failed = 0;
34
+
35
+ function assert(label, condition, detail) {
36
+ if (condition) {
37
+ console.log(` PASS ${label}`);
38
+ passed++;
39
+ } else {
40
+ console.error(` FAIL ${label}`);
41
+ if (detail !== undefined) console.error(` detail: ${String(detail).slice(0, 300)}`);
42
+ failed++;
43
+ }
44
+ }
45
+
46
+ // ── Packet helpers ──────────────────────────────────────────────────────────
47
+ // Save/restore the real packet so running this test doesn't corrupt live state.
48
+ let originalPacketContent = null;
49
+
50
+ function savePacket() {
51
+ try {
52
+ if (existsSync(PACKET_PATH)) {
53
+ originalPacketContent = readFileSync(PACKET_PATH, 'utf8');
54
+ } else {
55
+ originalPacketContent = null;
56
+ }
57
+ } catch { originalPacketContent = null; }
58
+ }
59
+
60
+ function restorePacket() {
61
+ try {
62
+ if (originalPacketContent !== null) {
63
+ writeFileSync(PACKET_PATH, originalPacketContent);
64
+ } else if (existsSync(PACKET_PATH)) {
65
+ unlinkSync(PACKET_PATH);
66
+ }
67
+ } catch {}
68
+ }
69
+
70
+ function writeOwnerPacket() {
71
+ // contractGate.signals.hamza === true → OWNER tier
72
+ const packet = {
73
+ ok: true,
74
+ stage: 'preflight',
75
+ harness: 'surface=platform:hamza group:false hamza:true',
76
+ contractGate: {
77
+ signals: { hamza: true },
78
+ },
79
+ };
80
+ if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
81
+ writeFileSync(PACKET_PATH, JSON.stringify(packet));
82
+ }
83
+
84
+ function writeClientPacket() {
85
+ // contractGate.signals.hamza === "false" (string) → CLIENT tier
86
+ const packet = {
87
+ ok: true,
88
+ stage: 'preflight',
89
+ harness: 'surface=platform:client group:false hamza:false',
90
+ contractGate: {
91
+ signals: { hamza: 'false' },
92
+ },
93
+ };
94
+ if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
95
+ writeFileSync(PACKET_PATH, JSON.stringify(packet));
96
+ }
97
+
98
+ // ── Transcript helpers ──────────────────────────────────────────────────────
99
+ // Write a minimal transcript where the most recent assistant turn has NO
100
+ // cognition block — triggering a block decision.
101
+ function writeEmptyTranscript(transcriptPath) {
102
+ const entries = [
103
+ // User message
104
+ JSON.stringify({
105
+ message: { role: 'user', content: [{ type: 'text', text: 'Run a build command' }] },
106
+ }),
107
+ // Assistant text with no cognition
108
+ JSON.stringify({
109
+ message: {
110
+ role: 'assistant',
111
+ content: [{ type: 'text', text: 'I will run the build.' }],
112
+ },
113
+ }),
114
+ ];
115
+ writeFileSync(transcriptPath, entries.join('\n'));
116
+ }
117
+
118
+ // Write a transcript with a Stop-gate-triggering assistant response (long,
119
+ // decision-signal, but no cognition).
120
+ function writeNoCognitionStopTranscript(transcriptPath) {
121
+ const longText = 'I recommend shipping this feature immediately because the test suite is green and the build passed. Let me proceed with the deployment now. ' +
122
+ 'The plan is as follows: first we build, then we deploy, then we verify. I would suggest we move forward without delay.';
123
+ const entries = [
124
+ JSON.stringify({
125
+ message: { role: 'user', content: [{ type: 'text', text: 'Should I ship?' }] },
126
+ }),
127
+ JSON.stringify({
128
+ message: {
129
+ role: 'assistant',
130
+ content: [{ type: 'text', text: longText }],
131
+ },
132
+ }),
133
+ ];
134
+ writeFileSync(transcriptPath, entries.join('\n'));
135
+ }
136
+
137
+ // ── Run hook helpers ────────────────────────────────────────────────────────
138
+ // Use a non-destructive but non-trivial command that skips the verify path
139
+ // and goes straight to cognition-missing, where lens labels appear.
140
+ function runPreToolGate(transcriptPath, command = 'npm run build --workspace=packages/aria-connector') {
141
+ const event = JSON.stringify({
142
+ tool_name: 'Bash',
143
+ tool_input: { command },
144
+ transcript_path: transcriptPath,
145
+ session_id: `test-tier-${Date.now()}`,
146
+ });
147
+ return spawnSync(process.execPath, [PRE_TOOL_HOOK], {
148
+ input: event,
149
+ encoding: 'utf8',
150
+ timeout: 10000,
151
+ env: {
152
+ ...process.env,
153
+ HOME,
154
+ ARIA_COGNITION_PUSH: 'off', // silence network push
155
+ ARIA_HARNESS_TOKEN: '', // no outbound harness calls
156
+ ARIA_BINDING_ENABLED: 'false', // disable binding gate — isolate cognition test
157
+ },
158
+ });
159
+ }
160
+
161
+ function runStopGate(transcriptPath) {
162
+ const event = JSON.stringify({
163
+ tool_name: 'Stop',
164
+ transcript_path: transcriptPath,
165
+ session_id: `test-tier-${Date.now()}`,
166
+ });
167
+ return spawnSync(process.execPath, [STOP_HOOK], {
168
+ input: event,
169
+ encoding: 'utf8',
170
+ timeout: 15000,
171
+ env: {
172
+ ...process.env,
173
+ HOME,
174
+ ARIA_COGNITION_PUSH: 'off',
175
+ ARIA_HARNESS_TOKEN: '',
176
+ },
177
+ });
178
+ }
179
+
180
+ // ── Test utilities ──────────────────────────────────────────────────────────
181
+ const tmpTranscript = `/tmp/test-tier-transcript-${Date.now()}.jsonl`;
182
+ const tmpTranscriptStop = `/tmp/test-tier-stop-transcript-${Date.now()}.jsonl`;
183
+
184
+ function cleanup() {
185
+ [tmpTranscript, tmpTranscriptStop].forEach((p) => {
186
+ try { if (existsSync(p)) unlinkSync(p); } catch {}
187
+ });
188
+ restorePacket();
189
+ }
190
+
191
+ // ── Suite ───────────────────────────────────────────────────────────────────
192
+
193
+ console.log('\n=== Phase 11 #59 — Tier-aware lens labeling smoke tests ===\n');
194
+
195
+ // Save existing packet so we can restore after test run.
196
+ savePacket();
197
+
198
+ // ── Test 1: pre-tool-gate, OWNER tier → canonical labels in block reason ──
199
+ console.log('Test 1: aria-pre-tool-gate — OWNER tier shows canonical lens names');
200
+ {
201
+ writeOwnerPacket();
202
+ writeEmptyTranscript(tmpTranscript);
203
+ const result = runPreToolGate(tmpTranscript);
204
+
205
+ // Gate should block (exit 2) — no cognition in transcript
206
+ assert(
207
+ 'T1: gate blocks (exit 2)',
208
+ result.status === 2,
209
+ `exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
210
+ );
211
+
212
+ let blockReason = '';
213
+ try {
214
+ const parsed = JSON.parse(result.stdout);
215
+ blockReason = parsed.reason || '';
216
+ } catch {
217
+ blockReason = result.stdout || '';
218
+ }
219
+
220
+ // Must mention at least one canonical label
221
+ const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
222
+ assert(
223
+ 'T1: block reason mentions canonical label (e.g. nur:)',
224
+ hasCanonical,
225
+ blockReason.slice(0, 400),
226
+ );
227
+
228
+ // Must NOT mention generic labels — no IP exposure for owner
229
+ // (owner should only see canonical; absence of generic is nice-to-verify
230
+ // but not a hard requirement since some generic words may appear in prose)
231
+ // Instead check the first lens label is canonical not generic:
232
+ const firstCanonicalIdx = blockReason.indexOf(`${CANONICAL[0]}:`);
233
+ const firstGenericIdx = blockReason.indexOf(`${GENERIC[0]}:`);
234
+ assert(
235
+ 'T1: first lens label is canonical (nur:), not generic (perception:)',
236
+ firstCanonicalIdx >= 0 && (firstGenericIdx < 0 || firstCanonicalIdx < firstGenericIdx),
237
+ `firstCanonicalIdx=${firstCanonicalIdx} firstGenericIdx=${firstGenericIdx}`,
238
+ );
239
+ }
240
+
241
+ // ── Test 2: pre-tool-gate, CLIENT tier → generic labels in block reason ──
242
+ console.log('\nTest 2: aria-pre-tool-gate — CLIENT tier shows generic lens labels');
243
+ {
244
+ writeClientPacket();
245
+ writeEmptyTranscript(tmpTranscript);
246
+ const result = runPreToolGate(tmpTranscript);
247
+
248
+ assert(
249
+ 'T2: gate blocks (exit 2)',
250
+ result.status === 2,
251
+ `exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
252
+ );
253
+
254
+ let blockReason = '';
255
+ try {
256
+ const parsed = JSON.parse(result.stdout);
257
+ blockReason = parsed.reason || '';
258
+ } catch {
259
+ blockReason = result.stdout || '';
260
+ }
261
+
262
+ // Must mention at least one generic label
263
+ const hasGeneric = GENERIC.some((name) => blockReason.includes(`${name}:`));
264
+ assert(
265
+ 'T2: block reason mentions generic label (e.g. perception:)',
266
+ hasGeneric,
267
+ blockReason.slice(0, 400),
268
+ );
269
+
270
+ // Must NOT expose canonical Arabic names
271
+ const exposesCanonical = CANONICAL.some((name) =>
272
+ blockReason.includes(`${name}:`),
273
+ );
274
+ assert(
275
+ 'T2: block reason does NOT expose canonical Arabic lens names',
276
+ !exposesCanonical,
277
+ `canonical leak: ${CANONICAL.filter((n) => blockReason.includes(`${n}:`)).join(', ')} in: ${blockReason.slice(0, 300)}`,
278
+ );
279
+
280
+ // Check doctrine memory filename is neutralized (no feedback_no_flag_without_fix.md for client)
281
+ const noDocFileLeak = !blockReason.includes('feedback_no_flag_without_fix.md');
282
+ // (This specific file only appears in discovery-unresolved path; just verify
283
+ // that the block reason itself also neutralizes EIGHT_LENS_DOCTRINE references if any)
284
+ // For this test we just assert the generic label set is present.
285
+ assert(
286
+ 'T2: first generic label (perception:) appears in block reason',
287
+ blockReason.includes(`${GENERIC[0]}:`),
288
+ blockReason.slice(0, 400),
289
+ );
290
+ }
291
+
292
+ // ── Test 3: stop-gate, OWNER tier → canonical labels ──
293
+ console.log('\nTest 3: aria-stop-gate — OWNER tier shows canonical lens names');
294
+ {
295
+ writeOwnerPacket();
296
+ writeNoCognitionStopTranscript(tmpTranscriptStop);
297
+ const result = runStopGate(tmpTranscriptStop);
298
+
299
+ assert(
300
+ 'T3: stop-gate blocks (exit 2)',
301
+ result.status === 2,
302
+ `exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
303
+ );
304
+
305
+ let blockReason = '';
306
+ try {
307
+ const parsed = JSON.parse(result.stdout);
308
+ blockReason = parsed.reason || '';
309
+ } catch {
310
+ blockReason = result.stdout || '';
311
+ }
312
+
313
+ const hasCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
314
+ assert(
315
+ 'T3: stop-gate block reason mentions canonical label',
316
+ hasCanonical,
317
+ blockReason.slice(0, 400),
318
+ );
319
+ }
320
+
321
+ // ── Test 4: stop-gate, CLIENT tier → generic labels, no canonical leak ──
322
+ console.log('\nTest 4: aria-stop-gate — CLIENT tier shows generic labels, no canonical leak');
323
+ {
324
+ writeClientPacket();
325
+ writeNoCognitionStopTranscript(tmpTranscriptStop);
326
+ const result = runStopGate(tmpTranscriptStop);
327
+
328
+ assert(
329
+ 'T4: stop-gate blocks (exit 2)',
330
+ result.status === 2,
331
+ `exit=${result.status} stderr=${result.stderr?.slice(0, 200)}`,
332
+ );
333
+
334
+ let blockReason = '';
335
+ try {
336
+ const parsed = JSON.parse(result.stdout);
337
+ blockReason = parsed.reason || '';
338
+ } catch {
339
+ blockReason = result.stdout || '';
340
+ }
341
+
342
+ const hasGeneric = GENERIC.some((name) => blockReason.includes(`${name}:`));
343
+ assert(
344
+ 'T4: stop-gate block reason mentions generic label',
345
+ hasGeneric,
346
+ blockReason.slice(0, 400),
347
+ );
348
+
349
+ const exposesCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
350
+ assert(
351
+ 'T4: stop-gate block reason does NOT expose canonical lens names',
352
+ !exposesCanonical,
353
+ `canonical leak: ${CANONICAL.filter((n) => blockReason.includes(`${n}:`)).join(', ')}`,
354
+ );
355
+ }
356
+
357
+ // ── Test 5: packet missing → defaults to CLIENT tier (fail-safe) ──────────
358
+ console.log('\nTest 5: missing packet cache → defaults to CLIENT tier (fail-safe)');
359
+ {
360
+ // Remove the packet cache
361
+ try { unlinkSync(PACKET_PATH); } catch {}
362
+ writeEmptyTranscript(tmpTranscript);
363
+ const result = runPreToolGate(tmpTranscript);
364
+
365
+ assert(
366
+ 'T5: gate blocks (exit 2) with no packet',
367
+ result.status === 2,
368
+ `exit=${result.status}`,
369
+ );
370
+
371
+ let blockReason = '';
372
+ try {
373
+ const parsed = JSON.parse(result.stdout);
374
+ blockReason = parsed.reason || '';
375
+ } catch {
376
+ blockReason = result.stdout || '';
377
+ }
378
+
379
+ // With no packet, should default to generic (client-safe fail direction)
380
+ const hasGeneric = GENERIC.some((name) => blockReason.includes(`${name}:`));
381
+ const exposesCanonical = CANONICAL.some((name) => blockReason.includes(`${name}:`));
382
+ assert(
383
+ 'T5: defaults to generic labels when packet is absent',
384
+ hasGeneric && !exposesCanonical,
385
+ `generic=${hasGeneric} canonicalLeak=${exposesCanonical} reason=${blockReason.slice(0, 300)}`,
386
+ );
387
+ }
388
+
389
+ // ── Summary ─────────────────────────────────────────────────────────────────
390
+ cleanup();
391
+ console.log(`\n${'─'.repeat(60)}`);
392
+ console.log(`Results: ${passed} passed, ${failed} failed`);
393
+ if (failed > 0) {
394
+ console.error('\nSome tests FAILED — see FAIL lines above.');
395
+ process.exit(1);
396
+ } else {
397
+ console.log('\nAll tests PASSED.');
398
+ process.exit(0);
399
+ }