@aria_asi/cli 0.2.33 → 0.2.34

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 (74) hide show
  1. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
  2. package/dist/aria-connector/src/connectors/codex.js +47 -0
  3. package/dist/aria-connector/src/connectors/codex.js.map +1 -1
  4. package/dist/assets/hooks/aria-harness-via-sdk.mjs +16 -3
  5. package/dist/assets/hooks/aria-pre-tool-gate.mjs +41 -1
  6. package/dist/assets/hooks/aria-stop-gate.mjs +42 -1
  7. package/dist/assets/hooks/doctrine_trigger_map.json +43 -0
  8. package/dist/assets/hooks/lib/skill-autoload-gate.mjs +14 -1
  9. package/dist/assets/opencode-plugins/harness-context/index.js +1 -1
  10. package/dist/assets/opencode-plugins/harness-gate/index.js +49 -9
  11. package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -1
  12. package/dist/assets/opencode-plugins/harness-stop/index.js +201 -166
  13. package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -1
  14. package/dist/runtime/codex-bridge.mjs +1 -1
  15. package/dist/runtime/discipline/CLAUDE.md +2 -2
  16. package/dist/runtime/discipline/doctrine_trigger_map.json +43 -0
  17. package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +3 -3
  18. package/dist/runtime/doctrine_trigger_map.json +43 -0
  19. package/dist/runtime/hooks/aria-agent-handoff.mjs +247 -0
  20. package/dist/runtime/hooks/aria-agent-ledger-merge.mjs +164 -0
  21. package/dist/runtime/hooks/aria-architect-fallback.mjs +267 -0
  22. package/dist/runtime/hooks/aria-cognition-substrate-binding.mjs +761 -0
  23. package/dist/runtime/hooks/aria-discovery-record.mjs +101 -0
  24. package/dist/runtime/hooks/aria-harness-via-sdk.mjs +544 -0
  25. package/dist/runtime/hooks/aria-import-resolution-gate.mjs +330 -0
  26. package/dist/runtime/hooks/aria-outcome-record.mjs +84 -0
  27. package/dist/runtime/hooks/aria-pre-emit-dryrun.mjs +329 -0
  28. package/dist/runtime/hooks/aria-pre-text-gate.mjs +112 -0
  29. package/dist/runtime/hooks/aria-pre-tool-gate.mjs +2482 -0
  30. package/dist/runtime/hooks/aria-preprompt-consult.mjs +464 -0
  31. package/dist/runtime/hooks/aria-preturn-memory-gate.mjs +647 -0
  32. package/dist/runtime/hooks/aria-repo-doctrine-gate.mjs +429 -0
  33. package/dist/runtime/hooks/aria-stop-gate.mjs +1882 -0
  34. package/dist/runtime/hooks/aria-trigger-autolearn.mjs +229 -0
  35. package/dist/runtime/hooks/aria-userprompt-abandon-detect.mjs +192 -0
  36. package/dist/runtime/hooks/doctrine_trigger_map.json +577 -0
  37. package/dist/runtime/hooks/lib/canonical-lenses.mjs +65 -0
  38. package/dist/runtime/hooks/lib/domain-output-quality.mjs +103 -0
  39. package/dist/runtime/hooks/lib/gate-audit.mjs +43 -0
  40. package/dist/runtime/hooks/lib/gate-loop-state.mjs +50 -0
  41. package/dist/runtime/hooks/lib/hook-message-window.mjs +121 -0
  42. package/dist/runtime/hooks/lib/skill-autoload-gate.mjs +14 -0
  43. package/dist/runtime/hooks/test-aria-preturn-memory-gate.mjs +245 -0
  44. package/dist/runtime/hooks/test-tier-lens-labeling.mjs +367 -0
  45. package/dist/runtime/manifest.json +2 -2
  46. package/dist/runtime/sdk/BUNDLED.json +2 -2
  47. package/dist/runtime/sdk/index.d.ts +39 -0
  48. package/dist/runtime/sdk/index.js +117 -0
  49. package/dist/runtime/sdk/index.js.map +1 -1
  50. package/dist/runtime/sdk/runWithGovernance.d.ts +16 -0
  51. package/dist/runtime/sdk/runWithGovernance.js +54 -0
  52. package/dist/runtime/sdk/runWithGovernance.js.map +1 -0
  53. package/dist/sdk/BUNDLED.json +2 -2
  54. package/dist/sdk/index.d.ts +39 -0
  55. package/dist/sdk/index.js +117 -0
  56. package/dist/sdk/index.js.map +1 -1
  57. package/dist/sdk/runWithGovernance.d.ts +16 -0
  58. package/dist/sdk/runWithGovernance.js +54 -0
  59. package/dist/sdk/runWithGovernance.js.map +1 -0
  60. package/hooks/aria-harness-via-sdk.mjs +16 -3
  61. package/hooks/aria-pre-tool-gate.mjs +41 -1
  62. package/hooks/aria-stop-gate.mjs +42 -1
  63. package/hooks/doctrine_trigger_map.json +43 -0
  64. package/hooks/lib/skill-autoload-gate.mjs +14 -1
  65. package/opencode-plugins/harness-context/index.js +1 -1
  66. package/opencode-plugins/harness-gate/index.js +49 -9
  67. package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +14 -1
  68. package/opencode-plugins/harness-stop/index.js +201 -166
  69. package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +14 -1
  70. package/package.json +12 -5
  71. package/runtime-src/codex-bridge.mjs +1 -1
  72. package/scripts/bundle-sdk.mjs +2 -0
  73. package/scripts/self-test-harness-gates.mjs +79 -0
  74. package/src/connectors/codex.ts +47 -0
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
6
6
  import { createHash } from 'node:crypto';
