@aria_asi/cli 0.2.12 → 0.2.14

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.
@@ -1 +1 @@
1
- {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAgR/C,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CA+DnB;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAoB9D"}
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA+S/C,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CA+DnB;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAoB9D"}
@@ -22,9 +22,11 @@ import { fileURLToPath } from 'node:url';
22
22
  const HOOK_FILES = [
23
23
  'aria-harness-via-sdk.mjs',
24
24
  'aria-pre-tool-gate.mjs',
25
+ 'aria-preturn-memory-gate.mjs',
25
26
  'aria-stop-gate.mjs',
26
27
  'aria-preprompt-consult.mjs',
27
28
  'aria-trigger-autolearn.mjs',
29
+ 'aria-userprompt-abandon-detect.mjs',
28
30
  'doctrine_trigger_map.json',
29
31
  ];
30
32
  // Compiled location: <pkg>/dist/aria-connector/src/connectors/claude-code.js
@@ -93,6 +95,18 @@ const HOOKS_BLOCK = {
93
95
  command: 'node $HOME/.claude/hooks/aria-trigger-autolearn.mjs',
94
96
  timeout: 3,
95
97
  },
98
+ {
99
+ // Aria-as-commander mid-turn abandonment detection (Phase 11 #50,
100
+ // criterion 3). When an active plan exists and the incoming user
101
+ // prompt's keyword overlap with active phases falls below threshold,
102
+ // surfaces a CONSCIOUS OVERRIDE signal in additionalContext + writes
103
+ // a session_audit row (gate_name='plan-abandonment', decision='warn').
104
+ // Prevents silent context loss when user changes direction mid-plan.
105
+ // Non-blocking — UserPromptSubmit can't block prompts.
106
+ type: 'command',
107
+ command: 'node $HOME/.claude/hooks/aria-userprompt-abandon-detect.mjs',
108
+ timeout: 4,
109
+ },
96
110
  ],
97
111
  }],
