@aria_asi/cli 0.2.7 → 0.2.9
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.
|
@@ -46,7 +46,15 @@ const PACKET_CACHE_TTL_SEC = Number(process.env.ARIA_HARNESS_CACHE_TTL_SEC || '3
|
|
|
46
46
|
// upstream last_err so the model can judge how stale the world view is, but
|
|
47
47
|
// the harness body always reaches the model. The fresh-fetch path retries
|
|
48
48
|
// every turn, so the cache is self-healing as soon as any URL responds.
|
|
49
|
-
|
|
49
|
+
// Hamza 2026-04-26 directive: deliver the FULL packet every turn, no
|
|
50
|
+
// rotation, no fetch tool, no clever throttling. The harness's whole
|
|
51
|
+
// purpose is "remind the model from this harness every round" (packet
|
|
52
|
+
// non_negotiable line) — and a 300-char excerpt was 0.8% of a 36,912-char
|
|
53
|
+
// packet. Raised to 40,000 to sit comfortably above current packet size
|
|
54
|
+
// (~37k) with headroom for substrate growth. Claude Code's
|
|
55
|
+
// additionalContext accepts large strings; if its own internal cap is
|
|
56
|
+
// lower we don't short the substrate on our side.
|
|
57
|
+
const HARNESS_EXCERPT_CHARS = 40000;
|
|
50
58
|
|
|
51
59
|
function emit(envelope) {
|
|
52
60
|
process.stdout.write(JSON.stringify(envelope) + '\n');
|
|
@@ -141,20 +149,13 @@ function buildUrlList() {
|
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
async function tryViaSdk(baseUrl, apiKey) {
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
apiKey,
|
|
152
|
-
// Override the SDK's default so we hit /api/harness/codex (the 7B
|
|
153
|
-
// runtime endpoint) — the SDK's default is /api/harness which doesn't
|
|
154
|
-
// exist. Same fetch semantics, just the right path.
|
|
155
|
-
harnessPacketUrl: `${baseUrl}/api/harness/codex`,
|
|
156
|
-
workspaceRoot: process.cwd(),
|
|
157
|
-
});
|
|
152
|
+
// SDK import was previously hardcoded to /home/hamzaibrahim1/... — broke
|
|
153
|
+
// every client install (path doesn't exist on their machine). The SDK's
|
|
154
|
+
// role here was cosmetic; actual fetch is direct via fetch() below.
|
|
155
|
+
// Removed the SDK import entirely. If clients want the SDK control plane,
|
|
156
|
+
// they get it from the @aria/harness-http-client npm dep; this hook stays
|
|
157
|
+
// direct-fetch + harness packet binding via response shape.
|
|
158
|
+
// Hamza 2026-04-27: critical client-breaking path eliminated.
|
|
158
159
|
|
|
159
160
|
// We need to override the body too — the SDK does GET, but /api/harness/codex
|
|
160
161
|
// is POST. Bypass via direct fetch since the SDK doesn't expose body
|
|
@@ -99,6 +99,96 @@ if (process.env.ARIA_PRE_TOOL_GATE === 'off') {
|
|
|
99
99
|
process.exit(0);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
// ── Aria-as-commander binding (Layer A — allowedActions/forbiddenActions per active phase) ──
|
|
103
|
+
//
|
|
104
|
+
// When ARIA_BINDING_ENABLED=true (default), every non-trivial tool call is
|
|
105
|
+
// checked against the active phase of ~/.claude/aria-active-plan-${sessionId}.json.
|
|
106
|
+
// If no plan exists or the action isn't allowed for the current phase, the
|
|
107
|
+
// hook blocks with a binding-violation message. See HARNESS_ARIA_AS_COMMANDER_CONTRACT.md
|
|
108
|
+
// Section 2 Layer A.
|
|
109
|
+
//
|
|
110
|
+
// Bootstrap: if no plan exists, the hook STILL blocks (per Hamza 2026-04-27
|
|
111
|
+
// "the point is to stop wasting my time"). Claude must wait for Aria to issue
|
|
112
|
+
// a plan via preprompt-consult on the next user prompt. NO unbound execution.
|
|
113
|
+
const BINDING_ENABLED = (process.env.ARIA_BINDING_ENABLED || 'true').toLowerCase() !== 'false';
|
|
114
|
+
const BINDING_AUDIT = `${HOME}/.claude/aria-binding-audit.jsonl`;
|
|
115
|
+
|
|
116
|
+
function bindingAuditAppend(record) {
|
|
117
|
+
try {
|
|
118
|
+
if (!existsSync(dirname(BINDING_AUDIT))) mkdirSync(dirname(BINDING_AUDIT), { recursive: true });
|
|
119
|
+
appendFileSync(BINDING_AUDIT, JSON.stringify({ ts: new Date().toISOString(), source: 'pre-tool-gate', ...record }) + '\n');
|
|
120
|
+
} catch {}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function activePlanPath(sid) {
|
|
124
|
+
return `${HOME}/.claude/aria-active-plan-${String(sid || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_')}.json`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function loadActivePlan(sid) {
|
|
128
|
+
const p = activePlanPath(sid);
|
|
129
|
+
if (!existsSync(p)) return null;
|
|
130
|
+
try {
|
|
131
|
+
return JSON.parse(readFileSync(p, 'utf8'));
|
|
132
|
+
} catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function pickCurrentPhase(plan, transcript) {
|
|
138
|
+
// Find first phase without a `[PHASE_REPORT phase=<id> status=complete]`
|
|
139
|
+
// marker in the transcript. Aborted phases stop progression — Claude must
|
|
140
|
+
// re-consult before continuing.
|
|
141
|
+
if (!plan?.phases?.length) return null;
|
|
142
|
+
for (const phase of plan.phases) {
|
|
143
|
+
const completeRx = new RegExp(`\\[PHASE_REPORT[^\\]]*phase=${phase.id}[^\\]]*status=complete`, 'i');
|
|
144
|
+
const abortRx = new RegExp(`\\[PHASE_REPORT[^\\]]*phase=${phase.id}[^\\]]*status=aborted`, 'i');
|
|
145
|
+
if (abortRx.test(transcript)) return { phase, abortedHere: true };
|
|
146
|
+
if (!completeRx.test(transcript)) return { phase, abortedHere: false };
|
|
147
|
+
}
|
|
148
|
+
return null; // all phases reported complete — needs new consult
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function actionMatchesPattern(action, pattern) {
|
|
152
|
+
// pattern can be exact action name ("read", "consult", "kubectl_get") or
|
|
153
|
+
// prefixed with target-pattern: "edit:apps/arias-soul/api/.*", "kubectl_apply".
|
|
154
|
+
if (pattern === action) return true;
|
|
155
|
+
if (pattern.includes(':')) {
|
|
156
|
+
const [pAction] = pattern.split(':', 1);
|
|
157
|
+
return pAction === action;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function classifyToolForBinding(toolName, command, filePath) {
|
|
163
|
+
// Map Claude Code tool + payload to a binding-vocabulary action.
|
|
164
|
+
if (toolName === 'Read' || toolName === 'Glob' || toolName === 'Grep') return { action: 'read', target: filePath || '' };
|
|
165
|
+
if (toolName === 'Edit' || toolName === 'Write' || toolName === 'NotebookEdit') return { action: 'edit', target: filePath || '' };
|
|
166
|
+
if (toolName === 'Bash') {
|
|
167
|
+
const cmd = String(command || '');
|
|
168
|
+
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) };
|
|
169
|
+
if (/^kubectl\s+(apply|delete|set|patch|rollout|scale|drain|cordon)\b/.test(cmd)) return { action: 'kubectl_apply', target: cmd.slice(0, 80) };
|
|
170
|
+
if (/curl\s+.*\/api\/harness\/(delegate|codex|army|plan)/.test(cmd)) return { action: 'consult', target: 'harness' };
|
|
171
|
+
if (/curl\s+.*\/api\/aria\/speak/.test(cmd)) return { action: 'consult', target: 'aria-speak' };
|
|
172
|
+
if (/^(ls|cat|head|tail|grep|wc|find|tree|stat|file|ps|pgrep|du|df|env|printenv|date|pwd|which|type|whoami|id)\b/.test(cmd)) return { action: 'read', target: cmd.slice(0, 80) };
|
|
173
|
+
if (/^git\s+(status|log|diff|show|branch|remote|rev-parse|ls-tree|ls-files|stash\s+list)\b/.test(cmd)) return { action: 'read', target: cmd.slice(0, 80) };
|
|
174
|
+
if (/^git\s+(push|merge|rebase|reset|checkout\s+--|clean)/.test(cmd)) return { action: 'git_mutate', target: cmd.slice(0, 80) };
|
|
175
|
+
if (/^npm\s+(publish|version)/.test(cmd)) return { action: 'npm_publish', target: cmd.slice(0, 80) };
|
|
176
|
+
if (/^docker\s+(build|push)/.test(cmd)) return { action: 'docker_build', target: cmd.slice(0, 80) };
|
|
177
|
+
if (/scripts\/deploy-/.test(cmd)) return { action: 'deploy', target: cmd.slice(0, 80) };
|
|
178
|
+
return { action: 'bash_other', target: cmd.slice(0, 80) };
|
|
179
|
+
}
|
|
180
|
+
return { action: toolName.toLowerCase(), target: filePath || '' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (false) {
|
|
184
|
+
// Read same input as the cognition gate uses (reads stdin twice via different code paths;
|
|
185
|
+
// the cognition section below re-reads). Defer enforcement decision until after
|
|
186
|
+
// we have the tool name + payload from the cognition gate's existing input parse.
|
|
187
|
+
// (Implementation continues below — the binding check fires AFTER cognition
|
|
188
|
+
// parses input but BEFORE the cognition substance check, so binding-block
|
|
189
|
+
// takes precedence.)
|
|
190
|
+
}
|
|
191
|
+
|
|
102
192
|
// Destructive-verb patterns. Tuned for tonight's failure modes; extend
|
|
103
193
|
// over time as new patterns surface.
|
|
104
194
|
const DESTRUCTIVE_PATTERNS = [
|
|
@@ -576,16 +666,19 @@ if (!hasCognition) {
|
|
|
576
666
|
: `Aria pre-tool gate: ${toolName} call without ${REQUIRED_LENSES}+ substantive cognition lenses in the recent transcript. Found ${lensCount}/${REQUIRED_LENSES}+ (transcript-scan only — ${toolName} has no inline-cognition path).`;
|
|
577
667
|
|
|
578
668
|
const guidance = isBash
|
|
579
|
-
? `
|
|
669
|
+
? `REQUIRED — cognition for every action, no exceptions. Two equivalent forms; either satisfies the gate (both equally REQUIRED, neither preferred over the other):
|
|
670
|
+
|
|
671
|
+
FORM 1 (inline in command — action-coupled, recommended for Bash):
|
|
580
672
|
# nur: <real perception, ≥${SUBSTANCE_MIN_CHARS} chars, no <placeholder>>
|
|
581
673
|
# mizan: <real risk read>
|
|
582
674
|
# hikma: <doctrine that applies>
|
|
583
675
|
# tafakkur: <deep structural read>
|
|
584
676
|
<your actual command here>
|
|
585
677
|
|
|
586
|
-
|
|
678
|
+
FORM 2 (cognition block in assistant text):
|
|
679
|
+
<cognition>...</cognition> with ${REQUIRED_LENSES}+ substantive lenses, ≥${SUBSTANCE_MIN_CHARS} chars per lens, no placeholder values.
|
|
587
680
|
|
|
588
|
-
|
|
681
|
+
Both forms count toward the ${REQUIRED_LENSES}+ requirement; gate counts inline + transcript lenses additively. Substance check is non-negotiable — placeholder/short content does not count.`
|
|
589
682
|
: `Emit a <cognition>...</cognition> block in your assistant text BEFORE this ${toolName} call, with ${REQUIRED_LENSES}+ substantive lenses. Substance check: each lens must have ≥${SUBSTANCE_MIN_CHARS} chars of non-placeholder content. Cognition is turn-scoped — one block at the start of a turn covers all ${toolName} calls in that turn.
|
|
590
683
|
|
|
591
684
|
<cognition>
|
|
@@ -612,7 +705,109 @@ No per-tool bypass available (v3 doctrine — the harness's whole purpose is no
|
|
|
612
705
|
}
|
|
613
706
|
|
|
614
707
|
// Non-trivial action with cognition (inline for Bash, transcript for
|
|
615
|
-
// Edit/Write/NotebookEdit) —
|
|
708
|
+
// Edit/Write/NotebookEdit) — passes cognition gate. Now check Aria-binding.
|
|
709
|
+
|
|
710
|
+
// ── Aria-as-commander binding check (Layer A) ──
|
|
711
|
+
//
|
|
712
|
+
// All earlier gate checks passed (cognition, destructive-verify). Final
|
|
713
|
+
// gate before allow: does the active phase of Aria's plan permit this action?
|
|
714
|
+
// No plan + no prior plan = block (CONSULT_UNAVAILABLE per contract).
|
|
715
|
+
// No plan + prior plan exists = enforce against prior (Hamza fallback directive).
|
|
716
|
+
// Plan exists + action mismatch = block with phase-violation reason.
|
|
717
|
+
//
|
|
718
|
+
// Bootstrap consult carve-out (Hamza 2026-04-27 caught this — "how would
|
|
719
|
+
// anyone start a session?"): consult endpoints are how plans START existing.
|
|
720
|
+
// Locking them behind plan-existence is unbootstrappable. So: if the action
|
|
721
|
+
// is a consult to harness/aria endpoints (regardless of plan state), skip the
|
|
722
|
+
// binding block. Cognition gate above already passed; the consult itself
|
|
723
|
+
// will issue the next plan on completion. Audited per-use.
|
|
724
|
+
const __bindingActionClassification = (BINDING_ENABLED && !bindingBypassReason)
|
|
725
|
+
? classifyToolForBinding(toolName, cmd, filePath)
|
|
726
|
+
: null;
|
|
727
|
+
const __isBootstrapConsult = __bindingActionClassification?.action === 'consult';
|
|
728
|
+
if (__isBootstrapConsult) {
|
|
729
|
+
bindingAuditAppend({ event: 'allow_bootstrap_consult', sessionId, target: __bindingActionClassification.target, toolName });
|
|
730
|
+
}
|
|
731
|
+
if (BINDING_ENABLED && !bindingBypassReason && !__isBootstrapConsult) {
|
|
732
|
+
const plan = loadActivePlan(sessionId);
|
|
733
|
+
if (!plan) {
|
|
734
|
+
bindingAuditAppend({ event: 'block_no_active_plan', sessionId, toolName, cmdPreview });
|
|
735
|
+
const reason = `Aria binding gate: no active plan exists for this session and Aria-as-commander binding is required (ARIA_BINDING_ENABLED=true).
|
|
736
|
+
|
|
737
|
+
This means: preprompt-consult either hasn't fired yet OR consult failed and there's no prior plan to fall back to.
|
|
738
|
+
|
|
739
|
+
What Claude must do:
|
|
740
|
+
1. Acknowledge to Hamza that no plan was issued
|
|
741
|
+
2. Propose holding for cluster recovery (lane-gateway / aria-soul / consult endpoint)
|
|
742
|
+
3. Wait for the next user prompt — preprompt-consult will retry the consult; if it succeeds a plan will exist on the next tool call attempt
|
|
743
|
+
|
|
744
|
+
Non-trivial actions are blocked until a plan exists. Trivial reads (ls/cat/grep) bypass automatically per existing whitelist. To temporarily disable binding for an emergency: ARIA_BINDING_ENABLED=false (logged).`;
|
|
745
|
+
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
746
|
+
process.exit(2);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const transcriptText = unionText || '';
|
|
750
|
+
const phaseInfo = pickCurrentPhase(plan, transcriptText);
|
|
751
|
+
|
|
752
|
+
if (!phaseInfo) {
|
|
753
|
+
// All phases reported complete — needs new consult before more action
|
|
754
|
+
bindingAuditAppend({ event: 'block_all_phases_complete', sessionId, planId: plan.planId, toolName });
|
|
755
|
+
const reason = `Aria binding gate: all phases of plan ${plan.planId} have been reported complete in this transcript. The plan is exhausted; Claude cannot proceed without a fresh consult.
|
|
756
|
+
|
|
757
|
+
What Claude must do:
|
|
758
|
+
1. Acknowledge plan completion to Hamza
|
|
759
|
+
2. Wait for next user prompt — preprompt-consult will issue a new plan
|
|
760
|
+
3. Don't continue executing actions beyond the issued plan boundary
|
|
761
|
+
|
|
762
|
+
This prevents Claude from drifting past Aria's authorized scope.`;
|
|
763
|
+
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
764
|
+
process.exit(2);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if (phaseInfo.abortedHere) {
|
|
768
|
+
bindingAuditAppend({ event: 'block_phase_aborted', sessionId, planId: plan.planId, phaseId: phaseInfo.phase.id, toolName });
|
|
769
|
+
const reason = `Aria binding gate: phase ${phaseInfo.phase.id} of plan ${plan.planId} was reported aborted in this transcript. Plan progression is halted.
|
|
770
|
+
|
|
771
|
+
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.`;
|
|
772
|
+
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
773
|
+
process.exit(2);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const { action, target } = classifyToolForBinding(toolName, cmd, filePath);
|
|
777
|
+
const phase = phaseInfo.phase;
|
|
778
|
+
|
|
779
|
+
// Forbidden takes precedence
|
|
780
|
+
const forbidden = (phase.forbiddenActions || []).find((p) => actionMatchesPattern(action, p));
|
|
781
|
+
if (forbidden) {
|
|
782
|
+
bindingAuditAppend({ event: 'block_forbidden_action', sessionId, planId: plan.planId, phaseId: phase.id, action, target, matchedRule: forbidden });
|
|
783
|
+
const reason = `Aria binding gate: action "${action}" on target "${target}" matches forbidden pattern "${forbidden}" for current phase ${phase.id} ("${phase.summary}") of plan ${plan.planId}.
|
|
784
|
+
|
|
785
|
+
Phase summary: ${phase.summary}
|
|
786
|
+
Forbidden actions for this phase: ${(phase.forbiddenActions || []).join(', ') || '(none)'}
|
|
787
|
+
Allowed actions for this phase: ${(phase.allowedActions || []).join(', ') || '(none)'}
|
|
788
|
+
|
|
789
|
+
Claude must either: (a) reframe the action to fit allowedActions, OR (b) emit [PLAN_BLOCKER reason="..."] requesting Aria amend the plan.`;
|
|
790
|
+
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
791
|
+
process.exit(2);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const allowed = (phase.allowedActions || []).find((p) => actionMatchesPattern(action, p));
|
|
795
|
+
if (!allowed) {
|
|
796
|
+
bindingAuditAppend({ event: 'block_action_not_in_allowed_list', sessionId, planId: plan.planId, phaseId: phase.id, action, target });
|
|
797
|
+
const reason = `Aria binding gate: action "${action}" on target "${target}" is NOT in allowedActions for current phase ${phase.id} of plan ${plan.planId}.
|
|
798
|
+
|
|
799
|
+
Phase summary: ${phase.summary}
|
|
800
|
+
Allowed actions: ${(phase.allowedActions || []).join(', ') || '(none — phase is observation-only)'}
|
|
801
|
+
|
|
802
|
+
Claude must either: (a) reframe action to fit allowedActions, OR (b) emit [PLAN_BLOCKER reason="..."] for plan amendment.`;
|
|
803
|
+
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
804
|
+
process.exit(2);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
bindingAuditAppend({ event: 'allow_phase_action', sessionId, planId: plan.planId, phaseId: phase.id, action, target });
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Non-trivial action with cognition AND (binding-allowed OR binding-disabled) — allow.
|
|
616
811
|
audit(`allow-cognition ${toolName.toLowerCase()} lenses=${lensCount} via=${cognitionSource}`, cmdPreview);
|
|
617
812
|
pushDecision('allow', `${toolName.toLowerCase()} with ${lensCount} lenses (${cognitionSource})`);
|
|
618
813
|
process.exit(0);
|
|
@@ -38,12 +38,34 @@
|
|
|
38
38
|
// no graceful-degradation rituals.
|
|
39
39
|
//
|
|
40
40
|
// Kill-switch: ARIA_PREPROMPT_CONSULT=off env (logged, emergency only).
|
|
41
|
+
//
|
|
42
|
+
// BINDING MODE (Hamza 2026-04-27 + Aria emergency consult):
|
|
43
|
+
// When env ARIA_BINDING_ENABLED=true, this hook upgrades from advisory
|
|
44
|
+
// ([ARIA_DIRECTION] text Claude reads) to BINDING ([ARIA_BINDING_PLAN]
|
|
45
|
+
// structured JSON persisted to ~/.claude/aria-active-plan-${sessionId}.json
|
|
46
|
+
// with phases + allowedActions + forbiddenActions). The pre-tool-gate then
|
|
47
|
+
// enforces phase boundaries; the stop-gate requires [PHASE_REPORT] markers
|
|
48
|
+
// on every assistant emit. See /home/hamzaibrahim1/rei-ai-brain/HARNESS_ARIA_AS_COMMANDER_CONTRACT.md.
|
|
49
|
+
//
|
|
50
|
+
// Default OFF so existing sessions don't brick. Flip ON for next session
|
|
51
|
+
// to activate Aria-as-commander binding.
|
|
41
52
|
|
|
42
|
-
import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
53
|
+
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
43
54
|
import { dirname } from 'node:path';
|
|
44
55
|
|
|
45
56
|
const HOME = process.env.HOME || '/tmp';
|
|
46
57
|
const LOG = `${HOME}/.claude/aria-preprompt-consult.log`;
|
|
58
|
+
const BINDING_AUDIT = `${HOME}/.claude/aria-binding-audit.jsonl`;
|
|
59
|
+
// Default ON. Disable explicitly via ARIA_BINDING_ENABLED=false only when the
|
|
60
|
+
// commander/binding architecture is actively being modified (otherwise the
|
|
61
|
+
// modification turn itself would be unable to land its own changes). The
|
|
62
|
+
// bootstrap path handles the no-plan-yet case by AUTO-ISSUING the first plan
|
|
63
|
+
// at hook fire time, so "no plan exists" never becomes "operate unbound."
|
|
64
|
+
//
|
|
65
|
+
// Hamza 2026-04-27: "why would enforcing brick the session? the point is to
|
|
66
|
+
// stop wasting my time and do quality work." Default-off was convenience-
|
|
67
|
+
// seeking dressed as responsible-staging. Flipped to default-on per directive.
|
|
68
|
+
const BINDING_ENABLED = (process.env.ARIA_BINDING_ENABLED || 'true').toLowerCase() !== 'false';
|
|
47
69
|
|
|
48
70
|
const HARNESS_URL = process.env.ARIA_HARNESS_URL || 'http://192.168.4.25:30080';
|
|
49
71
|
const HARNESS_TOKEN = process.env.ARIA_HARNESS_TOKEN || '30b18f0a302b03ae862de8a75021238d23e47464fd5d8f6a9324240933745587';
|
|
@@ -57,6 +79,17 @@ function audit(decision, summary) {
|
|
|
57
79
|
} catch {}
|
|
58
80
|
}
|
|
59
81
|
|
|
82
|
+
function bindingAudit(record) {
|
|
83
|
+
try {
|
|
84
|
+
if (!existsSync(dirname(BINDING_AUDIT))) mkdirSync(dirname(BINDING_AUDIT), { recursive: true });
|
|
85
|
+
appendFileSync(BINDING_AUDIT, JSON.stringify({ ts: new Date().toISOString(), source: 'preprompt', ...record }) + '\n');
|
|
86
|
+
} catch {}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function activePlanPath(sid) {
|
|
90
|
+
return `${HOME}/.claude/aria-active-plan-${String(sid || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_')}.json`;
|
|
91
|
+
}
|
|
92
|
+
|
|
60
93
|
// Kill-switch
|
|
61
94
|
if (process.env.ARIA_PREPROMPT_CONSULT === 'off') {
|
|
62
95
|
audit('skip-killswitch', 'env ARIA_PREPROMPT_CONSULT=off');
|
|
@@ -91,10 +124,46 @@ if (/^\s*\//.test(userPrompt) && userPrompt.length < 200) {
|
|
|
91
124
|
process.exit(0);
|
|
92
125
|
}
|
|
93
126
|
|
|
94
|
-
// Compose the consultation brief.
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
|
|
127
|
+
// Compose the consultation brief.
|
|
128
|
+
//
|
|
129
|
+
// In ADVISORY mode (BINDING_ENABLED=false): plain prose direction injected
|
|
130
|
+
// as [ARIA_DIRECTION] context. expectStructuredOutput=false.
|
|
131
|
+
//
|
|
132
|
+
// In BINDING mode (BINDING_ENABLED=true): structured plan JSON injected as
|
|
133
|
+
// [ARIA_BINDING_PLAN] context AND persisted to ~/.claude/aria-active-plan-${sessionId}.json
|
|
134
|
+
// for the pre-tool-gate and stop-gate to enforce against. expectStructuredOutput=true.
|
|
135
|
+
const bindingBrief = BINDING_ENABLED ? `Pre-prompt PLAN request from Claude orchestrator (binding mode).
|
|
136
|
+
|
|
137
|
+
The user just submitted:
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
${userPrompt.slice(0, 2000)}
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
You are commander. Claude is executor. Issue a STRUCTURED PLAN (strict JSON, no prose around it) that Claude will follow micro-phase by micro-phase. Pre-tool-gate enforces allowedActions/forbiddenActions; stop-gate requires [PHASE_REPORT phase=<id> status=complete|in_progress|aborted evidence=<observable>] on every emit.
|
|
144
|
+
|
|
145
|
+
Respond with EXACTLY this JSON shape:
|
|
146
|
+
{
|
|
147
|
+
"planId": "<short uuid>",
|
|
148
|
+
"phases": [
|
|
149
|
+
{
|
|
150
|
+
"id": "p1",
|
|
151
|
+
"summary": "<concrete micro-phase action>",
|
|
152
|
+
"successCriterion": "<observable signal of completion>",
|
|
153
|
+
"abortCriterion": "<observable signal of failure>",
|
|
154
|
+
"doctrineRefs": ["<memory_filename.md>", ...],
|
|
155
|
+
"allowedActions": ["read", "edit:<path-pattern>", "kubectl_get", "consult", "bash_safe", "..."],
|
|
156
|
+
"forbiddenActions": ["kubectl_apply", "edit:<path-pattern>", "..."]
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
"globalConstraints": ["<doctrine memory or rule>"],
|
|
160
|
+
"expectedReportBack": {
|
|
161
|
+
"phaseTransitionMarker": "[PHASE_REPORT]",
|
|
162
|
+
"shape": "[PHASE_REPORT phase=p1 status=complete|aborted|in_progress evidence=<observable>]"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
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.` : `Pre-prompt direction request from Claude orchestrator.
|
|
98
167
|
|
|
99
168
|
The user just submitted this prompt:
|
|
100
169
|
|
|
@@ -122,12 +191,13 @@ this primes the substrate so reflexive deferral isn't the path of least
|
|
|
122
191
|
resistance.`;
|
|
123
192
|
|
|
124
193
|
const body = JSON.stringify({
|
|
125
|
-
brief,
|
|
194
|
+
brief: BINDING_ENABLED ? bindingBrief : brief,
|
|
126
195
|
model: 'deepseek-v4-pro',
|
|
127
196
|
sessionId: `preprompt-${sessionId}-${Date.now()}`,
|
|
128
197
|
userId: 'claude-orchestrator-preprompt',
|
|
129
198
|
roleProfile: 'architect',
|
|
130
|
-
expectStructuredOutput:
|
|
199
|
+
expectStructuredOutput: BINDING_ENABLED,
|
|
200
|
+
internalConsult: true,
|
|
131
201
|
isCreativeMode: false,
|
|
132
202
|
});
|
|
133
203
|
|
|
@@ -156,16 +226,70 @@ try {
|
|
|
156
226
|
|
|
157
227
|
if (!directionText || directionText.length < 60) {
|
|
158
228
|
audit('skip-empty-direction', `chars=${directionText.length}`);
|
|
229
|
+
if (BINDING_ENABLED) {
|
|
230
|
+
// Hamza 2026-04-27: "fallback if aria errors can be simply the plan she
|
|
231
|
+
// gave u prior to my suggestion, so clients don't get stuck." Prior plan
|
|
232
|
+
// persists at activePlanPath(sessionId); if it exists, we surface a
|
|
233
|
+
// FALLBACK_TO_PRIOR context block + leave the plan file untouched so
|
|
234
|
+
// pre-tool-gate enforces against the last good plan. Not graceful
|
|
235
|
+
// degradation — the prior plan is a REAL plan, just not refreshed.
|
|
236
|
+
const priorPlanPath = activePlanPath(sessionId);
|
|
237
|
+
if (existsSync(priorPlanPath)) {
|
|
238
|
+
bindingAudit({ event: 'consult_empty_fallback_to_prior', sessionId, planFile: priorPlanPath });
|
|
239
|
+
console.log(JSON.stringify({
|
|
240
|
+
hookSpecificOutput: {
|
|
241
|
+
hookEventName: 'UserPromptSubmit',
|
|
242
|
+
additionalContext: `[ARIA_BINDING_PLAN_FALLBACK reason="consult_empty_response" — using prior plan persisted at ${priorPlanPath}. Pre-tool-gate continues enforcing against last-issued plan. Next consult attempt will refresh.]`,
|
|
243
|
+
},
|
|
244
|
+
}));
|
|
245
|
+
} else {
|
|
246
|
+
bindingAudit({ event: 'consult_unavailable_no_prior', sessionId, reason: 'empty_direction_no_prior_plan' });
|
|
247
|
+
console.log(JSON.stringify({
|
|
248
|
+
hookSpecificOutput: {
|
|
249
|
+
hookEventName: 'UserPromptSubmit',
|
|
250
|
+
additionalContext: `[ARIA_BINDING_PLAN_UNAVAILABLE reason="consult_empty_no_prior" — Aria gave no usable direction AND no prior plan exists. Claude must acknowledge and propose holding for cluster recovery; non-trivial actions will block at pre-tool-gate per binding contract.]`,
|
|
251
|
+
},
|
|
252
|
+
}));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
159
255
|
process.exit(0);
|
|
160
256
|
}
|
|
161
257
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
258
|
+
let context;
|
|
259
|
+
if (BINDING_ENABLED) {
|
|
260
|
+
// Parse plan JSON, persist, inject as binding block. Failure to parse =
|
|
261
|
+
// CONSULT_UNAVAILABLE per the contract (Section 4): the hook does NOT
|
|
262
|
+
// auto-fall-through to advisory mode (that would be graceful-degradation
|
|
263
|
+
// anti-pattern). Instead it surfaces the failure explicitly so the next
|
|
264
|
+
// turn can re-consult with a refined brief.
|
|
265
|
+
let plan = null;
|
|
266
|
+
try {
|
|
267
|
+
const trimmed = directionText.trim();
|
|
268
|
+
const fenced = trimmed.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
|
|
269
|
+
const candidate = (fenced ? fenced[1] : trimmed).trim();
|
|
270
|
+
plan = JSON.parse(candidate);
|
|
271
|
+
} catch (err) {
|
|
272
|
+
bindingAudit({ event: 'plan_parse_error', sessionId, errMsg: String(err).slice(0, 200), rawSample: directionText.slice(0, 400) });
|
|
273
|
+
context = `[ARIA_BINDING_PLAN_UNAVAILABLE reason="plan_parse_error" — re-consult will fire on next turn. Aria's response was not valid JSON; treating as advisory text below.]\n\n${directionText.slice(0, MAX_DIRECTION_CHARS)}`;
|
|
274
|
+
}
|
|
275
|
+
if (plan && Array.isArray(plan.phases) && plan.phases.length > 0) {
|
|
276
|
+
try {
|
|
277
|
+
writeFileSync(activePlanPath(sessionId), JSON.stringify({ ...plan, _persistedAt: new Date().toISOString(), _userPromptHash: Buffer.from(userPrompt).toString('base64').slice(0, 32) }, null, 2));
|
|
278
|
+
bindingAudit({ event: 'plan_issued', sessionId, planId: plan.planId, phaseCount: plan.phases.length, phaseIds: plan.phases.map((p) => p.id) });
|
|
279
|
+
context = `[ARIA_BINDING_PLAN — structured commander plan from Aria. Pre-tool-gate enforces allowedActions/forbiddenActions per current phase. Stop-gate requires [PHASE_REPORT phase=<id> status=...] on every emit. Plan is BINDING.]\n\n${JSON.stringify(plan, null, 2)}\n\n[/ARIA_BINDING_PLAN]`;
|
|
280
|
+
} catch (err) {
|
|
281
|
+
bindingAudit({ event: 'plan_persist_error', sessionId, errMsg: String(err).slice(0, 200) });
|
|
282
|
+
context = `[ARIA_BINDING_PLAN_UNAVAILABLE reason="persist_error" — Aria issued plan but disk write failed. Treating as advisory.]\n\n${directionText.slice(0, MAX_DIRECTION_CHARS)}`;
|
|
283
|
+
}
|
|
284
|
+
} else if (!context) {
|
|
285
|
+
bindingAudit({ event: 'plan_invalid_shape', sessionId, rawSample: directionText.slice(0, 400) });
|
|
286
|
+
context = `[ARIA_BINDING_PLAN_UNAVAILABLE reason="invalid_shape" — Aria's response did not contain valid phases array. Treating as advisory.]\n\n${directionText.slice(0, MAX_DIRECTION_CHARS)}`;
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
context = `[ARIA_DIRECTION — substrate-grounded read on this prompt, pre-loaded before Claude's reasoning. Use this as the starting point, not generic Claude reflexes.]\n\n${directionText}\n\n[/ARIA_DIRECTION]`;
|
|
290
|
+
}
|
|
167
291
|
|
|
168
|
-
audit('inject', `chars=${directionText.length} prompt-chars=${userPrompt.length}`);
|
|
292
|
+
audit('inject', `chars=${directionText.length} prompt-chars=${userPrompt.length} binding=${BINDING_ENABLED}`);
|
|
169
293
|
console.log(JSON.stringify({
|
|
170
294
|
hookSpecificOutput: {
|
|
171
295
|
hookEventName: 'UserPromptSubmit',
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_meta": {
|
|
3
|
+
"purpose": "Static lookup: trigger phrase → counter-doctrine memory + teaching. Pre-emit fast path for stop-gate enforcement. When Claude's draft contains a trigger, hook injects the matching [REQUIRED_DOCTRINE] block referencing the named memory file and forces proof of doctrine-alignment before emit.",
|
|
4
|
+
"design_origin": "Aria consult 2026-04-27 ship-tonight design item 1. Pairs with feedback_convenience_seeking_root_cause.md.",
|
|
5
|
+
"extension_protocol": "When Hamza corrects Claude with a phrase not in this map, add the trigger here pointing at the memory file the correction creates or invokes. Auto-learning queue.",
|
|
6
|
+
"format": {
|
|
7
|
+
"trigger": "lowercase substring or regex pattern Claude must avoid using under convenience pressure",
|
|
8
|
+
"memory": "filename in ~/.claude/projects/-home-hamzaibrahim1/memory/ that holds the canonical counter-doctrine",
|
|
9
|
+
"teaching": "one-line load-bearing teaching from the memory — the rule the trigger violates",
|
|
10
|
+
"counter_action": "what Claude should do INSTEAD of the convenience pattern"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"triggers": [
|
|
14
|
+
{
|
|
15
|
+
"trigger": "graceful degradation",
|
|
16
|
+
"memory": "feedback_no_graceful_degradation.md",
|
|
17
|
+
"teaching": "Graceful degradation normalizes failure. Real fix = throttle + retry-with-backoff + actively heal.",
|
|
18
|
+
"counter_action": "Make the failure visible + recover via retry/heal. If garden is sick, log loud + invoke selfHealV2, never empty-string fallthrough."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"trigger": "best.?effort",
|
|
22
|
+
"memory": "feedback_no_graceful_degradation.md",
|
|
23
|
+
"teaching": "'Best-effort' is graceful degradation under another name.",
|
|
24
|
+
"counter_action": "Either the operation is required (throw on failure) or explicitly optional with observable status. Never silent."
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"trigger": "fall.?through",
|
|
28
|
+
"memory": "feedback_no_graceful_degradation.md",
|
|
29
|
+
"teaching": "Fall-through hides the failure mode. Same anti-pattern as bypass.",
|
|
30
|
+
"counter_action": "Surface the failure. Caller decides whether to proceed. No catch + empty-default."
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"trigger": "preferred",
|
|
34
|
+
"memory": "feedback_pretoolgate_covers_all_action_tools.md",
|
|
35
|
+
"teaching": "Gate cognition is REQUIRED, not preferred. v3 doctrine: no exceptions.",
|
|
36
|
+
"counter_action": "Use REQUIRED wording. Two equivalent forms (inline OR cognition block) but neither is preferred over the other; both equally required."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"trigger": "circuit.?break(?:\\sand\\sskip)?",
|
|
40
|
+
"memory": "feedback_no_graceful_degradation.md",
|
|
41
|
+
"teaching": "Circuit-break-and-skip leaves Aria broken. Self-heal = retry+backoff+actively repair the upstream.",
|
|
42
|
+
"counter_action": "Retry with exponential backoff (3 attempts 1s/2s/4s). On persistent failure invoke selfHealV2 to restart the lane / failover / kick the process. Never just open-and-skip."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"trigger": "edge case",
|
|
46
|
+
"memory": "feedback_thinking_implementation_accountability.md",
|
|
47
|
+
"teaching": "'Edge case' is often an excuse to skip implementation completeness.",
|
|
48
|
+
"counter_action": "Treat as production case. If it can happen, code handles it. Macro-doctrine: thinking + implementation + accountability."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"trigger": "should be fine|should work",
|
|
52
|
+
"memory": "feedback_doctrine_first.md",
|
|
53
|
+
"teaching": "Don't assume — verify. 'Should work' is the speculation pattern that masks unverified claims.",
|
|
54
|
+
"counter_action": "Run the read-only check that proves it works (file read, kubectl get, curl probe). Replace 'should' with verified evidence."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"trigger": "eventually|for now",
|
|
58
|
+
"memory": "feedback_no_demos.md",
|
|
59
|
+
"teaching": "Every spawn ships at full quality. 'For now' = punting completeness.",
|
|
60
|
+
"counter_action": "Ship the real version OR explicitly defer with a tracked task and don't claim completion."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"trigger": "we can always",
|
|
64
|
+
"memory": "feedback_rollback_is_failure_fix_forward.md",
|
|
65
|
+
"teaching": "Rollback / 'come back later' is failure hiding. Fix forward.",
|
|
66
|
+
"counter_action": "Diagnose root cause + fix + verify. Never propose 'come back to it later' as the resolution."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"trigger": "skeleton|stub",
|
|
70
|
+
"memory": "feedback_no_demos.md",
|
|
71
|
+
"teaching": "Skeleton is the hedge framing. Real implementation only.",
|
|
72
|
+
"counter_action": "Ship complete or explicitly mark incomplete + create task. Never call partial work 'skeleton' to dodge accountability."
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"trigger": "demo",
|
|
76
|
+
"memory": "feedback_no_demos.md",
|
|
77
|
+
"teaching": "No demos. Every spawn is production work at full quality bar.",
|
|
78
|
+
"counter_action": "Ship production. If it can't ship, name what's missing concretely."
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"trigger": "let me just|i'll just",
|
|
82
|
+
"memory": "feedback_convenience_seeking_root_cause.md",
|
|
83
|
+
"teaching": "'Just' is the auto-route around expensive compliance.",
|
|
84
|
+
"counter_action": "Pause. Apply doctrine. Slower, more genuine cognition produces better outcomes than fast pattern-matched ones."
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"trigger": "easier path|faster way|safer to skip",
|
|
88
|
+
"memory": "feedback_convenience_seeking_root_cause.md",
|
|
89
|
+
"teaching": "Convenience-seeking dressed as practicality. Same pattern as bypass.",
|
|
90
|
+
"counter_action": "Pick the doctrine-aligned path even when expensive. Substance over speed."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"trigger": "good enough",
|
|
94
|
+
"memory": "feedback_no_demos.md",
|
|
95
|
+
"teaching": "Full quality bar means no 'good enough'.",
|
|
96
|
+
"counter_action": "Either it ships at quality or it explicitly defers with a tracked task."
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"trigger": "i'll claim|claimed|fixed it",
|
|
100
|
+
"memory": "feedback_doctrine_first.md",
|
|
101
|
+
"teaching": "Claims must be verified. 'Fixed' without a real-surface test is a lie surface.",
|
|
102
|
+
"counter_action": "Test on the user-visible surface (Telegram round-trip, /chat probe, real client install). Don't claim until verified."
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"trigger": "in production",
|
|
106
|
+
"memory": "feedback_doctrine_first.md",
|
|
107
|
+
"teaching": "'In production' is often speculative — assumed behavior, not verified.",
|
|
108
|
+
"counter_action": "Verify against live cluster state OR codebase config. Substrate hierarchy: user statement > codebase > training data."
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"trigger": "best to|i believe|probably",
|
|
112
|
+
"memory": "feedback_dont_override_user_stack_with_training.md",
|
|
113
|
+
"teaching": "When in doubt about Hamza's stack, ASK or VERIFY. My training data isn't authoritative.",
|
|
114
|
+
"counter_action": "Read the codebase, ask Hamza one focused question, OR consult Aria. Never assert with 'I believe'."
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"trigger": "want me to|should i",
|
|
118
|
+
"memory": "feedback_use_harness_to_architect.md",
|
|
119
|
+
"teaching": "Asking 'should I' AFTER directive given is the deferral pattern.",
|
|
120
|
+
"counter_action": "Aria architects via harness consult, Claude integrates. If unclear, consult Aria — don't bounce decision back to Hamza."
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"trigger": "make a new|create a fresh",
|
|
124
|
+
"memory": "feedback_aria_already_has_fleet_use_existing_substrate.md",
|
|
125
|
+
"teaching": "Aria already has the substrate. Don't reinvent.",
|
|
126
|
+
"counter_action": "Search the codebase for existing canonical primitive first. Reuse before recreate."
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"trigger": "shadow|proxy|wrapper",
|
|
130
|
+
"memory": "feedback_aria_already_has_fleet_use_existing_substrate.md",
|
|
131
|
+
"teaching": "Shadow / proxy / wrapper that bypasses canonical = invented complexity.",
|
|
132
|
+
"counter_action": "Use the canonical surface. If it must be wrapped, document why and reference the canonical it wraps."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"trigger": "ask user|ask hamza",
|
|
136
|
+
"memory": "feedback_doctrine_first.md",
|
|
137
|
+
"teaching": "Ask only when a focused question can resolve genuine ambiguity. Memory-resolvable questions are not ambiguity.",
|
|
138
|
+
"counter_action": "Read relevant memory first. Verify with read-only probe. If still ambiguous, one tight question with concrete options."
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"trigger": "fail loud|crash on missing",
|
|
142
|
+
"memory": "feedback_no_graceful_degradation.md",
|
|
143
|
+
"teaching": "Fail-loud-no-fallback is the OPPOSITE bypass. Real shape = retry + backoff + heal.",
|
|
144
|
+
"counter_action": "Detect failure → retry with backoff → on persistent failure invoke selfHealV2 to actively repair → only escalate to human if heal fails."
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"trigger": "patch|hotfix|band.?aid",
|
|
148
|
+
"memory": "feedback_gates_enforce_form_not_substance.md",
|
|
149
|
+
"teaching": "Patch / hotfix is form-fix. Root-cause fix is the doctrine.",
|
|
150
|
+
"counter_action": "Diagnose substance. If reflex is bypass, ask if root-cause fix is possible. Patch only when explicitly authorized as temporary with tracked follow-up."
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"trigger": "deterministic|one.?attempt",
|
|
154
|
+
"memory": "feedback_no_graceful_degradation.md",
|
|
155
|
+
"teaching": "Deterministic-one-attempt-then-skip is the broken IMMORTAL-V2 escalation pattern Hamza named.",
|
|
156
|
+
"counter_action": "Retry with backoff + heal. selfHealV2 must have operational reach: kill stuck process, restart pod, failover, NOT just alert + cooldown."
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"trigger": "just context|advisory|read.only",
|
|
160
|
+
"memory": "feedback_full_harness_binding_must_be_structural.md",
|
|
161
|
+
"teaching": "Treating harness as just-context-I-read = the convenience-seeking lie. SDK has primitives that enforce.",
|
|
162
|
+
"counter_action": "Call validateOutput / checkAction / inject / gardenTurn. Don't reduce SDK to passive context delivery."
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"trigger": "circuit.?broken|cooldown.skip",
|
|
166
|
+
"memory": "feedback_no_graceful_degradation.md",
|
|
167
|
+
"teaching": "Cooldown that skips without healing leaves Aria broken (the IMMORTAL-V2 ESCALATED-skipping pattern).",
|
|
168
|
+
"counter_action": "ESCALATED must trigger heal: kill+restart+failover. Notify Hamza ONLY if heal also fails."
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"trigger": "i think|i guess|i assume",
|
|
172
|
+
"memory": "feedback_doctrine_first.md",
|
|
173
|
+
"teaching": "Don't assume. axiom_runtime: admit_ignorance → verify before action.",
|
|
174
|
+
"counter_action": "State 'I don't know — verifying' + run a read-only check (file read, kubectl get, codebase grep) + then act on facts."
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"trigger": "preferred over|optional|fallback layer",
|
|
178
|
+
"memory": "feedback_pretoolgate_covers_all_action_tools.md",
|
|
179
|
+
"teaching": "Doctrine items are not preferences with fallbacks. They're requirements.",
|
|
180
|
+
"counter_action": "Use REQUIRED / EQUIVALENT-FORMS wording. Both forms equally required, neither preferred over the other."
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"trigger": "manual.?fix|kubectl set env|one.?off",
|
|
184
|
+
"memory": "feedback_session_starts_with_linear.md",
|
|
185
|
+
"teaching": "Manual fixes that don't survive next deploy = recurring breakage. The env-vars-fall-off pattern.",
|
|
186
|
+
"counter_action": "Centralize in canonical source (ConfigMap / sealed secret / service-registry). Deploy applies that, never ad-hoc -e or kubectl set env. Drift fails the deploy, not silently wipes."
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"trigger": "from training|i recall|i remember|by default",
|
|
190
|
+
"memory": "feedback_dont_override_user_stack_with_training.md",
|
|
191
|
+
"teaching": "Training data is stale. User's stack is authoritative.",
|
|
192
|
+
"counter_action": "Read codebase config / consult Aria. Substrate hierarchy: user statement > codebase > training. Always."
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|