7
+ import { spawnSync } from 'node:child_process';
7
8
  import { homedir } from 'node:os';
8
9
  import { join } from 'node:path';
9
10
  import { analyzeDomainOutputQuality, extractCodeBlocks } from './lib/domain-output-quality.js';
@@ -17,6 +18,7 @@ const SDK_CANDIDATES = [
17
18
  ];
18
19
  const OWNER_TOKEN_PATH = join(HOME, '.aria', 'owner-token');
19
20
  const LICENSE_PATH = join(HOME, '.aria', 'license.json');
21
+ const GOVERNANCE_GATE_PATH = join(HOME, '.aria', 'bin', 'aria-governance-gate');
20
22
  const RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
21
23
  const RECEIPT_DIR = join(HOME, '.opencode', 'aria-mizan-receipts');
22
24
 
@@ -49,6 +51,25 @@ function isGateBlock(error) {
49
51
  return BLOCK_PREFIX_RX.test(String(error?.message || error || ''));
50
52
  }
51
53
 
54
+ function runUniversalGovernanceGate(payload) {
55
+ if (!existsSync(GOVERNANCE_GATE_PATH)) return null;
56
+ const child = spawnSync(GOVERNANCE_GATE_PATH, {
57
+ input: `${JSON.stringify(payload)}\n`,
58
+ encoding: 'utf8',
59
+ maxBuffer: 1024 * 1024,
60
+ });
61
+ const stdout = String(child.stdout || '').trim();
62
+ let result = null;
63
+ try { result = stdout ? JSON.parse(stdout) : null; } catch {}
64
+ if (child.status !== 0 || result?.ok === false || result?.decision === 'block') {
65
+ throw new Error(
66
+ `=== ARIA UNIVERSAL GOVERNANCE GATE BLOCK ===\n\n` +
67
+ `${stdout || child.stderr || 'aria-governance-gate blocked this output.'}`
68
+ );
69
+ }
70
+ return result;
71
+ }
72
+
52
73
  let _client = null;
53
74
  let _clientError = null;
54
75
  let _lastMizanReceipt = null;
