@aria_asi/cli 0.2.31 → 0.2.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.js +30 -3
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
- package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +25 -9
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/must-read.js +4 -0
- package/dist/aria-connector/src/connectors/must-read.js.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.js +25 -9
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
- package/dist/assets/hooks/aria-agent-handoff.mjs +23 -0
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +69 -3
- package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +198 -17
- package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +30 -2
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +31 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +127 -35
- package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
- package/dist/assets/opencode-plugins/harness-gate/index.js +17 -4
- package/dist/assets/opencode-plugins/harness-stop/index.js +40 -6
- package/dist/runtime/discipline/CLAUDE.md +16 -0
- package/dist/runtime/discipline/doctrine_trigger_map.json +55 -0
- package/dist/runtime/doctrine_trigger_map.json +55 -0
- package/dist/runtime/harness-daemon.mjs +30 -3
- package/dist/runtime/manifest.json +1 -1
- package/dist/runtime/sdk/BUNDLED.json +1 -1
- package/dist/runtime/sdk/index.d.ts +5 -0
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +46 -1
- package/dist/sdk/BUNDLED.json +1 -1
- package/dist/sdk/index.d.ts +5 -0
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-agent-handoff.mjs +23 -0
- package/hooks/aria-cognition-substrate-binding.mjs +69 -3
- package/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/hooks/aria-pre-tool-gate.mjs +198 -17
- package/hooks/aria-preprompt-consult.mjs +28 -2
- package/hooks/aria-preturn-memory-gate.mjs +30 -2
- package/hooks/aria-repo-doctrine-gate.mjs +31 -1
- package/hooks/aria-stop-gate.mjs +127 -35
- package/hooks/doctrine_trigger_map.json +55 -0
- package/opencode-plugins/harness-gate/index.js +17 -4
- package/opencode-plugins/harness-stop/index.js +40 -6
- package/package.json +1 -1
- package/runtime-src/harness-daemon.mjs +30 -3
- package/runtime-src/service.mjs +46 -1
- package/src/connectors/claude-code.ts +31 -3
- package/src/connectors/codex.ts +25 -9
- package/src/connectors/must-read.ts +4 -0
- package/src/connectors/opencode.ts +25 -9
|
@@ -332,12 +332,29 @@ function actionMatchesPattern(action, pattern, target) {
|
|
|
332
332
|
}
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
+
function isOperationalMaintenanceCommand(command) {
|
|
336
|
+
const cmd = String(command || '').trim().replace(/\s+/g, ' ');
|
|
337
|
+
if (!cmd || /[;&|`$()]/.test(cmd)) return false;
|
|
338
|
+
|
|
339
|
+
// A rollout restart requeues existing pod templates; it does not change images,
|
|
340
|
+
// manifests, or cluster policy. Keep this narrow so deploy/apply/delete still gate.
|
|
341
|
+
const kubectlGlobalFlag = String.raw`(?:--(?:namespace|context|kubeconfig|as|as-group|cluster|user|server|request-timeout|field-manager)(?:=|\s+)\S+|-[A-Za-z](?:\s+\S+)?)`;
|
|
342
|
+
const kubectlTailFlag = String.raw`(?:--(?:namespace|context|request-timeout|field-manager|selector)(?:=|\s+)\S+|-[A-Za-z](?:\s+\S+)?)`;
|
|
343
|
+
const workloadTarget = String.raw`(?:deploy(?:ment)?|statefulset|sts|daemonset|ds)(?:\/[^\s]+|\s+[^\s-][^\s]*)`;
|
|
344
|
+
const rolloutRestartRx = new RegExp(
|
|
345
|
+
String.raw`^kubectl(?:\s+${kubectlGlobalFlag})*\s+rollout\s+restart\s+${workloadTarget}(?:\s+${kubectlTailFlag})*$`,
|
|
346
|
+
'i',
|
|
347
|
+
);
|
|
348
|
+
return rolloutRestartRx.test(cmd);
|
|
349
|
+
}
|
|
350
|
+
|
|
335
351
|
function classifyToolForBinding(toolName, command, filePath) {
|
|
336
352
|
// Map Claude Code tool + payload to a binding-vocabulary action.
|
|
337
353
|
if (toolName === 'Read' || toolName === 'Glob' || toolName === 'Grep') return { action: 'read', target: filePath || '' };
|
|
338
354
|
if (toolName === 'Edit' || toolName === 'Write' || toolName === 'NotebookEdit') return { action: 'edit', target: filePath || '' };
|
|
339
355
|
if (toolName === 'Bash') {
|
|
340
356
|
const cmd = String(command || '');
|
|
357
|
+
if (isOperationalMaintenanceCommand(cmd)) return { action: 'kubectl_maintenance', target: cmd.slice(0, 80) };
|
|
341
358
|
if (/^kubectl\s+(get|describe|logs|exec\s+\S+\s+--\s+printenv|top|version|api-resources)\b/.test(cmd)) return { action: 'kubectl_get', target: cmd.slice(0, 80) };
|
|
342
359
|
if (/^kubectl\s+(apply|delete|set|patch|rollout|scale|drain|cordon)\b/.test(cmd)) return { action: 'kubectl_apply', target: cmd.slice(0, 80) };
|
|
343
360
|
if (/curl\s+.*\/api\/harness\/(delegate|codex|army|plan)/.test(cmd)) return { action: 'consult', target: 'harness' };
|
|
@@ -511,6 +528,7 @@ function docRef(canonicalFilename, genericDescription) {
|
|
|
511
528
|
// colon, not a placeholder template). The substance check (added
|
|
512
529
|
// 2026-04-26) defeats ritual emission — `nur: ok` no longer counts.
|
|
513
530
|
const COGNITION_BLOCK_RX = /<cognition>([\s\S]*?)<\/cognition>/i;
|
|
531
|
+
const APPLIED_COGNITION_BLOCK_RX = /<applied_cognition>([\s\S]*?)<\/applied_cognition>/i;
|
|
514
532
|
// Hamza directive 2026-04-28: 8-lens enforcement, not 4-of-8. The earlier
|
|
515
533
|
// REQUIRED_LENSES=4 was a regression — owner caught it ("i no longer see
|
|
516
534
|
// 8 lens cognition per turn"). All 8 canonical lenses must appear with
|
|
@@ -554,7 +572,7 @@ const SHORT_BASH_LIMIT = 30;
|
|
|
554
572
|
// (b) Single-line: `# cognition: <label>=<text>; <label>=<text>; ...`
|
|
555
573
|
//
|
|
556
574
|
// Substance check applies (≥SUBSTANCE_MIN_CHARS, not a `<placeholder>`).
|
|
557
|
-
//
|
|
575
|
+
// All required substantive lenses inline → gate passes, no transcript scan
|
|
558
576
|
// needed. Cognition becomes a property of THE ACTION, not of some
|
|
559
577
|
// message somewhere — the deepest reading of "cognition before action."
|
|
560
578
|
//
|
|
@@ -610,6 +628,32 @@ function detectInlineCognition(cmd) {
|
|
|
610
628
|
return { count: names.length, names, hasSubstrateCite };
|
|
611
629
|
}
|
|
612
630
|
|
|
631
|
+
function validateAppliedCognitionContract(text, cmdText = '') {
|
|
632
|
+
const bodyText = `${String(text || '')}\n${String(cmdText || '')}`;
|
|
633
|
+
const block = bodyText.match(APPLIED_COGNITION_BLOCK_RX);
|
|
634
|
+
const inlineBody = String(cmdText || '')
|
|
635
|
+
.split('\n')
|
|
636
|
+
.filter((line) => /^\s*#\s*(?:decision[_ -]?delta|dominant[_ -]?domain|binds[_ -]?to|expected[_ -]?predicate|artifact[_ -]?change)\s*:/i.test(line))
|
|
637
|
+
.join('\n');
|
|
638
|
+
const contractBody = block ? block[1] : inlineBody;
|
|
639
|
+
if (!contractBody) return { ok: false, violations: ['missing <applied_cognition> contract or inline applied-cognition comments'] };
|
|
640
|
+
const required = [
|
|
641
|
+
['decision_delta', /\bdecision[_ -]?delta\s*:/i],
|
|
642
|
+
['dominant_domain', /\bdominant[_ -]?domain\s*:/i],
|
|
643
|
+
['binds_to', /\bbinds[_ -]?to\s*:/i],
|
|
644
|
+
['expected_predicate', /\bexpected[_ -]?predicate\s*:/i],
|
|
645
|
+
['artifact_change', /\bartifact[_ -]?change\s*:/i],
|
|
646
|
+
];
|
|
647
|
+
const violations = [];
|
|
648
|
+
for (const [name, rx] of required) {
|
|
649
|
+
if (!rx.test(contractBody)) violations.push(`missing ${name}`);
|
|
650
|
+
}
|
|
651
|
+
if (/decision[_ -]?delta\s*:\s*(?:none|n\/a|no change|unchanged|same)/i.test(contractBody)) {
|
|
652
|
+
violations.push('decision_delta says cognition changed nothing');
|
|
653
|
+
}
|
|
654
|
+
return { ok: violations.length === 0, violations, body: contractBody.trim() };
|
|
655
|
+
}
|
|
656
|
+
|
|
613
657
|
function normalizeInlineDirectiveBody(rawBody) {
|
|
614
658
|
if (!rawBody) return '';
|
|
615
659
|
const segments = String(rawBody)
|
|
@@ -671,6 +715,65 @@ function extractCurrentTurnAssistantText(event, toolInput) {
|
|
|
671
715
|
return parts.join('\n\n').trim();
|
|
672
716
|
}
|
|
673
717
|
|
|
718
|
+
function extractTextForUserCorrection(content) {
|
|
719
|
+
if (Array.isArray(content)) {
|
|
720
|
+
return content.filter((block) => block && block.type === 'text').map((block) => block.text || '').filter(Boolean).join('\n');
|
|
721
|
+
}
|
|
722
|
+
return typeof content === 'string' ? content : '';
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function isRuntimeInjectedUserText(text) {
|
|
726
|
+
return /^\s*\[tool_result\b/i.test(text) ||
|
|
727
|
+
/(?:PreToolUse:[A-Za-z]+ hook blocking error|Stop hook feedback:|Aria pre-tool gate:|Aria stop-gate:|<system-reminder>|<task-notification>|task-notification)/i.test(text);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function collectRecentRealUserText(event, transcriptPath, limit = 6) {
|
|
731
|
+
const texts = [];
|
|
732
|
+
const seen = new Set();
|
|
733
|
+
const pushText = (text) => {
|
|
734
|
+
const normalized = String(text || '').trim();
|
|
735
|
+
if (!normalized || seen.has(normalized) || isRuntimeInjectedUserText(normalized)) return;
|
|
736
|
+
seen.add(normalized);
|
|
737
|
+
texts.push(normalized);
|
|
738
|
+
};
|
|
739
|
+
const messages = Array.isArray(event?.messages) ? event.messages : [];
|
|
740
|
+
for (let i = messages.length - 1; i >= 0 && texts.length < limit; i--) {
|
|
741
|
+
const role = messages[i]?.message?.role ?? messages[i]?.role ?? messages[i]?.type;
|
|
742
|
+
if (role !== 'user') continue;
|
|
743
|
+
const content = messages[i]?.message?.content ?? messages[i]?.content ?? [];
|
|
744
|
+
if (Array.isArray(content) && content.length > 0 && content.every((block) => block && block.type === 'tool_result')) continue;
|
|
745
|
+
pushText(extractTextForUserCorrection(content));
|
|
746
|
+
}
|
|
747
|
+
if (transcriptPath && existsSync(transcriptPath) && texts.length < limit) {
|
|
748
|
+
try {
|
|
749
|
+
const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
750
|
+
for (let i = lines.length - 1; i >= 0 && texts.length < limit; i--) {
|
|
751
|
+
let entry;
|
|
752
|
+
try { entry = JSON.parse(lines[i]); } catch { continue; }
|
|
753
|
+
const role = entry?.message?.role ?? entry?.role ?? entry?.type;
|
|
754
|
+
if (role !== 'user') continue;
|
|
755
|
+
const content = entry?.message?.content ?? entry?.content ?? [];
|
|
756
|
+
if (Array.isArray(content) && content.length > 0 && content.every((block) => block && block.type === 'tool_result')) continue;
|
|
757
|
+
pushText(extractTextForUserCorrection(content));
|
|
758
|
+
}
|
|
759
|
+
} catch {}
|
|
760
|
+
}
|
|
761
|
+
return texts.join('\n\n');
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function userCorrectionBlocksCommand(userText, command) {
|
|
765
|
+
const text = String(userText || '');
|
|
766
|
+
const cmd = String(command || '');
|
|
767
|
+
if (!text || !cmd) return null;
|
|
768
|
+
const k8sMutation = /\bkubectl\s+(?:apply|delete|set\s+image|patch|replace|create|scale|rollout\s+(?:restart|undo))\b|scripts\/deploy-|\bdocker\s+push\b/i.test(cmd);
|
|
769
|
+
const explicitStop = /\b(?:stop|do\s+not|don't|quit|cease)\b[\s\S]{0,160}\b(?:deploy|redeploy|restart|rollout|kubectl|command|pods?)\b/i.test(text);
|
|
770
|
+
if (explicitStop && k8sMutation) return 'recent user explicitly told the agent to stop deploy/restart command attempts';
|
|
771
|
+
const macTargetInCommand = /\bmlx-mac\b|\bdeployment\/mlx-mac|\bdeploy(?:ment)?\s+mlx-mac/i.test(cmd);
|
|
772
|
+
const macContradiction = /\b(?:mac\s+lanes?|mac\s+pods?|mlx-mac)\b[\s\S]{0,140}\b(?:not\s+pods?|no\s+such\s+thing|non[-\s]?existent|do(?:es)?\s+not\s+exist|don't\s+exist)\b|\b(?:no\s+such\s+thing|non[-\s]?existent|do(?:es)?\s+not\s+exist|don't\s+exist)\b[\s\S]{0,140}\b(?:mac\s+lanes?|mac\s+pods?|mlx-mac|pods?)\b/i.test(text);
|
|
773
|
+
if (macTargetInCommand && macContradiction) return 'recent user contradicted the mlx-mac Kubernetes-pod/deployment assumption';
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
|
|
674
777
|
// Substance-checking lens detection (added 2026-04-26 per Hamza's
|
|
675
778
|
// gate-improvement doctrine: form-only emission must not satisfy
|
|
676
779
|
// the gate). For each lens, look for `<lens>: <content>` and verify
|
|
@@ -927,6 +1030,7 @@ try {
|
|
|
927
1030
|
|
|
928
1031
|
const toolName = event.tool_name ?? event.toolName ?? '';
|
|
929
1032
|
const toolInput = event.tool_input ?? event.toolInput ?? {};
|
|
1033
|
+
const transcriptPath = event.transcript_path ?? event.transcriptPath;
|
|
930
1034
|
|
|
931
1035
|
// Gate every action tool — every tool that mutates state must go through
|
|
932
1036
|
// cognition challenge. Hamza 2026-04-26: "regardless if u arent even
|
|
@@ -949,6 +1053,26 @@ const cmdPreview = toolName === 'Bash'
|
|
|
949
1053
|
? cmd.slice(0, 80).replace(/\s+/g, ' ')
|
|
950
1054
|
: `${toolName} ${filePath || '(no path)'}`.slice(0, 80);
|
|
951
1055
|
|
|
1056
|
+
if (toolName === 'Bash') {
|
|
1057
|
+
const recentUserText = collectRecentRealUserText(event, transcriptPath);
|
|
1058
|
+
const correctionBlockReason = userCorrectionBlocksCommand(recentUserText, cmd);
|
|
1059
|
+
if (correctionBlockReason) {
|
|
1060
|
+
const reason = `Aria pre-tool gate: USER-CORRECTION hard-block.
|
|
1061
|
+
|
|
1062
|
+
${correctionBlockReason}.
|
|
1063
|
+
|
|
1064
|
+
The next assistant response must stop the command loop, quote the user's correction in one sentence, and re-evaluate the target from substrate before any further mutation. Do not retry this Bash command shape.`;
|
|
1065
|
+
audit('block-user-correction-override', cmdPreview);
|
|
1066
|
+
emitBlock(reason, { source: 'pre-tool/user-correction' });
|
|
1067
|
+
process.exit(2);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (toolName === 'Bash' && isOperationalMaintenanceCommand(cmd)) {
|
|
1072
|
+
audit('allow-operational-maintenance kubectl-rollout-restart', cmdPreview);
|
|
1073
|
+
process.exit(0);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
952
1076
|
// V3: per-command bypass removed entirely. The only escape valves are:
|
|
953
1077
|
// (1) Trivial-bash whitelist — short read-only commands pass without cognition
|
|
954
1078
|
// (2) ~/.claude/settings.json hook removal — visible user action Hamza controls
|
|
@@ -1032,7 +1156,6 @@ const HARD_LOOKBACK_CAP = 50;
|
|
|
1032
1156
|
// window since the substance check defends quality regardless.
|
|
1033
1157
|
const USER_BOUNDARIES_TO_CROSS = 5;
|
|
1034
1158
|
|
|
1035
|
-
const transcriptPath = event.transcript_path ?? event.transcriptPath;
|
|
1036
1159
|
const recentAssistantTexts = [];
|
|
1037
1160
|
const recentAssistantTextSet = new Set();
|
|
1038
1161
|
function appendAssistantTexts(texts) {
|
|
@@ -1129,6 +1252,7 @@ const bestVerifyBody = (() => {
|
|
|
1129
1252
|
})();
|
|
1130
1253
|
const hasVerify = scoreVerifyFields(bestVerifyBody, VERIFY_REQUIRED_FIELDS) === VERIFY_REQUIRED_FIELDS.length;
|
|
1131
1254
|
const hasCognition = lensCount >= REQUIRED_LENSES;
|
|
1255
|
+
const appliedContract = validateAppliedCognitionContract(unionText, cmd);
|
|
1132
1256
|
const cognitionSource = inlineCog.count >= REQUIRED_LENSES
|
|
1133
1257
|
? 'inline-command'
|
|
1134
1258
|
: eventCog.count >= REQUIRED_LENSES
|
|
@@ -1181,6 +1305,7 @@ function pushDecision(decision, reasonText) {
|
|
|
1181
1305
|
hasVerify,
|
|
1182
1306
|
hasCognition,
|
|
1183
1307
|
hasSubstrateCite,
|
|
1308
|
+
appliedCognitionContractOk: appliedContract.ok,
|
|
1184
1309
|
},
|
|
1185
1310
|
});
|
|
1186
1311
|
}
|
|
@@ -1204,6 +1329,62 @@ Next response must do this in order:
|
|
|
1204
1329
|
4. If the blocker is stale gate state or ledger residue, say that explicitly instead of inventing fake proof.`;
|
|
1205
1330
|
}
|
|
1206
1331
|
|
|
1332
|
+
function buildForceRedoActionReason(reasonText, { source = 'pre-tool-gate', tool = toolName || 'unknown', lensCount = 0, requiredLenses = REQUIRED_LENSES } = {}) {
|
|
1333
|
+
if (String(reasonText || '').includes('ARIA FORCE_REDO_ACTION')) return reasonText;
|
|
1334
|
+
return [
|
|
1335
|
+
'=== ARIA FORCE_REDO_ACTION ===',
|
|
1336
|
+
`source: ${source}`,
|
|
1337
|
+
`tool: ${tool}`,
|
|
1338
|
+
'',
|
|
1339
|
+
'This is not a terminal error. It is a forced redo instruction before tool execution.',
|
|
1340
|
+
'Do not retry the same tool call unchanged. Re-author the action with cognition, proof, and expected outcome first.',
|
|
1341
|
+
'',
|
|
1342
|
+
'TEACHING:',
|
|
1343
|
+
'- Pre-tool gates exist to make the model think before acting, not to merely throw after a bad action shape.',
|
|
1344
|
+
'- The redo must name the failed mechanism, cite the current substrate, and state why the next action is safe.',
|
|
1345
|
+
'- Do not bypass by disabling tools, changing endpoints from memory, shortening runtime, or asking Hamza to solve saved-memory ambiguity.',
|
|
1346
|
+
'',
|
|
1347
|
+
'ORIGINAL GATE REASON:',
|
|
1348
|
+
String(reasonText || '').trim(),
|
|
1349
|
+
'',
|
|
1350
|
+
'REQUIRED REDO SHAPE:',
|
|
1351
|
+
'1. Emit/attach <cognition> with the required lenses before retrying the tool.',
|
|
1352
|
+
'2. For Bash, prepend inline cognition comments to the command:',
|
|
1353
|
+
' # nur: <observed state and substrate, >=20 chars>',
|
|
1354
|
+
' # mizan: <risk/tradeoff and why this action is balanced, >=20 chars>',
|
|
1355
|
+
' # hikma: <doctrine applied and what it forbids, >=20 chars>',
|
|
1356
|
+
' # tafakkur: <second-order effects and expected result, >=20 chars>',
|
|
1357
|
+
`3. Current cognition count: ${lensCount}/${requiredLenses}.`,
|
|
1358
|
+
'4. If deploy/shared-infra: include <verify> with target, verified evidence, rollback, and axiom.',
|
|
1359
|
+
'5. If non-trivial action: include <expected> with numeric/boolean/state-string predicate.',
|
|
1360
|
+
'=== END FORCE_REDO_ACTION ===',
|
|
1361
|
+
].join('\n');
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
function emitBlock(reasonText, meta = {}) {
|
|
1365
|
+
console.log(JSON.stringify({ decision: 'block', reason: buildForceRedoActionReason(reasonText, meta) }));
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
if (hasCognition && !appliedContract.ok) {
|
|
1369
|
+
const reason = `Aria pre-tool gate: cognition exists but is not applied to the action.
|
|
1370
|
+
|
|
1371
|
+
The harness no longer accepts cognition as decorative preface. Before a mutating tool call, cognition must produce an execution contract that changes or constrains the action.
|
|
1372
|
+
|
|
1373
|
+
Violations:
|
|
1374
|
+
${appliedContract.violations.map((v) => `- ${v}`).join('\n')}
|
|
1375
|
+
|
|
1376
|
+
Required contract, either in the assistant turn as <applied_cognition>...</applied_cognition> or inline Bash comments:
|
|
1377
|
+
decision_delta: <what changed because cognition ran; not "none">
|
|
1378
|
+
dominant_domain: <engineering_quality | trust | operations | security | product | ...>
|
|
1379
|
+
binds_to: <this exact tool call/action>
|
|
1380
|
+
expected_predicate: <numeric, boolean, or state-string predicate proving success>
|
|
1381
|
+
artifact_change: <how the artifact/action is different because cognition ran>`;
|
|
1382
|
+
audit('block-applied-cognition-contract', cmdPreview);
|
|
1383
|
+
pushDecision('block', `applied cognition contract missing: ${appliedContract.violations.join(', ')}`);
|
|
1384
|
+
emitBlock(reason, { source: 'pre-tool/applied-cognition-contract', tool: toolName, lensCount, requiredLenses: REQUIRED_LENSES });
|
|
1385
|
+
process.exit(2);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1207
1388
|
// ── Deploy-specific gate (doctrine #104) ────────────────────────────────
|
|
1208
1389
|
// Per feedback_deploy_requires_verify_cognition.md (Hamza 2026-04-28 after
|
|
1209
1390
|
// consciousness.ts crash took aria-soul into CrashLoopBackOff): deploys
|
|
@@ -1355,7 +1536,7 @@ Re-emit verify+cognition with the missing pieces, then retry the deploy command.
|
|
|
1355
1536
|
cmdPreview,
|
|
1356
1537
|
);
|
|
1357
1538
|
pushDecision('block', `deploy ${deployMatched.name}: ${reasons.join('; ')}`);
|
|
1358
|
-
|
|
1539
|
+
emitBlock(refusal, { source: 'pre-tool/deploy', tool: toolName });
|
|
1359
1540
|
process.exit(2);
|
|
1360
1541
|
}
|
|
1361
1542
|
|
|
@@ -1399,7 +1580,7 @@ At least 4 substantive lenses required.`),
|
|
|
1399
1580
|
`destructive:${matched.name}:${hasVerify ? 'need-cognition' : 'need-verify'}:${toolName}:${filePath || cmdPreview}`);
|
|
1400
1581
|
audit(`block ${matched.name} verify=${hasVerify} cognition=${lensCount}`, cmdPreview);
|
|
1401
1582
|
pushDecision('block', `destructive ${matched.name} missing ${!hasVerify ? 'verify' : 'cognition'}`);
|
|
1402
|
-
|
|
1583
|
+
emitBlock(reason, { source: 'pre-tool/destructive' });
|
|
1403
1584
|
process.exit(2);
|
|
1404
1585
|
}
|
|
1405
1586
|
|
|
@@ -1448,7 +1629,7 @@ No per-tool bypass available (v3 doctrine — the harness's whole purpose is no
|
|
|
1448
1629
|
|
|
1449
1630
|
audit(`block ${toolName.toLowerCase()} cognition=${lensCount}`, cmdPreview);
|
|
1450
1631
|
pushDecision('block', `${toolName.toLowerCase()} missing cognition (${lensCount}/${REQUIRED_LENSES})`);
|
|
1451
|
-
|
|
1632
|
+
emitBlock(reason, { source: 'pre-tool/missing-cognition' });
|
|
1452
1633
|
process.exit(2);
|
|
1453
1634
|
}
|
|
1454
1635
|
|
|
@@ -1479,7 +1660,7 @@ No env-var disable path — gates are unconditional from the gated process per H
|
|
|
1479
1660
|
|
|
1480
1661
|
audit(`block-discovery-unresolved ${toolName.toLowerCase()}`, cmdPreview);
|
|
1481
1662
|
pushDecision('block', `${toolName.toLowerCase()} cognition has unresolved discovery`);
|
|
1482
|
-
|
|
1663
|
+
emitBlock(reason, { source: 'pre-tool/discovery-binding' });
|
|
1483
1664
|
process.exit(2);
|
|
1484
1665
|
}
|
|
1485
1666
|
|
|
@@ -1543,7 +1724,7 @@ No bypass — doctrine:dalio_expected_required is unconditional for non-trivial
|
|
|
1543
1724
|
|
|
1544
1725
|
audit(`block-expected-missing ${toolName.toLowerCase()}`, cmdPreview);
|
|
1545
1726
|
pushDecision('block', `${toolName.toLowerCase()} missing <expected> measurable predicate`);
|
|
1546
|
-
|
|
1727
|
+
emitBlock(reason, { source: 'pre-tool/discovery-binding' });
|
|
1547
1728
|
process.exit(2);
|
|
1548
1729
|
}
|
|
1549
1730
|
}
|
|
@@ -1626,7 +1807,7 @@ Read it first (it is in your environment as ARIA_HARNESS_PACKET_PATH), then refe
|
|
|
1626
1807
|
|
|
1627
1808
|
Cognition-theater rejected per feedback_gates_enforce_form_not_substance.md.`;
|
|
1628
1809
|
audit(`block-subagent-no-packet-cite ${toolName.toLowerCase()}`, cmdPreview);
|
|
1629
|
-
|
|
1810
|
+
emitBlock(reason, { source: 'pre-tool/architecture-facts' });
|
|
1630
1811
|
process.exit(2);
|
|
1631
1812
|
}
|
|
1632
1813
|
})();
|
|
@@ -1669,7 +1850,7 @@ Rule references:
|
|
|
1669
1850
|
R10 no_kubectl_apply_for_image_drift — kubectl apply on aria-soul-stateful (project_forge_psi_oom_cascade.md)
|
|
1670
1851
|
R11 no_console_log_secrets — console.log with TOKEN/PASSWORD/SECRET/API_KEY/JWT/BEARER`;
|
|
1671
1852
|
audit(`block-arch-facts ${toolName.toLowerCase()} path=${filePath}`, `violations=${archResult.violations?.length ?? 0}`);
|
|
1672
|
-
|
|
1853
|
+
emitBlock(archReason, { source: 'pre-tool/architecture-facts' });
|
|
1673
1854
|
process.exit(2);
|
|
1674
1855
|
}
|
|
1675
1856
|
}
|
|
@@ -1771,7 +1952,7 @@ What Claude must do:
|
|
|
1771
1952
|
3. Wait for next user prompt — preprompt-consult will retry; if it succeeds a plan will exist on next tool call
|
|
1772
1953
|
|
|
1773
1954
|
Non-trivial actions are blocked until a plan exists. Trivial reads (ls/cat/grep) bypass automatically per existing whitelist. To temporarily disable binding for emergency: ARIA_BINDING_ENABLED=false (logged).`;
|
|
1774
|
-
|
|
1955
|
+
emitBlock(reason, { source: 'pre-tool/substrate-binding' });
|
|
1775
1956
|
process.exit(2);
|
|
1776
1957
|
}
|
|
1777
1958
|
}
|
|
@@ -1790,7 +1971,7 @@ What Claude must do:
|
|
|
1790
1971
|
3. Don't continue executing actions beyond the issued plan boundary
|
|
1791
1972
|
|
|
1792
1973
|
This prevents Claude from drifting past Aria's authorized scope.`;
|
|
1793
|
-
|
|
1974
|
+
emitBlock(reason, { source: 'pre-tool/substrate-binding' });
|
|
1794
1975
|
process.exit(2);
|
|
1795
1976
|
}
|
|
1796
1977
|
|
|
@@ -1827,7 +2008,7 @@ This prevents Claude from drifting past Aria's authorized scope.`;
|
|
|
1827
2008
|
if (!freshPhaseInfo || freshPhaseInfo.abortedHere) {
|
|
1828
2009
|
bindingAuditAppend({ event: 'block_aborted_phase_post_fallback_still_bad', sessionId, planId: plan.planId });
|
|
1829
2010
|
const reason = `Aria binding gate: aborted phase recovery minted plan ${plan.planId} but new plan also has no usable phase. Manual intervention required.`;
|
|
1830
|
-
|
|
2011
|
+
emitBlock(reason, { source: 'pre-tool/dalio-expected' });
|
|
1831
2012
|
process.exit(2);
|
|
1832
2013
|
}
|
|
1833
2014
|
phaseInfo = freshPhaseInfo;
|
|
@@ -1836,7 +2017,7 @@ This prevents Claude from drifting past Aria's authorized scope.`;
|
|
|
1836
2017
|
const reason = `Aria binding gate: phase ${phaseInfo.phase.id} of plan ${plan.planId} was reported aborted AND inline architect-fallback failed (exit=${architectExit}). Plan progression halted. ${architectStderr ? 'Architect stderr: ' + architectStderr : ''}
|
|
1837
2018
|
|
|
1838
2019
|
What Claude must do: emit [PLAN_BLOCKER reason="<concrete observation>" suggestedAmendment="<if any>"] for Hamza/Aria to issue a corrected plan. Do not continue executing the aborted plan.`;
|
|
1839
|
-
|
|
2020
|
+
emitBlock(reason, { source: 'pre-tool/dalio-expected' });
|
|
1840
2021
|
process.exit(2);
|
|
1841
2022
|
}
|
|
1842
2023
|
}
|
|
@@ -1864,7 +2045,7 @@ Forbidden actions for this phase: ${(phase.forbiddenActions || []).join(', ') ||
|
|
|
1864
2045
|
Allowed actions for this phase: ${(phase.allowedActions || []).join(', ') || '(none)'}
|
|
1865
2046
|
|
|
1866
2047
|
Claude must either: (a) reframe the action to fit allowedActions, OR (b) emit [PLAN_BLOCKER reason="..."] requesting Aria amend the plan.${recipeAddendum}`;
|
|
1867
|
-
|
|
2048
|
+
emitBlock(reason, { source: 'pre-tool/output-claim' });
|
|
1868
2049
|
process.exit(2);
|
|
1869
2050
|
}
|
|
1870
2051
|
|
|
@@ -1883,7 +2064,7 @@ Phase summary: ${phase.summary}
|
|
|
1883
2064
|
Allowed actions: ${(phase.allowedActions || []).join(', ') || '(none — phase is observation-only)'}
|
|
1884
2065
|
|
|
1885
2066
|
Claude must either: (a) reframe action to fit allowedActions, OR (b) emit [PLAN_BLOCKER reason="..."] for plan amendment.${recipeAddendum}`;
|
|
1886
|
-
|
|
2067
|
+
emitBlock(reason, { source: 'pre-tool/output-claim' });
|
|
1887
2068
|
process.exit(2);
|
|
1888
2069
|
}
|
|
1889
2070
|
|
|
@@ -2025,7 +2206,7 @@ try {
|
|
|
2025
2206
|
The substrate's contract gate refused this action. Local doctrine gates passed (cognition lenses, binding plan, drift) but the substrate sees a downstream contract that disallows this tool call. Address the substrate's reason above before retrying.`;
|
|
2026
2207
|
audit(`block-substrate-checkAction ${toolName.toLowerCase()} action=${__action} reason="${(__check.reason || '').slice(0, 80)}"`, cmdPreview);
|
|
2027
2208
|
pushDecision('block', `substrate checkAction denied: ${(__check.reason || 'unspecified').slice(0, 100)}`);
|
|
2028
|
-
|
|
2209
|
+
emitBlock(__reason, { source: 'pre-tool/final' });
|
|
2029
2210
|
process.exit(2);
|
|
2030
2211
|
}
|
|
2031
2212
|
}
|
|
@@ -2218,7 +2399,7 @@ causes merge conflicts and state divergence. Explicit coordination is the only s
|
|
|
2218
2399
|
pushDecision('block', `hive session-lock conflict on ${_lockCheckPath.slice(0, 80)}`);
|
|
2219
2400
|
console.log(JSON.stringify({
|
|
2220
2401
|
decision: 'block',
|
|
2221
|
-
reason: _lockBlockReason,
|
|
2402
|
+
reason: buildForceRedoActionReason(_lockBlockReason, { source: 'pre-tool/hive-lock-conflict', tool: toolName || 'unknown' }),
|
|
2222
2403
|
hookSpecificOutput: {
|
|
2223
2404
|
hookEventName: 'PreToolUse',
|
|
2224
2405
|
conflicting_locks: _conflictingLocks,
|
|
@@ -109,7 +109,27 @@ const HARNESS_URL =
|
|
|
109
109
|
process.env.ARIA_HARNESS_BASE_URL ||
|
|
110
110
|
process.env.ARIA_HARNESS_URL ||
|
|
111
111
|
'https://harness.ariasos.com';
|
|
112
|
-
|
|
112
|
+
function resolveHarnessToken() {
|
|
113
|
+
if (process.env.ARIA_HARNESS_TOKEN) return process.env.ARIA_HARNESS_TOKEN;
|
|
114
|
+
if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
|
|
115
|
+
if (process.env.ARIA_MASTER_TOKEN) return process.env.ARIA_MASTER_TOKEN;
|
|
116
|
+
try {
|
|
117
|
+
if (existsSync(OWNER_TOKEN_PATH)) {
|
|
118
|
+
const token = readFileSync(OWNER_TOKEN_PATH, 'utf8').trim();
|
|
119
|
+
if (token) return token;
|
|
120
|
+
}
|
|
121
|
+
} catch {}
|
|
122
|
+
try {
|
|
123
|
+
const licensePath = `${HOME}/.aria/license.json`;
|
|
124
|
+
if (existsSync(licensePath)) {
|
|
125
|
+
const license = JSON.parse(readFileSync(licensePath, 'utf8'));
|
|
126
|
+
if (license.harnessToken) return String(license.harnessToken).trim();
|
|
127
|
+
if (license.token) return String(license.token).trim();
|
|
128
|
+
}
|
|
129
|
+
} catch {}
|
|
130
|
+
return '';
|
|
131
|
+
}
|
|
132
|
+
const HARNESS_TOKEN = resolveHarnessToken();
|
|
113
133
|
const MIN_PROMPT_CHARS = 40; // skip auto-consult on trivial prompts
|
|
114
134
|
const MAX_DIRECTION_CHARS = 4000; // cap injected chunk size
|
|
115
135
|
|
|
@@ -248,6 +268,8 @@ Respond with EXACTLY this JSON shape:
|
|
|
248
268
|
"successCriterion": "<observable signal of completion>",
|
|
249
269
|
"abortCriterion": "<observable signal of failure>",
|
|
250
270
|
"doctrineRefs": ["<memory_filename.md>", ...],
|
|
271
|
+
"cognitionUse": "<how Aria cognition should change the executor's tool/input/output shape for this phase>",
|
|
272
|
+
"evidenceRequired": ["<observable proof the executor must collect before reporting complete>"],
|
|
251
273
|
"allowedActions": ["read", "edit:<path-pattern>", "kubectl_get", "consult", "bash_safe", "..."],
|
|
252
274
|
"forbiddenActions": ["kubectl_apply", "edit:<path-pattern>", "..."]
|
|
253
275
|
}
|
|
@@ -259,7 +281,9 @@ Respond with EXACTLY this JSON shape:
|
|
|
259
281
|
}
|
|
260
282
|
}
|
|
261
283
|
|
|
262
|
-
Apply your 8 lenses + substrate. Phases are ordered + small (one logical step each). Doctrine refs cite memory files in /home/hamzaibrahim1/.claude/projects/-home-hamzaibrahim1/memory/. Be specific in allowedActions / forbiddenActions — Claude can ONLY do what's allowed for the current phase
|
|
284
|
+
Apply your 8 lenses + substrate. Phases are ordered + small (one logical step each). Doctrine refs cite memory files in /home/hamzaibrahim1/.claude/projects/-home-hamzaibrahim1/memory/. Be specific in allowedActions / forbiddenActions — Claude can ONLY do what's allowed for the current phase.
|
|
285
|
+
|
|
286
|
+
Every phase must include cognitionUse and evidenceRequired. cognitionUse tells Claude how Aria's cognition should improve the actual tool input, edit shape, review scope, or final output. evidenceRequired tells Claude what observation must be collected before reporting the phase complete.` : `Pre-prompt direction request from Claude orchestrator.
|
|
263
287
|
|
|
264
288
|
The user just submitted this prompt:
|
|
265
289
|
|
|
@@ -280,6 +304,8 @@ thinking from training reflex. Be concrete:
|
|
|
280
304
|
(NOT a reflexive "want me to" deferral).
|
|
281
305
|
4. Mizan check: any risk patterns in this prompt — over-scope creep,
|
|
282
306
|
over-replacement temptation, tier-substitution temptation, etc.
|
|
307
|
+
5. How should Aria cognition improve Claude's actual input/output shape —
|
|
308
|
+
what should be more specific, safer, more evidence-bound, or differently scoped?
|
|
283
309
|
|
|
284
310
|
Keep direction under 1500 chars. This is the pre-load context for Claude's
|
|
285
311
|
turn — not the final response. Claude will still emit cognition + action;
|
|
@@ -56,6 +56,30 @@ function auditLog(decision, summary, sessionId) {
|
|
|
56
56
|
appendFileSync(GATE_LOG, `${new Date().toISOString()} [${sessionId}] ${decision} ${summary}\n`);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function buildForceRedoActionReason({ source, reason, missingSignals = [], incidents = [] }) {
|
|
60
|
+
return [
|
|
61
|
+
'=== ARIA FORCE_REDO_ACTION ===',
|
|
62
|
+
`source: ${source}`,
|
|
63
|
+
`reason: ${reason}`,
|
|
64
|
+
'',
|
|
65
|
+
'This is not a terminal error. It is a forced redo instruction before action execution.',
|
|
66
|
+
'Do not proceed with tools from unloaded context. Load/repair substrate first, then retry the action.',
|
|
67
|
+
'',
|
|
68
|
+
'TEACHING:',
|
|
69
|
+
'- Every action turn must begin from Aria substrate, not improvised memory.',
|
|
70
|
+
'- Blocking incidents are Dalio failure deltas; they must be fixed and marked resolved before new action.',
|
|
71
|
+
'- Missing harness/direction/memory signals mean the context-loader path must run before retry.',
|
|
72
|
+
missingSignals.length ? `\nMISSING SIGNALS:\n${missingSignals.map((signal) => `- ${signal}`).join('\n')}` : '',
|
|
73
|
+
incidents.length ? `\nINCIDENTS TO RESOLVE:\n${incidents.map((incident) => `- ${incident}`).join('\n')}` : '',
|
|
74
|
+
'',
|
|
75
|
+
'REQUIRED REDO SHAPE:',
|
|
76
|
+
'1. Run the structured recovery action in hookSpecificOutput.recovery.',
|
|
77
|
+
'2. Verify harness_packet, aria_direction/binding_plan, and memory references are loaded.',
|
|
78
|
+
'3. Retry the original action only after the missing context or incidents are resolved.',
|
|
79
|
+
'=== END FORCE_REDO_ACTION ===',
|
|
80
|
+
].filter(Boolean).join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
59
83
|
// ── Turn-state deduplication ──────────────────────────────────────────
|
|
60
84
|
const TURN_DEDUP_WINDOW_MS = 60_000; // 60s
|
|
61
85
|
|
|
@@ -461,7 +485,11 @@ Per Dalio Loop Layer 2 doctrine: failure deltas are not optional to address. The
|
|
|
461
485
|
|
|
462
486
|
console.log(JSON.stringify({
|
|
463
487
|
decision: 'block',
|
|
464
|
-
reason:
|
|
488
|
+
reason: buildForceRedoActionReason({
|
|
489
|
+
source: 'preturn-memory/blocking-incidents',
|
|
490
|
+
reason: blockReason,
|
|
491
|
+
incidents: blockingIncidents.map((inc) => `${inc.incident_id || '(no incident_id)'} ${inc.title || '(no title)'}`),
|
|
492
|
+
}),
|
|
465
493
|
hookSpecificOutput: {
|
|
466
494
|
hookEventName: 'PreToolUse',
|
|
467
495
|
blocking_incidents: blockingIncidents.map((inc) => ({
|
|
@@ -605,7 +633,7 @@ auditLog(
|
|
|
605
633
|
|
|
606
634
|
console.log(JSON.stringify({
|
|
607
635
|
decision: 'block',
|
|
608
|
-
reason,
|
|
636
|
+
reason: buildForceRedoActionReason({ source: 'preturn-memory/context-not-loaded', reason, missingSignals }),
|
|
609
637
|
hookSpecificOutput: {
|
|
610
638
|
hookEventName: 'PreToolUse',
|
|
611
639
|
recovery: {
|
|
@@ -87,6 +87,29 @@ function audit(event, data = {}) {
|
|
|
87
87
|
} catch {}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
function buildForceRedoActionReason({ source, reason, violations = [] }) {
|
|
91
|
+
return [
|
|
92
|
+
'=== ARIA FORCE_REDO_ACTION ===',
|
|
93
|
+
`source: ${source}`,
|
|
94
|
+
`reason: ${reason}`,
|
|
95
|
+
'',
|
|
96
|
+
'This is not a terminal error. It is a forced redo instruction for the attempted repository edit/action.',
|
|
97
|
+
'Do not retry the same edit. Redo the action so doctrine-bound source remains real, reviewable, and production-safe.',
|
|
98
|
+
'',
|
|
99
|
+
'TEACHING:',
|
|
100
|
+
'- Production paths cannot gain stub, mock, fake, placeholder, pending, or direct-provider bypass semantics.',
|
|
101
|
+
'- If an exception is intentional, isolate it in tests/specs/fixtures/examples/demos/mocks or add the explicit allow marker in the file.',
|
|
102
|
+
'- External provider calls must use an approved runtime/SDK path or carry the explicit external-call allow marker.',
|
|
103
|
+
violations.length ? `\nVIOLATIONS TO FIX:\n${violations.map((v) => `- ${v}`).join('\n')}` : '',
|
|
104
|
+
'',
|
|
105
|
+
'REQUIRED REDO SHAPE:',
|
|
106
|
+
'1. Preserve the intended behavior without stub/mock/pending semantics.',
|
|
107
|
+
'2. Use real implementation wiring or an explicit reviewed allow marker.',
|
|
108
|
+
'3. Re-run the action after removing the violating lines from the diff.',
|
|
109
|
+
'=== END FORCE_REDO_ACTION ===',
|
|
110
|
+
].filter(Boolean).join('\n');
|
|
111
|
+
}
|
|
112
|
+
|
|
90
113
|
function parseArgs(argv) {
|
|
91
114
|
const parsed = {
|
|
92
115
|
mode: 'working-tree',
|
|
@@ -384,7 +407,14 @@ async function main() {
|
|
|
384
407
|
audit('repo_doctrine_gate_block', { repoRoot, mode, violations });
|
|
385
408
|
|
|
386
409
|
if (event) {
|
|
387
|
-
console.log(JSON.stringify({
|
|
410
|
+
console.log(JSON.stringify({
|
|
411
|
+
decision: 'block',
|
|
412
|
+
reason: buildForceRedoActionReason({
|
|
413
|
+
source: 'repo-doctrine',
|
|
414
|
+
reason,
|
|
415
|
+
violations: violations.slice(0, 20).map((violation) => `${violation.path}:${violation.line} [${violation.rule}] ${violation.excerpt}`),
|
|
416
|
+
}),
|
|
417
|
+
}));
|
|
388
418
|
process.exit(2);
|
|
389
419
|
}
|
|
390
420
|
|