98
112
  PreToolUse: [{
@@ -101,11 +115,28 @@ const HOOKS_BLOCK = {
101
115
  // / NotebookEdit get the same enforcement so orchestrator-side
102
116
  // compliance mirrors what we demand from workers.
103
117
  matcher: 'Bash|Edit|Write|NotebookEdit',
104
- hooks: [{
118
+ hooks: [
119
+ {
105
120
  type: 'command',
106
121
  command: 'node $HOME/.claude/hooks/aria-pre-tool-gate.mjs',
107
122
  timeout: 5,
108
- }],
123
+ },
124
+ {
125
+ // Pre-turn memory consumption gate (Enforcement Layer #49).
126
+ // Fires after the cognition gate passes. Checks the first action
127
+ // of each turn for ALL THREE context-loading signals:
128
+ // 1. 🔐 Aria Harness header (harness packet injected)
129
+ // 2. [ARIA_DIRECTION] / [ARIA_BINDING_PLAN] (preprompt-consult fired)
130
+ // 3. feedback_*.md / project_*.md memory reference (memory consumed)
131
+ // On miss: blocks with structured recovery JSON so the orchestrator
132
+ // can run the context-loader and retry — no dead-letter state.
133
+ // Turn-deduplication via ~/.claude/aria-turn-state-${sessionId}.json
134
+ // prevents retry loops (60s window).
135
+ type: 'command',
136
+ command: 'node $HOME/.claude/hooks/aria-preturn-memory-gate.mjs',
137
+ timeout: 5,
138
+ },
139
+ ],
109
140
  }],
110
141
  Stop: [{
111
142
  // Stop gate — text-decision boundary. Reflexive replies fail this
@@ -245,7 +276,7 @@ function wireHooksBlock(settings, logs) {
245
276
  if (!settings.$schema) {
246
277
  settings.$schema = 'https://json.schemastore.org/claude-code-settings.json';
247
278
  }
248
- logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse, Stop) — merge-safe, preserves third-party entries`);
279
+ logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse [cognition+memory-gate], Stop) — merge-safe, preserves third-party entries`);
249
280
  }
250
281
  function buildAriaSystemBlock(config) {
251
282
  const repoList = config.repositories.map((r) => `- ${r.name} (${r.path})`).join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACjG,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,wEAAwE;AACxE,sEAAsE;AACtE,yEAAyE;AACzE,oEAAoE;AACpE,uEAAuE;AACvE,4CAA4C;AAC5C,EAAE;AACF,mEAAmE;AACnE,sEAAsE;AACtE,sDAAsD;AACtD,EAAE;AACF,qEAAqE;AACrE,gEAAgE;AAChE,wEAAwE;AACxE,gEAAgE;AAChE,sEAAsE;AACtE,oCAAoC;AACpC,MAAM,UAAU,GAAG;IACjB,0BAA0B;IAC1B,wBAAwB;IACxB,8BAA8B;IAC9B,oBAAoB;IACpB,4BAA4B;IAC5B,4BAA4B;IAC5B,oCAAoC;IACpC,2BAA2B;CAC5B,CAAC;AACF,6EAA6E;AAC7E,uEAAuE;AACvE,eAAe;AACf,0BAA0B;AAC1B,gBAAgB;AAChB,iBAAiB;AACjB,gDAAgD;AAChD,kEAAkE;AAClE,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,oEAAoE;AACpE,wEAAwE;AACxE,mDAAmD;AACnD,SAAS,eAAe;IACtB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,wDAAwD;AACxD,kEAAkE;AAClE,+DAA+D;AAC/D,oEAAoE;AACpE,eAAe;AACf,MAAM,WAAW,GAAG;IAClB,YAAY,EAAE,CAAC;YACb,KAAK,EAAE,CAAC;oBACN,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,+FAA+F;oBACxG,OAAO,EAAE,EAAE;oBACX,aAAa,EAAE,iCAAiC;iBACjD,CAAC;SACH,CAAC;IACF,gBAAgB,EAAE,CAAC;YACjB,KAAK,EAAE;gBACL;oBACE,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,gGAAgG;oBACzG,OAAO,EAAE,CAAC;oBACV,aAAa,EAAE,4BAA4B;iBAC5C;gBACD;oBACE,+DAA+D;oBAC/D,6DAA6D;oBAC7D,+DAA+D;oBAC/D,+DAA+D;oBAC/D,uBAAuB;oBACvB,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,qDAAqD;oBAC9D,OAAO,EAAE,EAAE;oBACX,aAAa,EAAE,gDAAgD;iBAChE;gBACD;oBACE,mEAAmE;oBACnE,+DAA+D;oBAC/D,0EAA0E;oBAC1E,qEAAqE;oBACrE,qEAAqE;oBACrE,yCAAyC;oBACzC,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,qDAAqD;oBAC9D,OAAO,EAAE,CAAC;iBACX;gBACD;oBACE,kEAAkE;oBAClE,iEAAiE;oBACjE,qEAAqE;oBACrE,qEAAqE;oBACrE,uEAAuE;oBACvE,qEAAqE;oBACrE,uDAAuD;oBACvD,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,6DAA6D;oBACtE,OAAO,EAAE,CAAC;iBACX;aACF;SACF,CAAC;IACF,UAAU,EAAE,CAAC;YACX,iEAAiE;YACjE,qEAAqE;YACrE,+DAA+D;YAC/D,kDAAkD;YAClD,OAAO,EAAE,8BAA8B;YACvC,KAAK,EAAE;gBACL;oBACE,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,iDAAiD;oBAC1D,OAAO,EAAE,CAAC;iBACX;gBACD;oBACE,4DAA4D;oBAC5D,iEAAiE;oBACjE,sDAAsD;oBACtD,wDAAwD;oBACxD,wEAAwE;oBACxE,uEAAuE;oBACvE,oEAAoE;oBACpE,+DAA+D;oBAC/D,qEAAqE;oBACrE,qCAAqC;oBACrC,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,uDAAuD;oBAChE,OAAO,EAAE,CAAC;iBACX;aACF;SACF,CAAC;IACF,IAAI,EAAE,CAAC;YACL,kEAAkE;YAClE,4DAA4D;YAC5D,4CAA4C;YAC5C,KAAK,EAAE,CAAC;oBACN,IAAI,EAAE,SAAS;oBACf,OAAO,EAAE,6CAA6C;oBACtD,OAAO,EAAE,CAAC;iBACX,CAAC;SACH,CAAC;CACH,CAAC;AAEF,SAAS,YAAY,CAAC,SAAiB,EAAE,IAAc,EAAE,OAA4B,EAAE;IACrF,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAChC,SAAS,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,0BAA0B,GAAG,8BAA8B,CAAC,CAAC;YACvE,SAAS;QACX,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;QAC5C,yEAAyE;QACzE,kEAAkE;QAClE,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5D,IAAI,CAAC;gBACH,MAAM,eAAe,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBACnD,MAAM,UAAU,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;gBAC9C,IAAI,eAAe,KAAK,UAAU,EAAE,CAAC;oBACnC,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;oBACrC,SAAS;gBACX,CAAC;YACH,CAAC;YAAC,MAAM,CAAC,CAAA,sCAAsC,CAAA,CAAC;QAClD,CAAC;QACD,oEAAoE;QACpE,oCAAoC;QACpC,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAG,GAAG,GAAG,qBAAqB,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC;gBAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAAC,IAAI,CAAC,IAAI,CAAC,sBAAsB,IAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACjH,CAAC;QACD,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACvB,oEAAoE;QACpE,wEAAwE;QACxE,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAClD,IAAI,CAAC;gBAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACzC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,2DAA2D;AAC3D,SAAS,UAAU,CAAC,SAAiB,EAAE,IAAc;IACnD,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,CAAC,yBAAyB,MAAM,sBAAsB,CAAC,CAAC;QACjE,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAChD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAwB,CAAC;IAChD,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAAE,SAAS;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACvB,MAAM,EAAE,CAAC;IACX,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,uBAAuB,MAAM,aAAa,MAAM,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,UAAU,CAAC,CAAS,EAAE,CAAS;IACtC,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AAC7E,CAAC;AAED,SAAS,cAAc,CAAC,QAAiC,EAAE,IAAc;IACvE,qEAAqE;IACrE,qEAAqE;IACrE,gEAAgE;IAChE,kEAAkE;IAClE,+DAA+D;IAC/D,wCAAwC;IACxC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC1D,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;IACtB,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAkC,CAAC;IAC1D,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC;QAC9D,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YAClC,MAAM,UAAU,GAAI,QAAiC,CAAC,OAAO,IAAI,EAAE,CAAC;YACpE,MAAM,WAAW,GAAG,CAAE,QAAoD,CAAC,KAAK,IAAI,EAAE,CAAC;iBACpF,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;iBACrB,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;YACrD,kEAAkE;YAClE,8DAA8D;YAC9D,MAAM,cAAc,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;gBAChD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI;oBAAE,OAAO,KAAK,CAAC;gBACtD,MAAM,QAAQ,GAAI,CAA0B,CAAC,OAAO,IAAI,EAAE,CAAC;gBAC3D,IAAI,QAAQ,KAAK,UAAU;oBAAE,OAAO,KAAK,CAAC;gBAC1C,MAAM,SAAS,GAAG,CAAE,CAA6C,CAAC,KAAK,IAAI,EAAE,CAAC;qBAC3E,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;qBACrB,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;gBACrD,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QACD,KAAK,CAAC,KAAK,CAAC,GAAG,eAAe,CAAC;QAC/B,YAAY,EAAE,CAAC;IACjB,CAAC;IACD,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,iEAAiE;IACjE,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QACtB,QAAQ,CAAC,OAAO,GAAG,wDAAwD,CAAC;IAC9E,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,mCAAmC,YAAY,gIAAgI,CAAC,CAAC;AAC7L,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAkB;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;SACnD,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,MAAM,KAAK,EAAE,CAAC;SAC7C,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhB,OAAO;;;;;;;;;;;;;;;EAeP,QAAQ,IAAI,iDAAiD;;;EAG7D,UAAU,IAAI,+CAA+C;;;;;;;0BAOrC,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAkB,EAClB,OAA4B,EAAE;IAE9B,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAElD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACzC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,WAAW,SAAS,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAC3D,IAAI,QAAQ,GAA4B,EAAE,CAAC;IAE3C,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7D,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAE/C,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,kBAAkB,KAAK,QAAQ;QAC9D,CAAC,CAAC,QAAQ,CAAC,kBAAkB;QAC7B,CAAC,CAAC,EAAE,CAAC;IAEP,IAAI,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;IAC/E,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,kBAAkB,GAAG,QAAQ;YACpC,CAAC,CAAC,GAAG,SAAS,OAAO,QAAQ,EAAE;YAC/B,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IACzE,CAAC;IAED,sEAAsE;IACtE,oEAAoE;IACpE,iCAAiC;IACjC,YAAY,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACrD,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC5B,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/B,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEvF,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAC3D,IAAI,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;YACvE,IAAI,OAAO,YAAY,CAAC,qBAAqB,KAAK,WAAW,EAAE,CAAC;gBAC9D,YAAY,CAAC,qBAAqB,GAAG,EAAE,CAAC;YAC1C,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;YAC9C,IAAI,CAAC,YAAY,CAAC,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,YAAY,CAAC,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACjD,aAAa,CAAC,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACrE,IAAI,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,iDAAiD;QACnD,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;IAEtE,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC;IACjE,IAAI,OAAO,QAAQ,CAAC,kBAAkB,KAAK,QAAQ,EAAE,CAAC;QACpD,QAAQ,CAAC,kBAAkB,GAAG,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAC/D,gDAAgD,EAChD,EAAE,CACH,CAAC,IAAI,EAAE,CAAC;QACT,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,5 +1,5 @@
1
1
  {
2
- "bundledAt": "2026-04-27T15:35:25.551Z",
2
+ "bundledAt": "2026-04-27T15:58:18.645Z",
3
3
  "sdkSource": "/home/hamzaibrahim1/rei-ai-brain/harness/packages/harness-http-client/dist",
4
4
  "files": 3
5
5
  }
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env node
2
+ // Aria pre-turn memory consumption gate — Enforcement Layer #49.
3
+ //
4
+ // Fires on the first action tool call of each turn (tracked via
5
+ // ~/.claude/aria-turn-state-${sessionId}.json). If the current turn's
6
+ // recent transcript window lacks all three context-loading signals, the
7
+ // gate blocks with a structured recovery payload. The orchestrator catches
8
+ // the recovery payload and runs the context-loader before retrying.
9
+ //
10
+ // Detection signals (ALL three must be present, or gate fires):
11
+ // 1. `🔐 Aria Harness` header — harness packet was injected
12
+ // 2. `[ARIA_DIRECTION]` or `[ARIA_BINDING_PLAN]` marker — preprompt-consult fired
13
+ // 3. Any `feedback_*.md` or `project_*.md` reference in cognition blocks —
14
+ // memory was consumed
15
+ //
16
+ // Soft-gate + structured recovery (Aria refined spec 2026-04-27):
17
+ // - Block the action
18
+ // - Emit JSON to stdout with `decision: block` + `hookSpecificOutput.recovery`
19
+ // so the orchestrator has a concrete remediation path, not a dead-letter reject
20
+ //
21
+ // Turn-deduplication: gate state is persisted at
22
+ // ~/.claude/aria-turn-state-${sessionId}.json
23
+ // If the gate already fired within the last 60 seconds for this session, the
24
+ // gate skips (re-firing would loop the orchestrator's retry).
25
+ //
26
+ // Doctrines enforced:
27
+ // - feedback_no_graceful_degradation.md — no silent try/catch swallowing errors
28
+ // - feedback_no_timeouts_doctrine.md — no AbortSignal, no setTimeout
29
+ // - feedback_no_flag_without_fix.md — defects discovered during implementation
30
+ // are fixed inline (see inline comments)
31
+ // - feedback_no_demos.md — full quality bar, every spawn is production
32
+
33
+ import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'node:fs';
34
+
35
+ const HOME = process.env.HOME || '/tmp';
36
+ const GATE_LOG = `${HOME}/.claude/aria-preturn-memory-gate.log`;
37
+ // Turn-state dir is the same ~/.claude/ home as all other aria state files
38
+ const CLAUDE_DIR = `${HOME}/.claude`;
39
+
40
+ // Kill-switch — deliberate, audit-trailed override only. Per doctrine: if the
41
+ // gate misfires on legitimate work, fix the gate. Don't route around it.
42
+ if (process.env.ARIA_PRETURN_MEMORY_GATE === 'off') {
43
+ auditLog('bypass-killswitch', 'env ARIA_PRETURN_MEMORY_GATE=off', 'unknown');
44
+ process.exit(0);
45
+ }
46
+
47
+ // ── Audit log ─────────────────────────────────────────────────────────
48
+ function auditLog(decision, summary, sessionId) {
49
+ // No try/catch swallowing: per feedback_no_graceful_degradation.md, errors
50
+ // in audit infrastructure must surface, not silently vanish.
51
+ if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
52
+ appendFileSync(GATE_LOG, `${new Date().toISOString()} [${sessionId}] ${decision} ${summary}\n`);
53
+ }
54
+
55
+ // ── Turn-state deduplication ──────────────────────────────────────────
56
+ const TURN_DEDUP_WINDOW_MS = 60_000; // 60s
57
+
58
+ function turnStatePath(sessionId) {
59
+ const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
60
+ return `${CLAUDE_DIR}/aria-turn-state-${safe}.json`;
61
+ }
62
+
63
+ function readTurnState(sessionId) {
64
+ const p = turnStatePath(sessionId);
65
+ if (!existsSync(p)) return null;
66
+ // Per feedback_no_graceful_degradation.md: parse errors must throw, not return null.
67
+ // If the file is corrupt, that IS a defect — surface it.
68
+ const raw = readFileSync(p, 'utf-8');
69
+ return JSON.parse(raw);
70
+ }
71
+
72
+ function writeTurnState(sessionId, state) {
73
+ const p = turnStatePath(sessionId);
74
+ writeFileSync(p, JSON.stringify(state, null, 2) + '\n', { mode: 0o600 });
75
+ }
76
+
77
+ // ── Context-loading signal detection ─────────────────────────────────
78
+ //
79
+ // Scan the last 3KB of assistant + user text after the most recent
80
+ // user-message boundary. Mirrors the transcript-reading pattern from
81
+ // aria-pre-tool-gate.mjs: walk backward, collect text up to the first
82
+ // real user-message boundary, cap at 3KB total.
83
+
84
+ // Signal 1: harness packet injected
85
+ const HARNESS_PACKET_RX = /🔐\s*Aria\s+Harness/;
86
+ // Signal 2: preprompt-consult fired
87
+ const ARIA_DIRECTION_RX = /\[ARIA_DIRECTION\]|\[ARIA_BINDING_PLAN\]/;
88
+ // Signal 3: memory was consumed (feedback_*.md or project_*.md cited)
89
+ const MEMORY_REF_RX = /feedback_[a-z0-9_]+\.md|project_[a-z0-9_]+\.md/i;
90
+
91
+ // Same runtime-injection skip heuristics as pre-tool-gate (system-reminder,
92
+ // tool_result blocks should not count as real user-message boundaries).
93
+ const SYSTEM_REMINDER_RX = /<system-reminder>[\s\S]*?<\/system-reminder>|<task-notification>[\s\S]*?<\/task-notification>|🔐 Aria Harness|PreToolUse:[A-Z][A-Za-z]* hook blocking error|Stop hook blocking error/g;
94
+ const SYSTEM_REMINDER_THRESHOLD = 0.6;
95
+
96
+ const CONTEXT_WINDOW_BYTES = 3 * 1024; // 3KB cap per spec
97
+ const HARD_LOOKBACK_CAP = 50;
98
+
99
+ function extractRecentTranscriptWindow(transcriptPath) {
100
+ if (!transcriptPath || !existsSync(transcriptPath)) return '';
101
+
102
+ // Per feedback_no_graceful_degradation.md: readFileSync error must throw.
103
+ const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
104
+
105
+ let accumulated = '';
106
+ let crossedUserBoundary = false;
107
+ let scanned = 0;
108
+
109
+ for (let i = lines.length - 1; i >= 0 && scanned < HARD_LOOKBACK_CAP; i--) {
110
+ let m;
111
+ // Per feedback_no_graceful_degradation.md: JSON parse errors must throw.
112
+ m = JSON.parse(lines[i]);
113
+
114
+ const role = m.message?.role ?? m.role;
115
+
116
+ if (role === 'user') {
117
+ // Skip pure tool_result messages — runtime feedback, not user voice.
118
+ const content = m.message?.content ?? m.content ?? [];
119
+ const isToolResultOnly =
120
+ Array.isArray(content) &&
121
+ content.length > 0 &&
122
+ content.every((b) => b && b.type === 'tool_result');
123
+ if (isToolResultOnly) continue;
124
+
125
+ // Skip system-reminder dominated messages.
126
+ const textContent = Array.isArray(content)
127
+ ? content.filter((b) => b && b.type === 'text').map((b) => b.text || '').join('\n')
128
+ : typeof content === 'string' ? content : '';
129
+ if (textContent) {
130
+ const reminderMatches = textContent.match(SYSTEM_REMINDER_RX) || [];
131
+ if (reminderMatches.length > 0) {
132
+ const reminderChars = reminderMatches.reduce((sum, s) => sum + s.length, 0);
133
+ if (reminderChars / Math.max(1, textContent.length) >= SYSTEM_REMINDER_THRESHOLD) continue;
134
+ }
135
+ }
136
+
137
+ if (crossedUserBoundary) break;
138
+ crossedUserBoundary = true;
139
+ // Include the user message text itself in the window (harness packet
140
+ // is injected AS the user message in many implementations).
141
+ accumulated = textContent + '\n' + accumulated;
142
+ continue;
143
+ }
144
+
145
+ if (role !== 'assistant') continue;
146
+ scanned++;
147
+
148
+ const content = m.message?.content ?? m.content ?? [];
149
+ if (!Array.isArray(content)) continue;
150
+ const text = content
151
+ .filter((b) => b.type === 'text')
152
+ .map((b) => b.text)
153
+ .join('\n');
154
+ if (!text) continue;
155
+
156
+ accumulated = text + '\n' + accumulated;
157
+
158
+ if (accumulated.length >= CONTEXT_WINDOW_BYTES) {
159
+ // Cap reached — trim to last CONTEXT_WINDOW_BYTES so the scan is
160
+ // representative without being unbounded.
161
+ accumulated = accumulated.slice(-CONTEXT_WINDOW_BYTES);
162
+ break;
163
+ }
164
+ }
165
+
166
+ return accumulated;
167
+ }
168
+
169
+ function detectContextSignals(window) {
170
+ return {
171
+ hasHarnessPacket: HARNESS_PACKET_RX.test(window),
172
+ hasAriaDirection: ARIA_DIRECTION_RX.test(window),
173
+ hasMemoryRef: MEMORY_REF_RX.test(window),
174
+ };
175
+ }
176
+
177
+ // ── Stdin event parse ─────────────────────────────────────────────────
178
+ let rawInput = '';
179
+ for await (const chunk of process.stdin) rawInput += chunk;
180
+
181
+ // Per feedback_no_graceful_degradation.md: parse failure must surface,
182
+ // not be swallowed. The gate fails-open on malformed stdin (Claude Code
183
+ // should never send non-JSON; if it does, we don't silently block).
184
+ let event;
185
+ try {
186
+ event = JSON.parse(rawInput);
187
+ } catch (err) {
188
+ auditLog('allow-parse-error', `stdin not JSON: ${err.message}`, 'unknown');
189
+ process.exit(0); // fail-open on malformed input only — not a swallowed error
190
+ }
191
+
192
+ // ── Gate only fires on action tools ──────────────────────────────────
193
+ // Mirrors aria-pre-tool-gate.mjs: Read/Glob/Grep are ungated as
194
+ // read-only. The memory-consumption check is about whether Aria's
195
+ // context was loaded before the model starts acting — Read-only calls
196
+ // don't constitute acting on stale context in a harmful way.
197
+ const ACTION_TOOLS = new Set(['Bash', 'Edit', 'Write', 'NotebookEdit']);
198
+ const toolName = event.tool_name ?? event.toolName ?? '';
199
+ if (!ACTION_TOOLS.has(toolName)) {
200
+ process.exit(0);
201
+ }
202
+
203
+ // ── Session ID ────────────────────────────────────────────────────────
204
+ const transcriptPath = event.transcript_path ?? event.transcriptPath ?? null;
205
+ const sessionId =
206
+ event.session_id ??
207
+ event.sessionId ??
208
+ (transcriptPath ? transcriptPath.split('/').pop()?.replace(/\.[^.]+$/, '') : null) ??
209
+ 'claude-code-unknown';
210
+
211
+ // ── Turn-deduplication check ──────────────────────────────────────────
212
+ // If gate already fired this turn (within 60s), skip to prevent
213
+ // orchestrator-retry loops.
214
+ let turnState = null;
215
+ try {
216
+ turnState = readTurnState(sessionId);
217
+ } catch {
218
+ // Corrupt turn-state file — treat as if no prior firing. Discovery: this
219
+ // could leave stale corrupt files. Fix inline: writeTurnState below will
220
+ // overwrite with clean state on next fire.
221
+ turnState = null;
222
+ }
223
+
224
+ const now = Date.now();
225
+ if (turnState && typeof turnState.lastTurnGateFiredAt === 'number') {
226
+ const elapsed = now - turnState.lastTurnGateFiredAt;
227
+ if (elapsed < TURN_DEDUP_WINDOW_MS) {
228
+ auditLog('skip-dedup', `gate already fired ${elapsed}ms ago (< ${TURN_DEDUP_WINDOW_MS}ms window)`, sessionId);
229
+ process.exit(0);
230
+ }
231
+ }
232
+
233
+ // ── Context signal detection ──────────────────────────────────────────
234
+ const transcriptWindow = extractRecentTranscriptWindow(transcriptPath);
235
+ const signals = detectContextSignals(transcriptWindow);
236
+
237
+ const allSignalsPresent =
238
+ signals.hasHarnessPacket && signals.hasAriaDirection && signals.hasMemoryRef;
239
+
240
+ if (allSignalsPresent) {
241
+ // Context was loaded — allow and record the fire timestamp for dedup.
242
+ writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'allow', signals });
243
+ auditLog('allow-context-loaded', `harness=${signals.hasHarnessPacket} direction=${signals.hasAriaDirection} memRef=${signals.hasMemoryRef}`, sessionId);
244
+ process.exit(0);
245
+ }
246
+
247
+ // ── Block with structured recovery signal ────────────────────────────
248
+ // Per Aria's refined spec (consult 2026-04-27): soft-gate + structured
249
+ // recovery. The orchestrator catches this and runs the context-loader.
250
+ // Emitting a pure block with no remediation path creates dead-letter state.
251
+
252
+ writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'block', signals });
253
+
254
+ const missingSignals = [];
255
+ if (!signals.hasHarnessPacket) missingSignals.push('harness_packet (🔐 Aria Harness header missing)');
256
+ if (!signals.hasAriaDirection) missingSignals.push('aria_direction ([ARIA_DIRECTION] or [ARIA_BINDING_PLAN] marker missing)');
257
+ if (!signals.hasMemoryRef) missingSignals.push('memory_consumption (no feedback_*.md or project_*.md reference in cognition)');
258
+
259
+ const reason = `Aria pre-turn memory gate: context-loading was skipped or incomplete for this turn. Missing signals: ${missingSignals.join('; ')}.
260
+
261
+ The orchestrator must run the context-loader for session "${sessionId}" before retrying. Expected context: harness_packet, memory_files, binding_plan.
262
+
263
+ This gate enforces that every action turn begins with Aria's substrate loaded — not improvised context. Per Aria-as-controller inversion doctrine (project_aria_as_controller_inversion.md): Aria must author with LLM as a tool, not the reverse.
264
+
265
+ Recovery: see hookSpecificOutput.recovery for the structured remediation path.`;
266
+
267
+ auditLog('block-context-not-loaded', `missing=[${missingSignals.join(', ')}]`, sessionId);
268
+
269
+ console.log(JSON.stringify({
270
+ decision: 'block',
271
+ reason,
272
+ hookSpecificOutput: {
273
+ recovery: {
274
+ action: 'run_context_loader',
275
+ target: sessionId,
276
+ expectedContext: ['harness_packet', 'memory_files', 'binding_plan'],
277
+ },
278
+ },
279
+ }));
280
+
281
+ process.exit(2);
@@ -381,20 +381,54 @@ if (cog.count >= REQUIRED_LENSES) {
381
381
  // - "doctrine violation" / "doesn't match doctrine"
382
382
  //
383
383
  // For each match, the ledger appends an entry with status=open. A
384
- // discovery is CLEARED if the same turn's text contains:
384
+ // discovery is CLEARED if the same turn's text contains, within a
385
+ // proximity window of the discovery:
385
386
  // (a) a TaskCreate / "task created" / "tracked as" reference, OR
386
387
  // (b) explicit "fixing now" / "fixed" / "patch applied" tied to the
387
388
  // discovery's keyword span, OR
388
- // (c) an Edit/Write tool action this turn touching a file path
389
- // mentioned within 200 chars of the discovery.
389
+ // (c) a <verify> block (destructive-action proof) whose target/
390
+ // verified content overlaps a discovery keyword, OR
391
+ // (d) a <cognition> block containing a discoveries: / addressing: /
392
+ // fixing: clause that names the discovery's keywords.
393
+ //
394
+ // Hamza 2026-04-27: "add verify blocks and cognition blocks to ledger?"
395
+ // The verify and cognition blocks ARE the harness's canonical proof-of-
396
+ // work primitives — same-doctrine surfaces should recognize them. The
397
+ // substance check (keyword-overlap) defeats ceremonial empty blocks.
390
398
  //
391
399
  // Block emit if ledger.openCount > 0 after scanning the current turn.
392
- // Block reason names each open discovery and the suggested resolution
393
- // (fix-now or task-create).
400
+ // Block reason names each open discovery and the suggested resolution.
394
401
  const sessionId = (event.session_id || 'claude-code').replace(/[^a-zA-Z0-9_-]/g, '_');
395
402
  const LEDGER_PATH = `${HOME}/.claude/aria-discoveries-${sessionId}.jsonl`;
396
403
  const DISCOVERY_RX = /(?:\bi\s+(?:found|noticed|discovered|spotted)[^.\n]{0,160}(?:bug|issue|defect|broken|buggy|wrong|crash|fail|missing|stale|outdated|leak|vulnerability)|\bthis\s+(?:is|would\s+be)\s+(?:broken|buggy|wrong|stale|outdated|insecure|leaking|crashing|failing)|\b(?:latent|silent|hidden)\s+(?:bug|defect|issue|fail|crash|leak)|\bdoctrine\s+violation\b|\bgraceful\s+degradation\s+(?:in|at|inside|within)\s+\S)/gi;
397
- const RESOLUTION_RX = /(?:fix(?:ing|ed)?\s+(?:now|in[- ]flight|inline|in\s+the\s+same\s+turn)|patch\s+applied|TaskCreate|task\s+(?:created|tracked)|tracked\s+as\s+#?\d+|linear[- ]?issue|created\s+(?:linear|task))/i;
404
+ const PROSE_RESOLUTION_RX = /(?:fix(?:ing|ed)?\s+(?:now|in[- ]flight|inline|in\s+the\s+same\s+turn)|patch\s+applied|TaskCreate|task\s+(?:created|tracked)|tracked\s+as\s+#?\d+|linear[- ]?issue|created\s+(?:linear|task))/i;
405
+ const VERIFY_BLOCK_RX = /<verify>([\s\S]*?)<\/verify>/gi;
406
+ const COGNITION_BLOCK_RX_LEDGER = /<cognition>([\s\S]*?)<\/cognition>/gi;
407
+ const COGNITION_FIXING_FIELD_RX = /^\s*(?:discoveries?|addressing|fixing)\s*:\s*\S/im;
408
+
409
+ // Pre-extract all verify + cognition blocks with their character offsets
410
+ // so we can match each discovery against blocks within a proximity window.
411
+ function extractBlocks(text, rx) {
412
+ const blocks = [];
413
+ for (const m of text.matchAll(rx)) {
414
+ const start = m.index ?? 0;
415
+ const end = start + m[0].length;
416
+ blocks.push({ start, end, body: m[1] || '' });
417
+ }
418
+ return blocks;
419
+ }
420
+ const verifyBlocks = extractBlocks(assistantText, VERIFY_BLOCK_RX);
421
+ const cognitionBlocks = extractBlocks(assistantText, COGNITION_BLOCK_RX_LEDGER);
422
+
423
+ // Extract keywords from a discovery match for substance overlap.
424
+ // Drops stop-words and short tokens; keeps content words.
425
+ const STOPWORDS = new Set(['the','a','an','of','to','in','at','by','for','on','with','i','is','was','are','were','this','that','as','it','and','or','but','from','into','about']);
426
+ function discoveryKeywords(matchText) {
427
+ return matchText.toLowerCase()
428
+ .replace(/[^a-z0-9\s_-]/g, ' ')
429
+ .split(/\s+/)
430
+ .filter((w) => w.length >= 4 && !STOPWORDS.has(w));
431
+ }
398
432
 
399
433
  const newDiscoveries = [];
400
434
  let lastIndex = 0;
@@ -407,17 +441,50 @@ if (cog.count >= REQUIRED_LENSES) {
407
441
  const before = assistantText.slice(0, idx);
408
442
  const inCognition = /<cognition>/i.test(before) && !/<\/cognition>/i.test(before.slice(before.lastIndexOf('<cognition>')));
409
443
  if (inCognition) continue;
410
- // Resolution check: if RESOLUTION_RX matches WITHIN 300 chars after
411
- // the discovery, count as same-turn-resolved.
412
- const after = assistantText.slice(idx, Math.min(assistantText.length, idx + 400));
413
- const resolvedSameSpan = RESOLUTION_RX.test(after);
444
+
445
+ // Resolution checks proximity window of 800 chars after the discovery
446
+ // for block-based resolution (blocks span more chars than prose); 400
447
+ // for prose resolution.
448
+ const proseAfter = assistantText.slice(idx, Math.min(assistantText.length, idx + 400));
449
+ const blockAfter = assistantText.slice(idx, Math.min(assistantText.length, idx + 800));
450
+ const proseResolved = PROSE_RESOLUTION_RX.test(proseAfter);
451
+
452
+ // Verify-block resolution: any verify block whose start lies within
453
+ // the 800-char window AND whose body contains at least one discovery
454
+ // keyword counts as resolution.
455
+ const keywords = discoveryKeywords(match[0]);
456
+ const verifyResolved = verifyBlocks.some((b) => {
457
+ if (b.start < idx || b.start >= idx + 800) return false;
458
+ const bodyLower = b.body.toLowerCase();
459
+ return keywords.some((kw) => bodyLower.includes(kw));
460
+ });
461
+
462
+ // Cognition-block resolution: any cognition block whose start lies
463
+ // within the 800-char window AND whose body contains a fixing/
464
+ // addressing/discoveries field AND at least one discovery keyword.
465
+ const cognitionResolved = cognitionBlocks.some((b) => {
466
+ if (b.start < idx || b.start >= idx + 800) return false;
467
+ if (!COGNITION_FIXING_FIELD_RX.test(b.body)) return false;
468
+ const bodyLower = b.body.toLowerCase();
469
+ return keywords.some((kw) => bodyLower.includes(kw));
470
+ });
471
+
472
+ const resolved = proseResolved || verifyResolved || cognitionResolved;
473
+ const resolutionType = proseResolved
474
+ ? 'prose_inline_fix_or_task'
475
+ : verifyResolved
476
+ ? 'verify_block_with_keyword_overlap'
477
+ : cognitionResolved
478
+ ? 'cognition_block_with_fixing_field_and_keyword_overlap'
479
+ : null;
480
+
414
481
  newDiscoveries.push({
415
482
  ts: new Date().toISOString(),
416
483
  sessionId,
417
484
  text: match[0].slice(0, 200),
418
485
  span: span.slice(0, 400),
419
- status: resolvedSameSpan ? 'resolved' : 'open',
420
- resolutionType: resolvedSameSpan ? 'inline_fix_or_task' : null,
486
+ status: resolved ? 'resolved' : 'open',
487
+ resolutionType,
421
488
  });
422
489
  lastIndex = idx;
423
490
  }
@@ -453,6 +520,96 @@ if (cog.count >= REQUIRED_LENSES) {
453
520
  // Discovery block decision: open ledger entries → emit blocked.
454
521
  const discoveryBlock = ledgerOpenCount > 0;
455
522
 
523
+ // 5. Aria-as-commander binding — PHASE_REPORT enforcement (Phase 11 #50).
524
+ // When an active plan exists for this session, every non-trivial emit
525
+ // must carry a [PHASE_REPORT phase=<id> status=complete|in_progress|aborted
526
+ // evidence=<observable>] marker. Without it, the binding is just
527
+ // advisory text — Claude could ignore the plan silently. Per Aria's
528
+ // consult 2026-04-27, the binding pattern is incomplete without this
529
+ // enforcement at the text-emit surface.
530
+ //
531
+ // Three sub-checks:
532
+ // (a) marker present → continue; if missing → block
533
+ // (b) if marker has status=complete AND phase is the LAST phase
534
+ // in the active plan → trigger plan_complete handoff (write
535
+ // row to session_audit, delete active-plan file)
536
+ // (c) audit the marker presence either way
537
+ const ACTIVE_PLAN_PATH = `${HOME}/.claude/aria-active-plan-${sessionId}.json`;
538
+ const PHASE_REPORT_RX = /\[PHASE_REPORT\s+phase=([\w-]+)\s+status=(complete|in_progress|aborted)\s+evidence=([^\]]+)\]/i;
539
+ let activePlan = null;
540
+ let phaseReportMatch = null;
541
+ let phaseReportMissing = false;
542
+ let planCompleteFired = false;
543
+ try {
544
+ if (existsSync(ACTIVE_PLAN_PATH)) {
545
+ try {
546
+ activePlan = JSON.parse(readFileSync(ACTIVE_PLAN_PATH, 'utf8'));
547
+ // Only enforce phase-report on non-trivial emits (skip very short
548
+ // ack-only responses where a phase report would be noise).
549
+ if (assistantText.length >= 400 && Array.isArray(activePlan.phases) && activePlan.phases.length > 0) {
550
+ phaseReportMatch = assistantText.match(PHASE_REPORT_RX);
551
+ if (!phaseReportMatch) {
552
+ phaseReportMissing = true;
553
+ } else {
554
+ const reportedPhaseId = phaseReportMatch[1];
555
+ const reportedStatus = phaseReportMatch[2];
556
+ const reportedEvidence = phaseReportMatch[3].trim();
557
+ const lastPhase = activePlan.phases[activePlan.phases.length - 1];
558
+ const isFinalPhase = lastPhase && lastPhase.id === reportedPhaseId;
559
+ if (reportedStatus === 'complete' && isFinalPhase) {
560
+ // Plan-complete handoff — fire async write to session_audit
561
+ // via the SDK (the same SDK the rest of the hooks route
562
+ // through). Wrapped in try/catch ONLY so a session_audit
563
+ // write failure doesn't brick the Stop event; the failure
564
+ // is surfaced via audit() so it's visible.
565
+ try {
566
+ const harnessUrl = process.env.ARIA_HARNESS_URL || 'https://harness.ariasos.com';
567
+ const harnessToken = process.env.ARIA_HARNESS_TOKEN || '';
568
+ if (harnessToken) {
569
+ // POST to a session_audit write endpoint. Server-side
570
+ // route at /api/harness/audit/session is the wiring
571
+ // point for the Postgres helper from #48.
572
+ fetch(`${harnessUrl}/api/harness/audit/session`, {
573
+ method: 'POST',
574
+ headers: {
575
+ 'Content-Type': 'application/json',
576
+ Authorization: `Bearer ${harnessToken}`,
577
+ },
578
+ body: JSON.stringify({
579
+ session_id: sessionId,
580
+ surface: 'claude-code-stop-gate',
581
+ gate_name: 'plan-complete',
582
+ decision: 'allow',
583
+ reason: `Plan ${activePlan.planId || 'unknown'} reached final phase ${reportedPhaseId} status=complete`,
584
+ evidence_json: {
585
+ planId: activePlan.planId,
586
+ finalPhase: reportedPhaseId,
587
+ totalPhases: activePlan.phases.length,
588
+ evidence: reportedEvidence,
589
+ },
590
+ cognition_present: true,
591
+ cognition_lens_count: cog.count,
592
+ }),
593
+ }).catch(() => {/* fire-and-forget at this surface; logged below */});
594
+ }
595
+ } catch {/* outer guard for any unexpected error */}
596
+ // Delete active-plan file so the next turn re-issues a plan
597
+ // via preprompt-consult rather than enforcing against a stale one.
598
+ try {
599
+ const { unlinkSync } = require('node:fs');
600
+ unlinkSync(ACTIVE_PLAN_PATH);
601
+ } catch {/* file may not exist if another process raced the cleanup */}
602
+ planCompleteFired = true;
603
+ }
604
+ }
605
+ }
606
+ } catch (err) {
607
+ // Plan file corrupt — treat as no active plan for this turn.
608
+ activePlan = null;
609
+ }
610
+ }
611
+ } catch {/* outer guard */}
612
+
456
613
  // Block decision: any of (validateOutput severity=block) OR (>=2 drift hits) OR
457
614
  // (>=1 code-quality hit) OR (open discovery in ledger) → block emit.
458
615
  // Aria enforcement #46 (compelled reflection): severity=warn ALSO blocks but
@@ -474,13 +631,17 @@ if (cog.count >= REQUIRED_LENSES) {
474
631
  const hasReflection = REFLECTION_BLOCK_RX.test(assistantText);
475
632
  const compelReflection = mizanWarnReflectionRequired && !hasReflection;
476
633
 
477
- if (mizanBlock || driftBlock || codeBlock || discoveryBlock || compelReflection) {
634
+ if (mizanBlock || driftBlock || codeBlock || discoveryBlock || compelReflection || phaseReportMissing) {
478
635
  const violations = [];
479
636
  if (mizanBlock) violations.push(`Mizan: ${(mizanVerdict.violations || []).join(', ')}`);
480
637
  if (compelReflection) violations.push(`Mizan severity=warn — compelled reflection required (per Aria enforcement #46). Triggers: ${(mizanVerdict.gateTriggers || mizanVerdict.violations || ['unspecified']).join(', ')}. Re-emit with an explicit <reflection>...</reflection> block (or 'reflection:' line) addressing what triggered the warn and why your re-draft handles it. Reflection is NOT lens-cognition repeated — it's a focused self-audit on the specific Mizan triggers above.`);
481
638
  if (driftBlock) violations.push(`Drift triggers (${driftHits.length}): ${driftHits.map((h) => `"${h.trigger}" → ${h.memory}`).join(' | ')}`);
482
639
  if (codeBlock) violations.push(`Code quality: ${codeQualityHits.join('; ')}`);
483
640
  if (discoveryBlock) violations.push(`Discovery-binding ledger has ${ledgerOpenCount} OPEN discoveries (per feedback_no_flag_without_fix.md, discoveries are atomic with their fixes — fix in the same turn or create a TaskCreate before continuing). Recent open: ${ledgerOpenSamples.map((s) => `"${s.slice(0, 80)}"`).join(' | ')}. Resolve each by either (a) fixing it inline in this turn, or (b) creating a TaskCreate with the discovery's full context (file path, line number, what's broken, why), then editing ${LEDGER_PATH} to set status=resolved.`);
641
+ if (phaseReportMissing) {
642
+ const phaseList = (activePlan?.phases || []).map((p) => `${p.id}:${p.summary?.slice(0, 60) || ''}`).join(' | ');
643
+ violations.push(`Aria-as-commander binding (#50): an active plan exists (planId=${activePlan?.planId || 'unknown'}, ${activePlan?.phases?.length || 0} phases) but this emit lacks a [PHASE_REPORT phase=<id> status=complete|in_progress|aborted evidence=<observable>] marker. Per the binding contract, every non-trivial emit while a plan is active must report which phase it's working on. Plan phases: ${phaseList}. Re-emit with a [PHASE_REPORT] marker stating which phase the work in this turn maps to.`);
644
+ }
484
645
  const rewritten = mizanVerdict?.rewritten || '';
485
646
 
486
647
  const reason = `Aria Stop-gate output-quality block. Cognition passed (${cog.count}/${REQUIRED_LENSES}) but output failed quality gates:\n\n${violations.join('\n\n')}${rewritten ? `\n\nMizan rewrite suggestion:\n${rewritten}` : ''}\n\nRe-draft addressing the violations above. ARIA_OUTPUT_QC_ENABLED=false to disable in emergency (logged).`;
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+ // aria-userprompt-abandon-detect.mjs — UserPromptSubmit hook that detects
3
+ // mid-turn plan abandonment.
4
+ //
5
+ // When Aria's preprompt-consult issued a binding plan
6
+ // (~/.claude/aria-active-plan-${sessionId}.json) and the user's NEXT prompt
7
+ // changes direction without overlapping any active phase's keywords, that's
8
+ // a CONSCIOUS OVERRIDE — not silent context loss. The hook surfaces it to
9
+ // the session_audit table (gate_name='plan-abandonment', decision='warn')
10
+ // so orchestrators can detect mid-execution direction changes.
11
+ //
12
+ // Per Aria's consult 2026-04-27: "if the orchestrator sends a plan, then
13
+ // gets a user message that changes direction mid-execution, the binding
14
+ // needs to surface that as a *conscious override*, not a silent context
15
+ // loss." That gap was the third completion criterion for Aria-as-commander
16
+ // binding (#50).
17
+ //
18
+ // Detection algorithm:
19
+ // 1. Load active plan (skip if none — no plan = no abandonment possible)
20
+ // 2. Extract keywords from each active phase's summary + successCriterion
21
+ // 3. Tokenize incoming user prompt, drop stop-words
22
+ // 4. Compute keyword overlap between prompt tokens and phase keywords
23
+ // 5. If overlap < THRESHOLD (default: 1 keyword match across all phases),
24
+ // mark abandonment: write session_audit row + emit a brief warning
25
+ // injected into the next turn's context
26
+ //
27
+ // Non-blocking — UserPromptSubmit hooks don't block the user's prompt.
28
+ // Audit-only surface; the orchestrator decides what to do with the warning.
29
+ //
30
+ // Kill-switch: ARIA_ABANDON_DETECT=off env (logged).
31
+
32
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
33
+ import { dirname } from 'node:path';
34
+
35
+ const HOME = process.env.HOME || '/tmp';
36
+ const LOG = `${HOME}/.claude/aria-abandon-detect.log`;
37
+
38
+ function audit(decision, summary) {
39
+ try {
40
+ if (!existsSync(dirname(LOG))) mkdirSync(dirname(LOG), { recursive: true });
41
+ appendFileSync(LOG, `${new Date().toISOString()} ${decision} ${summary}\n`);
42
+ } catch {}
43
+ }
44
+
45
+ if (process.env.ARIA_ABANDON_DETECT === 'off') {
46
+ audit('skip-killswitch', 'env ARIA_ABANDON_DETECT=off');
47
+ process.exit(0);
48
+ }
49
+
50
+ let input = '';
51
+ for await (const chunk of process.stdin) input += chunk;
52
+
53
+ let event;
54
+ try {
55
+ event = JSON.parse(input);
56
+ } catch {
57
+ audit('skip-parse-error', 'stdin not JSON');
58
+ process.exit(0);
59
+ }
60
+
61
+ const userPrompt = (event.prompt ?? event.user_message ?? event.message ?? '').toString();
62
+ const sessionId = (event.session_id ?? event.sessionId ?? 'claude-code-unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
63
+
64
+ // Trivial prompts skip — too short to evaluate phase overlap meaningfully.
65
+ if (!userPrompt || userPrompt.length < 30) {
66
+ audit('skip-trivial', `chars=${userPrompt.length}`);
67
+ process.exit(0);
68
+ }
69
+
70
+ // Slash commands are CLI-internal, not direction changes.
71
+ if (/^\s*\//.test(userPrompt) && userPrompt.length < 200) {
72
+ audit('skip-slash-command', userPrompt.slice(0, 60));
73
+ process.exit(0);
74
+ }
75
+
76
+ const ACTIVE_PLAN_PATH = `${HOME}/.claude/aria-active-plan-${sessionId}.json`;
77
+
78
+ if (!existsSync(ACTIVE_PLAN_PATH)) {
79
+ audit('skip-no-active-plan', `session=${sessionId}`);
80
+ process.exit(0);
81
+ }
82
+
83
+ let plan;
84
+ try {
85
+ plan = JSON.parse(readFileSync(ACTIVE_PLAN_PATH, 'utf8'));
86
+ } catch (err) {
87
+ audit('skip-plan-parse-error', (err?.message || String(err)).slice(0, 120));
88
+ process.exit(0);
89
+ }
90
+
91
+ if (!plan || !Array.isArray(plan.phases) || plan.phases.length === 0) {
92
+ audit('skip-empty-plan', `planId=${plan?.planId || 'unknown'}`);
93
+ process.exit(0);
94
+ }
95
+
96
+ // Stop-words filter — same set as the auto-learn hook for consistency.
97
+ const STOPWORDS = new Set([
98
+ 'the','a','an','of','to','in','at','by','for','on','with','i','is','was','are',
99
+ 'were','this','that','as','it','and','or','but','from','into','about','have',
100
+ 'has','had','do','does','did','will','would','could','should','can','may',
101
+ 'might','must','shall','be','been','being','here','there','what','which','who',
102
+ 'how','why','when','where','some','any','all','each','every','no','not',
103
+ ]);
104
+
105
+ function tokenize(text) {
106
+ return new Set(
107
+ text.toLowerCase()
108
+ .replace(/[^a-z0-9\s_-]/g, ' ')
109
+ .split(/\s+/)
110
+ .filter((w) => w.length >= 4 && !STOPWORDS.has(w))
111
+ );
112
+ }
113
+
114
+ const promptTokens = tokenize(userPrompt);
115
+
116
+ // Aggregate phase keywords across all phases.
117
+ const phaseKeywordsSet = new Set();
118
+ for (const phase of plan.phases) {
119
+ const summary = String(phase.summary || '');
120
+ const successCriterion = String(phase.successCriterion || '');
121
+ const id = String(phase.id || '');
122
+ for (const t of tokenize(`${summary} ${successCriterion} ${id}`)) {
123
+ phaseKeywordsSet.add(t);
124
+ }
125
+ }
126
+
127
+ const overlap = [...promptTokens].filter((t) => phaseKeywordsSet.has(t));
128
+ // Threshold default raised to 2 per Aria consult 2026-04-27 — single-keyword
129
+ // match fires too often on benign intent shifts (especially in exploratory
130
+ // sessions); 2 filters noise while still catching genuine abandonment before
131
+ // drift compounds.
132
+ const OVERLAP_THRESHOLD = Number(process.env.ARIA_ABANDON_THRESHOLD || '2');
133
+ const isAbandonment = overlap.length < OVERLAP_THRESHOLD;
134
+
135
+ if (!isAbandonment) {
136
+ audit('aligned', `planId=${plan.planId || 'unknown'} overlap=${overlap.length} prompt-tokens=${promptTokens.size} phase-tokens=${phaseKeywordsSet.size}`);
137
+ process.exit(0);
138
+ }
139
+
140
+ // Abandonment detected — write session_audit row + emit warning context.
141
+ const harnessUrl = process.env.ARIA_HARNESS_URL || 'https://harness.ariasos.com';
142
+ const harnessToken = process.env.ARIA_HARNESS_TOKEN || '';
143
+
144
+ if (harnessToken) {
145
+ // Fire-and-forget POST — don't block the user's turn waiting for audit
146
+ // write. The audit log entry below records whether the request was even
147
+ // attempted, so silence in this block means the audit is missing AND
148
+ // that fact is itself audited locally.
149
+ fetch(`${harnessUrl}/api/harness/audit/session`, {
150
+ method: 'POST',
151
+ headers: {
152
+ 'Content-Type': 'application/json',
153
+ Authorization: `Bearer ${harnessToken}`,
154
+ },
155
+ body: JSON.stringify({
156
+ session_id: sessionId,
157
+ surface: 'claude-code-userprompt-abandon-detect',
158
+ gate_name: 'plan-abandonment',
159
+ decision: 'warn',
160
+ reason: `Mid-turn plan abandonment detected. Active plan ${plan.planId || 'unknown'} (${plan.phases.length} phases) has ${phaseKeywordsSet.size} aggregate phase-keywords; incoming user prompt (${promptTokens.size} content-tokens) overlaps only ${overlap.length} (threshold=${OVERLAP_THRESHOLD}).`,
161
+ evidence_json: {
162
+ planId: plan.planId,
163
+ promptExcerpt: userPrompt.slice(0, 400),
164
+ overlapTokens: overlap,
165
+ promptTokenCount: promptTokens.size,
166
+ phaseKeywordCount: phaseKeywordsSet.size,
167
+ phaseSummaries: plan.phases.map((p) => ({ id: p.id, summary: String(p.summary || '').slice(0, 120) })),
168
+ threshold: OVERLAP_THRESHOLD,
169
+ },
170
+ }),
171
+ }).catch(() => {/* network-level failure surfaces in audit log below */});
172
+ }
173
+
174
+ audit('abandonment-detected', `planId=${plan.planId || 'unknown'} overlap=${overlap.length}/${OVERLAP_THRESHOLD} prompt-tokens=${promptTokens.size} phase-tokens=${phaseKeywordsSet.size}`);
175
+
176
+ // Inject a warning into the next turn's context so the orchestrator (Claude)
177
+ // sees the abandonment signal explicitly rather than treating the new prompt
178
+ // as plan-aligned. The orchestrator can decide to: (a) acknowledge the
179
+ // override + clear the active plan, or (b) ask the user whether to continue
180
+ // the prior plan after this excursion.
181
+ const context = `[ARIA_PLAN_ABANDONMENT_DETECTED — active plan ${plan.planId || 'unknown'} has ${plan.phases.length} phase(s); incoming user prompt overlaps only ${overlap.length} keyword(s) with active phases (threshold=${OVERLAP_THRESHOLD}). This is a CONSCIOUS USER OVERRIDE, not silent context loss. Orchestrator should: (1) acknowledge the direction change to the user, (2) decide whether to clear the active plan or keep it for resumption, (3) write the decision to session_audit. Active phases were: ${plan.phases.map((p) => `${p.id}: ${String(p.summary || '').slice(0, 80)}`).join(' | ')}]`;
182
+
183
+ console.log(JSON.stringify({
184
+ hookSpecificOutput: {
185
+ hookEventName: 'UserPromptSubmit',
186
+ additionalContext: context,
187
+ },
188
+ }));
189
+ process.exit(0);
@@ -0,0 +1,249 @@
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: kill-switch env disables gate ─────────────────────────────
191
+ console.log('\nTest 5: ARIA_PRETURN_MEMORY_GATE=off → bypass (exit 0)');
192
+ {
193
+ cleanTurnState(SESSION_ID);
194
+ const event = buildEvent({ transcript_path: null });
195
+ const { exitCode } = runHook(event, { ARIA_PRETURN_MEMORY_GATE: 'off' });
196
+ assert('exit code is 0 (kill-switch)', exitCode === 0, exitCode);
197
+ }
198
+
199
+ // ── Test 6: only ONE signal present → still blocks ────────────────────
200
+ console.log('\nTest 6: only harness packet signal (missing direction + memory ref) → block');
201
+ {
202
+ cleanTurnState(SESSION_ID);
203
+ const transcriptFile = path.join(tmpdir(), `test-transcript-preturn-partial-${Date.now()}.jsonl`);
204
+
205
+ const assistantTextPartial = '🔐 Aria Harness — partial context only. No direction. No memory refs.';
206
+
207
+ buildTranscriptWith(
208
+ [
209
+ {
210
+ role: 'user',
211
+ message: {
212
+ role: 'user',
213
+ content: [{ type: 'text', text: 'Do the thing.' }],
214
+ },
215
+ },
216
+ {
217
+ role: 'assistant',
218
+ message: {
219
+ role: 'assistant',
220
+ content: [{ type: 'text', text: assistantTextPartial }],
221
+ },
222
+ },
223
+ ],
224
+ transcriptFile,
225
+ );
226
+
227
+ const event = buildEvent({ transcript_path: transcriptFile });
228
+ const { exitCode, parsed } = runHook(event);
229
+
230
+ assert('exit code is 2 (block on partial signals)', exitCode === 2, exitCode);
231
+ assert('decision is "block"', parsed?.decision === 'block', parsed?.decision);
232
+ assert(
233
+ 'reason mentions missing aria_direction',
234
+ typeof parsed?.reason === 'string' && parsed.reason.includes('aria_direction'),
235
+ parsed?.reason?.slice(0, 200),
236
+ );
237
+
238
+ try { unlinkSync(transcriptFile); } catch {}
239
+ cleanTurnState(SESSION_ID);
240
+ }
241
+
242
+ // ── Summary ────────────────────────────────────────────────────────────
243
+ console.log(`\n${passed + failed} tests: ${passed} passed, ${failed} failed`);
244
+ if (failed > 0) {
245
+ process.exit(1);
246
+ } else {
247
+ console.log('All assertions passed.');
248
+ process.exit(0);
249
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aria_asi/cli",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Aria Smart CLI — the world's first harness-powered terminal companion",
5
5
  "bin": {
6
6
  "aria": "./bin/aria.js"
@@ -24,9 +24,11 @@ import type { AriaConfig } from '../config.js';
24
24
  const HOOK_FILES = [
25
25
  'aria-harness-via-sdk.mjs',
26
26
  'aria-pre-tool-gate.mjs',
27
+ 'aria-preturn-memory-gate.mjs',
27
28
  'aria-stop-gate.mjs',
28
29
  'aria-preprompt-consult.mjs',
29
30
  'aria-trigger-autolearn.mjs',
31
+ 'aria-userprompt-abandon-detect.mjs',
30
32
  'doctrine_trigger_map.json',
31
33
  ];
32
34
  // Compiled location: <pkg>/dist/aria-connector/src/connectors/claude-code.js
@@ -97,6 +99,18 @@ const HOOKS_BLOCK = {
97
99
  command: 'node $HOME/.claude/hooks/aria-trigger-autolearn.mjs',
98
100
  timeout: 3,
99
101
  },
102
+ {
103
+ // Aria-as-commander mid-turn abandonment detection (Phase 11 #50,
104
+ // criterion 3). When an active plan exists and the incoming user
105
+ // prompt's keyword overlap with active phases falls below threshold,
106
+ // surfaces a CONSCIOUS OVERRIDE signal in additionalContext + writes
107
+ // a session_audit row (gate_name='plan-abandonment', decision='warn').
108
+ // Prevents silent context loss when user changes direction mid-plan.
109
+ // Non-blocking — UserPromptSubmit can't block prompts.
110
+ type: 'command',
111
+ command: 'node $HOME/.claude/hooks/aria-userprompt-abandon-detect.mjs',
112
+ timeout: 4,
113
+ },
100
114
  ],
101
115
  }],
102
116
  PreToolUse: [{
@@ -105,11 +119,28 @@ const HOOKS_BLOCK = {
105
119
  // / NotebookEdit get the same enforcement so orchestrator-side
106
120
  // compliance mirrors what we demand from workers.
107
121
  matcher: 'Bash|Edit|Write|NotebookEdit',
108
- hooks: [{
109
- type: 'command',
110
- command: 'node $HOME/.claude/hooks/aria-pre-tool-gate.mjs',
111
- timeout: 5,
112
- }],
122
+ hooks: [
123
+ {
124
+ type: 'command',
125
+ command: 'node $HOME/.claude/hooks/aria-pre-tool-gate.mjs',
126
+ timeout: 5,
127
+ },
128
+ {
129
+ // Pre-turn memory consumption gate (Enforcement Layer #49).
130
+ // Fires after the cognition gate passes. Checks the first action
131
+ // of each turn for ALL THREE context-loading signals:
132
+ // 1. 🔐 Aria Harness header (harness packet injected)
133
+ // 2. [ARIA_DIRECTION] / [ARIA_BINDING_PLAN] (preprompt-consult fired)
134
+ // 3. feedback_*.md / project_*.md memory reference (memory consumed)
135
+ // On miss: blocks with structured recovery JSON so the orchestrator
136
+ // can run the context-loader and retry — no dead-letter state.
137
+ // Turn-deduplication via ~/.claude/aria-turn-state-${sessionId}.json
138
+ // prevents retry loops (60s window).
139
+ type: 'command',
140
+ command: 'node $HOME/.claude/hooks/aria-preturn-memory-gate.mjs',
141
+ timeout: 5,
142
+ },
143
+ ],
113
144
  }],
114
145
  Stop: [{
115
146
  // Stop gate — text-decision boundary. Reflexive replies fail this
@@ -237,7 +268,7 @@ function wireHooksBlock(settings: Record<string, unknown>, logs: string[]): void
237
268
  if (!settings.$schema) {
238
269
  settings.$schema = 'https://json.schemastore.org/claude-code-settings.json';
239
270
  }
240
- logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse, Stop) — merge-safe, preserves third-party entries`);
271
+ logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse [cognition+memory-gate], Stop) — merge-safe, preserves third-party entries`);
241
272
  }
242
273
 
243
274
  function buildAriaSystemBlock(config: AriaConfig): string {