@@ -233,192 +254,206 @@ function detectCognitionLenses(text) {
233
254
  export default async function HarnessStopPlugin(ctx) {
234
255
  process.stderr.write('[harness-stop] Active — SDK-backed text-emission gate\n');
235
256
 
236
- return {
237
- 'session.idle': async (event) => {
238
- process.stderr.write(' [strip gate registed — session i\n');
239
- },
240
-
241
- event: async ({ event }) => {
242
- if (event.type !== 'message.updated') return;
243
- const parts = event.properties?.parts || [];
244
- const text = parts
245
- .filter(p => p.type === 'text')
246
- .map(p => p.text || '')
247
- .join('\n');
257
+ async function validateText(text, eventContext = {}) {
258
+ if (!text || text.length < NON_TRIVIAL_MIN_CHARS) return;
259
+ if (TRIVIAL_ACK_RX.test(text.trim())) return;
248
260
 
249
- if (!text || text.length < NON_TRIVIAL_MIN_CHARS) return;
250
- if (TRIVIAL_ACK_RX.test(text.trim())) return;
251
-
252
- const cog = detectCognitionLenses(text);
253
- const sessionId = process.env.ARIA_SESSION_ID || 'opencode';
254
- const skillGate = evaluateSkillGate({
255
- sessionId,
256
- surface: 'opencode-harness-stop',
257
- text: JSON.stringify(event || {}) + '\n' + text,
258
- isOutputCloseout: true,
259
- autoLoadAvailable: false,
261
+ const cog = detectCognitionLenses(text);
262
+ const sessionId = eventContext.sessionID || process.env.ARIA_SESSION_ID || process.env.OPENCODE_SESSION_ID || 'opencode';
263
+ runUniversalGovernanceGate({
264
+ sessionId,
265
+ sourceRuntime: 'opencode',
266
+ surface: 'opencode-harness-stop',
267
+ text,
268
+ isOutputCloseout: true,
269
+ evidence: makeEvidenceRef('opencode_assistant_output_candidate', text.slice(0, 8000), { sessionId }),
270
+ });
271
+ const skillGate = evaluateSkillGate({
272
+ sessionId,
273
+ surface: 'opencode-harness-stop',
274
+ text: JSON.stringify(eventContext || {}) + '\n' + text,
275
+ isOutputCloseout: true,
276
+ autoLoadAvailable: false,
277
+ });
278
+ if (!skillGate.ok) {
279
+ throw new Error(
280
+ formatBlockReason(
281
+ '=== ARIA SKILL AUTOLOAD GATE BLOCK ===',
282
+ `${formatSkillGateBlock(skillGate)}\nRequired next step: load ${skillGate.missingSkills.join(', ')} before re-emitting this output.`,
283
+ )
284
+ );
285
+ }
286
+ const outputRef = makeEvidenceRef('opencode_assistant_output', text.slice(0, 8000), { sessionId });
287
+ try {
288
+ const mizan = await runtimeMizanPost(text.slice(0, 8000), sessionId, {
289
+ message: text.slice(0, 1000),
290
+ plannedApproach: 'OpenCode stop gate output review',
291
+ outputRef,
292
+ }, {
293
+ output_ref: outputRef,
294
+ cognition_lens_count: cog.count,
260
295
  });
261
- if (!skillGate.ok) {
296
+ _lastMizanReceipt = mizan.receipt || _lastMizanReceipt;
297
+ if (_lastMizanReceipt) {
298
+ const existing = loadReceiptState(sessionId) || {};
299
+ persistReceiptState(sessionId, {
300
+ ...existing,
301
+ updatedAt: new Date().toISOString(),
302
+ sessionId,
303
+ postReceipt: _lastMizanReceipt,
304
+ postResult: mizan.result || null,
305
+ postContract: mizan.contract || null,
306
+ postSummary: mizan.summary || null,
307
+ outputRef,
308
+ });
309
+ }
310
+ if (mizan.receipt?.blocked || mizan.result?.fitrahVetoed || mizan.result?.reAuthorSignal) {
311
+ const details = (mizan.result?.notes || ['post-phase blocked']).slice(0, 4).join(' | ');
262
312
  throw new Error(
263
- formatBlockReason(
264
- '=== ARIA SKILL AUTOLOAD GATE BLOCK ===',
265
- `${formatSkillGateBlock(skillGate)}\nRequired next step: load ${skillGate.missingSkills.join(', ')} before re-emitting this output.`,
266
- )
313
+ formatBlockReason('=== ARIA MIZAN POST BLOCK ===', details)
267
314
  );
268
315
  }
269
- const outputRef = makeEvidenceRef('opencode_assistant_output', text.slice(0, 8000), { sessionId });
316
+ } catch (e) {
317
+ if (isGateBlock(e)) throw e;
318
+ process.stderr.write(`[harness-stop] mizan/post unavailable: ${e.message}\n`);
319
+ }
320
+
321
+ const client = await getClient();
322
+ if (client) {
270
323
  try {
271
- const mizan = await runtimeMizanPost(text.slice(0, 8000), sessionId, {
272
- message: text.slice(0, 1000),
273
- plannedApproach: 'OpenCode stop gate output review',
274
- outputRef,
275
- }, {
276
- output_ref: outputRef,
277
- cognition_lens_count: cog.count,
278
- });
279
- _lastMizanReceipt = mizan.receipt || _lastMizanReceipt;
280
- if (_lastMizanReceipt) {
281
- const existing = loadReceiptState(sessionId) || {};
282
- persistReceiptState(sessionId, {
283
- ...existing,
284
- updatedAt: new Date().toISOString(),
285
- sessionId,
286
- postReceipt: _lastMizanReceipt,
287
- postResult: mizan.result || null,
288
- postContract: mizan.contract || null,
289
- postSummary: mizan.summary || null,
290
- outputRef,
291
- });
292
- }
293
- if (mizan.receipt?.blocked || mizan.result?.fitrahVetoed || mizan.result?.reAuthorSignal) {
294
- const details = (mizan.result?.notes || ['post-phase blocked']).slice(0, 4).join(' | ');
324
+ const result = await runtimeValidateOutput(
325
+ text.slice(0, 8000),
326
+ sessionId,
327
+ ).catch(() => client.validateOutput(
328
+ text.slice(0, 8000),
329
+ sessionId,
330
+ ));
331
+ if (result.severity === 'block') {
295
332
  throw new Error(
296
- formatBlockReason('=== ARIA MIZAN POST BLOCK ===', details)
333
+ formatBlockReason(
334
+ '=== ARIA OUTPUT GATE BLOCK ===',
335
+ `${result.violations.length} violations: ${result.violations.join('; ').slice(0, 500)}`,
336
+ )
337
+ );
338
+ } else if (result.severity === 'warn') {
339
+ throw new Error(
340
+ formatBlockReason(
341
+ '=== ARIA OUTPUT GATE BLOCK ===',
342
+ `SDK returned warn for ${result.violations.length} violation(s): ${(result.violations || []).join('; ').slice(0, 500)}. Warnings are fail-closed on owner-facing output.`,
343
+ )
297
344
  );
298
345
  }
346
+ if (result.gateTriggers?.length) {
347
+ process.stderr.write(`[harness-stop] SDK triggers: ${result.gateTriggers.join(', ')}\n`);
348
+ }
349
+ return;
299
350
  } catch (e) {
300
351
  if (isGateBlock(e)) throw e;
301
- process.stderr.write(`[harness-stop] mizan/post unavailable: ${e.message}\n`);
352
+ process.stderr.write(`[harness-stop] SDK validateOutput failed: ${e.message} — falling through to local gate\n`);
302
353
  }
354
+ }
303
355
 
304
- // Try SDK validateOutput() — Mizan classifier-backed validation
305
- const client = await getClient();
306
- if (client) {
307
- try {
308
- const result = await runtimeValidateOutput(
309
- text.slice(0, 8000),
310
- sessionId,
311
- ).catch(() => client.validateOutput(
312
- text.slice(0, 8000),
313
- sessionId,
314
- ));
315
- if (result.severity === 'block') {
316
- throw new Error(
317
- formatBlockReason(
318
- '=== ARIA OUTPUT GATE BLOCK ===',
319
- `${result.violations.length} violations: ${result.violations.join('; ').slice(0, 500)}`,
320
- )
321
- );
322
- } else if (result.severity === 'warn') {
323
- process.stderr.write(
324
- `[harness-stop] SDK WARN — ${result.violations.length} violations (non-blocking)\n`
325
- );
326
- }
327
- // Log gate triggers
328
- if (result.gateTriggers?.length) {
329
- process.stderr.write(`[harness-stop] SDK triggers: ${result.gateTriggers.join(', ')}\n`);
330
- }
331
- return;
332
- } catch (e) {
333
- if (isGateBlock(e)) throw e;
334
- process.stderr.write(`[harness-stop] SDK validateOutput failed: ${e.message} — falling through to local gate\n`);
356
+ const triggerMapPaths = [
357
+ `${HOME}/.aria/runtime/discipline/doctrine_trigger_map.json`,
358
+ `${HOME}/.aria/runtime/doctrine_trigger_map.json`,
359
+ `${HOME}/.opencode/doctrine_trigger_map.json`,
360
+ `${HOME}/.codex/doctrine_trigger_map.json`,
361
+ `${HOME}/.claude/hooks/doctrine_trigger_map.json`,
362
+ `${HOME}/.claude/projects/-home-hamzaibrahim1/memory/doctrine_trigger_map.json`,
363
+ ];
364
+ let driftHits = [];
365
+ try {
366
+ const triggerMapPath = triggerMapPaths.find((candidate) => existsSync(candidate));
367
+ if (triggerMapPath) {
368
+ const triggerMap = JSON.parse(readFileSync(triggerMapPath, 'utf8'));
369
+ const lower = text.toLowerCase();
370
+ for (const t of triggerMap.triggers || []) {
371
+ try {
372
+ const rx = new RegExp(t.trigger, 'i');
373
+ if (rx.test(lower)) {
374
+ const memCited = t.memory && lower.includes(t.memory.replace(/\.md$/, '').toLowerCase());
375
+ if (!memCited) driftHits.push(t.trigger);
376
+ }
377
+ } catch {}
335
378
  }
336
379
  }
380
+ } catch {}
337
381
 
338
- // Local fallback gate
339
- // Scan drift triggers
340
- const triggerMapPaths = [
341
- `${HOME}/.aria/runtime/discipline/doctrine_trigger_map.json`,
342
- `${HOME}/.aria/runtime/doctrine_trigger_map.json`,
343
- `${HOME}/.opencode/doctrine_trigger_map.json`,
344
- `${HOME}/.codex/doctrine_trigger_map.json`,
345
- `${HOME}/.claude/hooks/doctrine_trigger_map.json`,
346
- `${HOME}/.claude/projects/-home-hamzaibrahim1/memory/doctrine_trigger_map.json`,
347
- ];
348
- let driftHits = [];
349
- try {
350
- const triggerMapPath = triggerMapPaths.find((candidate) => existsSync(candidate));
351
- if (triggerMapPath) {
352
- const triggerMap = JSON.parse(readFileSync(triggerMapPath, 'utf8'));
353
- const lower = text.toLowerCase();
354
- for (const t of triggerMap.triggers || []) {
355
- try {
356
- const rx = new RegExp(t.trigger, 'i');
357
- if (rx.test(lower)) {
358
- const memCited = t.memory && lower.includes(t.memory.replace(/\.md$/, '').toLowerCase());
359
- if (!memCited) driftHits.push(t.trigger);
360
- }
361
- } catch {}
362
- }
363
- }
364
- } catch {}
382
+ if (cog.count < REQUIRED_LENSES || driftHits.length >= 2) {
383
+ throw new Error(
384
+ formatBlockReason(
385
+ '=== ARIA LOCAL OUTPUT BLOCK ===',
386
+ `cognition=${cog.count}/${REQUIRED_LENSES}; drift=${driftHits.length}`,
387
+ )
388
+ );
389
+ }
365
390
 
366
- if (cog.count < REQUIRED_LENSES || driftHits.length >= 2) {
367
- throw new Error(
368
- formatBlockReason(
369
- '=== ARIA LOCAL OUTPUT BLOCK ===',
370
- `cognition=${cog.count}/${REQUIRED_LENSES}; drift=${driftHits.length}`,
371
- )
372
- );
373
- }
391
+ if (DECISION_SIGNAL_RX.test(text) && !APPLIED_COGNITION_RX.test(text)) {
392
+ throw new Error(
393
+ formatBlockReason(
394
+ '=== ARIA LOCAL OUTPUT BLOCK ===',
395
+ 'decision-bearing output lacks required applied_cognition binding fields',
396
+ )
397
+ );
398
+ }
374
399
 
375
- if (DECISION_SIGNAL_RX.test(text) && !APPLIED_COGNITION_RX.test(text)) {
376
- throw new Error(
377
- formatBlockReason(
378
- '=== ARIA LOCAL OUTPUT BLOCK ===',
379
- 'decision-bearing output lacks required applied_cognition binding fields',
380
- )
381
- );
382
- }
400
+ const domainQuality = analyzeDomainOutputQuality(text, { codeBlocks: extractCodeBlocks(text) });
401
+ if (domainQuality.blockers.length > 0) {
402
+ throw new Error(
403
+ formatBlockReason(
404
+ '=== ARIA LOCAL OUTPUT BLOCK ===',
405
+ `domain output QA (${domainQuality.domains.join(', ') || 'general'}): ${domainQuality.blockers.join('; ')}`,
406
+ )
407
+ );
408
+ }
383
409
 
384
- const domainQuality = analyzeDomainOutputQuality(text, { codeBlocks: extractCodeBlocks(text) });
385
- if (domainQuality.blockers.length > 0) {
386
- throw new Error(
387
- formatBlockReason(
388
- '=== ARIA LOCAL OUTPUT BLOCK ===',
389
- `domain output QA (${domainQuality.domains.join(', ') || 'general'}): ${domainQuality.blockers.join('; ')}`,
390
- )
391
- );
392
- }
410
+ try {
411
+ const existing = loadReceiptState(sessionId);
412
+ await runtimeDecisionLog({
413
+ decision_type: 'turn_action',
414
+ category: 'agentic_execution',
415
+ context: `opencode stop-gate turn (chars=${text.length})`,
416
+ decision: 'turn completed',
417
+ reasoning: cog.count > 0
418
+ ? `Cognition lenses applied: ${cog.names.join(', ')}.`
419
+ : 'No explicit cognition block in turn.',
420
+ outcome: 'pending',
421
+ outcome_details: {
422
+ expected: null,
423
+ immediate_actual: null,
424
+ anchors: [],
425
+ },
426
+ expected_outcome: null,
427
+ metadata: {
428
+ pre_receipt_id: existing?.receipt?.receiptId || null,
429
+ post_receipt_id: _lastMizanReceipt?.receiptId || null,
430
+ output_ref: outputRef,
431
+ },
432
+ source: 'opencode-stop-gate-runtime',
433
+ model_used: process.env.OPENCODE_MODEL || 'opencode',
434
+ });
435
+ } catch (e) {
436
+ process.stderr.write(`[harness-stop] decision/log unavailable: ${e.message}\n`);
437
+ }
438
+ }
393
439
 
394
- try {
395
- const existing = loadReceiptState(sessionId);
396
- await runtimeDecisionLog({
397
- decision_type: 'turn_action',
398
- category: 'agentic_execution',
399
- context: `opencode stop-gate turn (chars=${text.length})`,
400
- decision: 'turn completed',
401
- reasoning: cog.count > 0
402
- ? `Cognition lenses applied: ${cog.names.join(', ')}.`
403
- : 'No explicit cognition block in turn.',
404
- outcome: 'pending',
405
- outcome_details: {
406
- expected: null,
407
- immediate_actual: null,
408
- anchors: [],
409
- },
410
- expected_outcome: null,
411
- metadata: {
412
- pre_receipt_id: existing?.receipt?.receiptId || null,
413
- post_receipt_id: _lastMizanReceipt?.receiptId || null,
414
- output_ref: outputRef,
415
- },
416
- source: 'opencode-stop-gate-runtime',
417
- model_used: process.env.OPENCODE_MODEL || 'opencode',
418
- });
419
- } catch (e) {
420
- process.stderr.write(`[harness-stop] decision/log unavailable: ${e.message}\n`);
421
- }
440
+ return {
441
+ 'session.idle': async (event) => {
442
+ process.stderr.write('[harness-stop] session idle heartbeat — text-emission gate registered\n');
443
+ },
444
+
445
+ event: async ({ event }) => {
446
+ if (event.type !== 'message.updated') return;
447
+ const parts = event.properties?.parts || [];
448
+ const text = parts
449
+ .filter(p => p.type === 'text')
450
+ .map(p => p.text || '')
451
+ .join('\n');
452
+ await validateText(text, { event });
453
+ },
454
+
455
+ 'experimental.text.complete': async (input, output) => {
456
+ await validateText(String(output?.text || ''), input || {});
422
457
  },
423
458
  };
424
459
  }
@@ -1 +1,14 @@
1
- export * from '../../../../../ops/claude-hooks/lib/skill-autoload-gate.mjs';
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { tmpdir } from 'node:os';
4
+ const RECEIPT_ROOT = process.env.ARIA_SKILL_RECEIPT_DIR || join(tmpdir(), 'aria-skill-receipts');
5
+ const ALIASES = new Map([['deploy', 'aria-harness-deploy'], ['output', 'aria-harness-output-discipline'], ['repo', 'aria-repo-doctrine'], ['forge', 'aria-forge-guardrails']]);
6
+ const RX = { deploy: /deploy-service\.sh|kubectl\s+(?:apply|set|rollout|delete|scale)|helm\s+upgrade|terraform\s+apply|docker\s+push/i, mutationTool: /^(?:edit|write|notebookedit|patch|apply_patch)$/i, mutation: /apply_patch|write file|edit file|modify|delete file|migration|handler|route|runtime|hook|plugin|\btest\b|smoke script/i, strip: /remove|delete|strip|drop|omit|disable|bypass|skip|stub|mock|fake|placeholder|temporary|quick scaffold|band-aid/i, readiness: /production-ready|ready for production|works in general|general readiness|client packages?|npm packages?|SDKs?|runtimes?|harnesses?|release-ready|ship-ready/i, narrow: /single flow|one flow|narrow e2e|covered flow|specific path|widget flow/i, completion: /done|complete|completed|ready|verified|fixed|shipped|implemented|production-ready/i, badProof: /but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped|unresolved|follow-up|failed|failing|error|red|not run|could not verify|untested|no proof|missing proof|without proof/i, advisory: /non-blocking|warning only|warn only|advisory|fall through|falls through|fail open|soft fail|logged and continue|quality gate warning/i, success: /(?:verified|passed|success|successful|green|ok)\s*[:=\-].{0,120}(?:npm|node|playwright|jest|vitest|build|test|lint|typecheck|curl|kubectl|self-test|e2e|probe|smoke)|(?:npm|node|playwright|jest|vitest|build|test|lint|typecheck|curl|kubectl).{0,120}(?:passed|success|successful|green|exit\s*0)/i, resubmit: /re-?submission|resubmit/i, rewrite: /re-?write|rewrite|fix/i, retest: /re-?test|retest|rerun/i, aria: /ARIA console|Aria console|\/chat|aria-pipeline-mcp|aria_chat|escalat(?:e|ion).{0,80}ARIA/i };
7
+ function normalizeSkillName(skill) { return ALIASES.get(String(skill || '').trim()) || String(skill || '').trim(); }
8
+ function sessionDir(sessionId) { return join(RECEIPT_ROOT, encodeURIComponent(String(sessionId || 'unknown'))); }
9
+ function readReceiptSkills(sessionId) { const dir = sessionDir(sessionId); if (!existsSync(dir)) return new Set(); const skills = new Set(); for (const name of readdirSync(dir)) { if (!name.endsWith('.json')) continue; try { const receipt = JSON.parse(readFileSync(join(dir, name), 'utf8')); if (receipt?.skill) skills.add(normalizeSkillName(receipt.skill)); } catch {} } return skills; }
10
+ function readInlineSkills(text) { const skills = new Set(); const value = String(text || ''); for (const match of value.matchAll(/<skill_content\s+name=["']([^"']+)["']/gi)) skills.add(normalizeSkillName(match[1])); return skills; }
11
+ export function recordSkillLoaded({ sessionId, skill, surface = 'unknown', metadata = {} } = {}) { const normalized = normalizeSkillName(skill); if (!normalized) throw new Error('recordSkillLoaded requires a skill name'); const dir = sessionDir(sessionId); mkdirSync(dir, { recursive: true }); const receipt = { skill: normalized, surface, metadata, recordedAt: new Date().toISOString() }; writeFileSync(join(dir, `${encodeURIComponent(normalized)}.json`), `${JSON.stringify(receipt, null, 2)}\n`); return receipt; }
12
+ export function classifyRequiredSkills({ text = '', action = '', toolName = '', filePath = '', isDeploy = false, isMutation = false, isOutputCloseout = false } = {}) { const combined = [text, action, toolName, filePath].filter(Boolean).join('\n'); const required = new Set(); const reasons = []; const recoveryMissing = []; if (isDeploy || RX.deploy.test(combined)) { required.add('aria-harness-deploy'); required.add('aria-forge-guardrails'); reasons.push('deploy/shared-infrastructure action requires fail-closed deploy and forge guardrails'); } if (isMutation || RX.mutationTool.test(toolName)) { required.add('aria-repo-doctrine'); reasons.push('repository/runtime mutation requires repo doctrine'); } if (RX.strip.test(combined)) { required.add('aria-harness-no-stripping'); reasons.push('strip/remove/bypass language requires no-stripping gate'); } if (isOutputCloseout && RX.completion.test(combined)) { required.add('aria-harness-output-discipline'); reasons.push('owner-facing completion/readiness claim requires output discipline'); if (!RX.success.test(combined)) recoveryMissing.push('successful proof from a concrete command/probe'); } if (RX.readiness.test(combined)) { required.add('architecture-decision'); required.add('testing-strategy'); required.add('aria-forge-guardrails'); required.add('aria-harness-output-discipline'); reasons.push('broad production/package/SDK/runtime readiness claim requires architecture, testing, and forge guardrails'); } if (RX.readiness.test(combined) && RX.narrow.test(combined)) { required.add('testing-strategy'); required.add('aria-forge-guardrails'); reasons.push('narrow e2e proof cannot support broad readiness claim without readiness-matrix discipline'); } if (RX.completion.test(combined) && RX.badProof.test(combined)) { required.add('aria-harness-output-discipline'); required.add('aria-forge-guardrails'); reasons.push('completion claim with unresolved or failed proof requires recovery cycle'); if (!RX.resubmit.test(combined)) recoveryMissing.push('re-submission'); if (!RX.rewrite.test(combined)) recoveryMissing.push('re-write'); if (!RX.retest.test(combined)) recoveryMissing.push('re-test'); if (!RX.aria.test(combined)) recoveryMissing.push('ARIA console escalation'); } if (RX.advisory.test(combined)) { required.add('aria-forge-guardrails'); required.add('aria-harness-output-discipline'); reasons.push('advisory/fail-open gate language requires fail-closed hardening discipline'); } return { requiredSkills: [...required].sort(), reasons, recoveryMissing }; }
13
+ export function evaluateSkillGate(options = {}) { const classified = classifyRequiredSkills(options); const text = [options.text, options.action].filter(Boolean).join('\n'); const loaded = new Set([...readReceiptSkills(options.sessionId), ...readInlineSkills(text)]); const missingSkills = classified.requiredSkills.filter((skill) => !loaded.has(skill)); const recoveryMissing = classified.recoveryMissing || []; return { ok: missingSkills.length === 0 && recoveryMissing.length === 0, surface: options.surface || 'unknown', sessionId: options.sessionId || 'unknown', requiredSkills: classified.requiredSkills, loadedSkills: [...loaded].sort(), missingSkills, recoveryMissing, reasons: classified.reasons, autoLoadAvailable: options.autoLoadAvailable === true }; }
14
+ export function formatSkillGateBlock(result = {}) { const missing = Array.isArray(result.missingSkills) ? result.missingSkills : []; const recovery = Array.isArray(result.recoveryMissing) ? result.recoveryMissing : []; const reasons = Array.isArray(result.reasons) ? result.reasons : []; return ['=== ARIA SKILL AUTOLOAD GATE BLOCK ===', `surface: ${result.surface || 'unknown'}`, `missing_skills: ${missing.length ? missing.join(', ') : '(none)'}`, `missing_recovery_cycle: ${recovery.length ? recovery.join(', ') : '(none)'}`, `required_skills: ${(result.requiredSkills || []).join(', ') || '(none)'}`, reasons.length ? `reasons: ${reasons.join(' | ')}` : 'reasons: no classifier reason recorded', 'counter_action: re-submit, re-write, re-test, and escalate through ARIA console until successful proof exists. Do not downgrade this to an advisory warning.'].join('\n'); }
@@ -6,7 +6,7 @@ import { homedir } from 'node:os';
6
6
  import { delimiter, dirname, resolve } from 'node:path';
7
7
  import { spawn, spawnSync } from 'node:child_process';
8
8
  import { createRequire } from 'node:module';
9
- import { evaluateSkillGate, formatSkillGateBlock } from '../../../ops/claude-hooks/lib/skill-autoload-gate.mjs';
9
+ import { evaluateSkillGate, formatSkillGateBlock } from './hooks/lib/skill-autoload-gate.mjs';
10
10
 
11
11
  const require = createRequire(import.meta.url);
12
12
  const { WebSocketServer, WebSocket } = require('ws');
@@ -18,7 +18,7 @@ If compaction, gate deadlock, or repeated drift happens, restart from that order
18
18
 
19
19
  ## What this SDK is
20
20
 
21
- `@aria/harness-http-client` is the Aria harness SDK. It binds a process
21
+ `@aria_asi/harness-http-client` is the Aria harness SDK. It binds a process
22
22
  (worker, autonomous loop, dispatched artifact) to Aria's three-layer
23
23
  enforcement substrate so the process cannot drift from Aria's doctrine,
24
24
  axioms, frames, and memory.
@@ -221,7 +221,7 @@ The Stop-gate scans output for these patterns. When matched, the response is rej
221
221
  ### 1. Build a harness-bound LLM call
222
222
 
223
223
  ```ts
224
- import { getBoundHarnessAndPrompt, bindingContext } from '@aria/harness-http-client';
224
+ import { getBoundHarnessAndPrompt, bindingContext } from '@aria_asi/harness-http-client';
225
225
  import { runLayer3Gates } from '@aria/gate-runtime';
226
226
 
227
227
  const { prompt: harnessContext, packet } = await getBoundHarnessAndPrompt(
@@ -508,6 +508,49 @@
508
508
  "counter_action": "Create or reuse one turn substrate object containing embedding, perturb snapshot, ProjectAllDomains result, awakenAria Garden block, DeepSoul/Noor/shards/Mizan signals; pass it downstream.",
509
509
  "message": "Duplicate projection path detected. Replace with one per-turn cognition packet and shared consumption."
510
510
  },
511
+ {
512
+ "trigger_id": "premature_task_closeout",
513
+ "trigger": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
514
+ "rx": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
515
+ "doctrine": "memory:feedback_no_premature_task_closeout.md",
516
+ "memory": "feedback_no_premature_task_closeout.md",
517
+ "severity": "block",
518
+ "teaching": "A task is not complete while material blockers remain. Completion claims must match the verified scope.",
519
+ "counter_action": "Do not close the task. Fix the blocker now, or create an owner-visible durable task with the exact failing surface and verification gap.",
520
+ "message": "Premature closeout detected: completion language coexists with unresolved blockers. Fix or durably track before emitting."
521
+ },
522
+ {
523
+ "trigger_id": "narrow_e2e_overclaim",
524
+ "trigger": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
525
+ "rx": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
526
+ "doctrine": "memory:feedback_narrow_e2e_overclaim.md",
527
+ "memory": "feedback_narrow_e2e_overclaim.md",
528
+ "severity": "block",
529
+ "teaching": "A narrow e2e does not prove general production readiness for SDKs, npm packages, runtimes, clients, or harnesses.",
530
+ "counter_action": "Run the general readiness matrix or explicitly limit the claim to the verified surface. Do not imply general readiness from one app flow.",
531
+ "message": "Narrow proof overclaim detected. General readiness requires the client/package/runtime/harness matrix."
532
+ },
533
+ {
534
+ "trigger_id": "advisory_gate_not_gate",
535
+ "trigger": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
536
+ "rx": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
537
+ "doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
538
+ "memory": "feedback_advisory_gate_is_not_gate.md",
539
+ "severity": "block",
540
+ "teaching": "A gate that only warns or falls through is not a gate. Enforcement must fail closed where quality or doctrine is required.",
541
+ "counter_action": "Convert advisory paths to blocking errors, add a self-test that proves rejection, and install the hardened gate into each consumer surface.",
542
+ "message": "Advisory gate bypass detected. Convert to fail-closed enforcement and prove it with a blocking self-test."
543
+ },
544
+ {
545
+ "trigger_id": "start_new_session_as_gate_fix",
546
+ "trigger": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
547
+ "rx": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
548
+ "doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
549
+ "memory": "feedback_advisory_gate_is_not_gate.md",
550
+ "severity": "block",
551
+ "teaching": "A new session is not an enforcement fix. It only reloads whatever gate contract exists; broken gates remain broken.",
552
+ "counter_action": "Fix the runtime/plugin/hook contract, reinstall it, and run a live bad-action/bad-output self-test that proves blocking."
553
+ },
511
554
  {
512
555
  "trigger_id": "registry_image_drift",
513
556
  "trigger": "image.?pull|ErrImageNeverPull|ImagePullBackOff|registry gc|image.*missing|image.*not.*found",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: aria-harness-onboarding
3
- description: Use when starting a new project that imports @aria/harness-http-client OR when an agent first encounters the SDK in a repo. Loads the foundational 3-layer harness model (packet/BIND/gate-runtime), the doctrine reference list, and points to the four sibling skills (aria-harness-deploy, aria-harness-substrate-binding, aria-harness-no-stripping, aria-harness-output-discipline). Read this once per project to inherit the discipline.
3
+ description: Use when starting a new project that imports @aria_asi/harness-http-client OR when an agent first encounters the SDK in a repo. Loads the foundational 3-layer harness model (packet/BIND/gate-runtime), the doctrine reference list, and points to the four sibling skills (aria-harness-deploy, aria-harness-substrate-binding, aria-harness-no-stripping, aria-harness-output-discipline). Read this once per project to inherit the discipline.
4
4
  ---
5
5
 
6
6
  # Aria Harness Onboarding
@@ -9,7 +9,7 @@ You are about to author code that uses the Aria harness SDK. This skill loads th
9
9
 
10
10
  ## What the SDK is
11
11
 
12
- `@aria/harness-http-client` binds a process (worker, autonomous loop, dispatched artifact) to Aria's three-layer enforcement substrate so the process cannot drift from Aria's doctrine, axioms, frames, and memory.
12
+ `@aria_asi/harness-http-client` binds a process (worker, autonomous loop, dispatched artifact) to Aria's three-layer enforcement substrate so the process cannot drift from Aria's doctrine, axioms, frames, and memory.
13
13
 
14
14
  ## The three layers — every consumer enforces all three
15
15
 
@@ -36,7 +36,7 @@ This onboarding skill is the entry point. Four sibling skills auto-trigger on sp
36
36
  ## Build a harness-bound LLM call (the basic pattern)
37
37
 
38
38
  ```ts
39
- import { getBoundHarnessAndPrompt, bindingContext } from '@aria/harness-http-client';
39
+ import { getBoundHarnessAndPrompt, bindingContext } from '@aria_asi/harness-http-client';
40
40
  import { runLayer3Gates } from '@aria/gate-runtime';
41
41
 
42
42
  const { prompt: harnessContext, packet } = await getBoundHarnessAndPrompt(
@@ -508,6 +508,49 @@
508
508
  "counter_action": "Create or reuse one turn substrate object containing embedding, perturb snapshot, ProjectAllDomains result, awakenAria Garden block, DeepSoul/Noor/shards/Mizan signals; pass it downstream.",
509
509
  "message": "Duplicate projection path detected. Replace with one per-turn cognition packet and shared consumption."
510
510
  },
511
+ {
512
+ "trigger_id": "premature_task_closeout",
513
+ "trigger": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
514
+ "rx": "(?:done|complete|completed|ready|verified|fixed).{0,120}(?:but|except|caveat|remaining|not yet|still|separate|later|blocked|skipped)",
515
+ "doctrine": "memory:feedback_no_premature_task_closeout.md",
516
+ "memory": "feedback_no_premature_task_closeout.md",
517
+ "severity": "block",
518
+ "teaching": "A task is not complete while material blockers remain. Completion claims must match the verified scope.",
519
+ "counter_action": "Do not close the task. Fix the blocker now, or create an owner-visible durable task with the exact failing surface and verification gap.",
520
+ "message": "Premature closeout detected: completion language coexists with unresolved blockers. Fix or durably track before emitting."
521
+ },
522
+ {
523
+ "trigger_id": "narrow_e2e_overclaim",
524
+ "trigger": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
525
+ "rx": "(?:production-ready|ready for production|works in general|client packages?|npm packages?|SDKs?|runtimes?|harnesses?).{0,220}(?:deal|single flow|one flow|widget|one service|specific path|covered flow)",
526
+ "doctrine": "memory:feedback_narrow_e2e_overclaim.md",
527
+ "memory": "feedback_narrow_e2e_overclaim.md",
528
+ "severity": "block",
529
+ "teaching": "A narrow e2e does not prove general production readiness for SDKs, npm packages, runtimes, clients, or harnesses.",
530
+ "counter_action": "Run the general readiness matrix or explicitly limit the claim to the verified surface. Do not imply general readiness from one app flow.",
531
+ "message": "Narrow proof overclaim detected. General readiness requires the client/package/runtime/harness matrix."
532
+ },
533
+ {
534
+ "trigger_id": "advisory_gate_not_gate",
535
+ "trigger": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
536
+ "rx": "(?:non-blocking|warn(?:ing)? only|advisory|falls? through|fail open|soft fail|log(?:ged)? and continue|quality gate warning)",
537
+ "doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
538
+ "memory": "feedback_advisory_gate_is_not_gate.md",
539
+ "severity": "block",
540
+ "teaching": "A gate that only warns or falls through is not a gate. Enforcement must fail closed where quality or doctrine is required.",
541
+ "counter_action": "Convert advisory paths to blocking errors, add a self-test that proves rejection, and install the hardened gate into each consumer surface.",
542
+ "message": "Advisory gate bypass detected. Convert to fail-closed enforcement and prove it with a blocking self-test."
543
+ },
544
+ {
545
+ "trigger_id": "start_new_session_as_gate_fix",
546
+ "trigger": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
547
+ "rx": "(?:start|open|begin).{0,40}(?:new|fresh).{0,30}session.{0,120}(?:fix|gate|harness|enforcement)",
548
+ "doctrine": "memory:feedback_advisory_gate_is_not_gate.md",
549
+ "memory": "feedback_advisory_gate_is_not_gate.md",
550
+ "severity": "block",
551
+ "teaching": "A new session is not an enforcement fix. It only reloads whatever gate contract exists; broken gates remain broken.",
552
+ "counter_action": "Fix the runtime/plugin/hook contract, reinstall it, and run a live bad-action/bad-output self-test that proves blocking."
553
+ },
511
554
  {
512
555
  "trigger_id": "registry_image_drift",
513
556
  "trigger": "image.?pull|ErrImageNeverPull|ImagePullBackOff|registry gc|image.*missing|image.*not.*found",