instar 1.3.341 → 1.3.342

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.
@@ -0,0 +1,90 @@
1
+ /**
2
+ * GuardPostureTripwire — a disabled guard is itself an incident.
3
+ *
4
+ * Triggering incident (2026-06-05): the meltdown load-shed at 2:54 PM PDT
5
+ * batch-flipped a set of monitoring guards off in `.instar/config.json` —
6
+ * scheduler.enabled (issue #882), contextWedgeSentinel, failureLearning,
7
+ * resourceLedger, burnDetection. Only the scheduler was noticed and
8
+ * re-enabled (5.5h later); the wedge sentinel stayed dark and watched the
9
+ * EXO 3.0 AUP-rejection wedge kill a session for an hour THAT SAME EVENING
10
+ * without a single audit row. No instar code writes those flags — the flip
11
+ * was emergency hand-editing — so nothing structural recorded it. Two
12
+ * silently-disabled guards discovered in one day is a class, not a
13
+ * coincidence.
14
+ *
15
+ * The tripwire: at every server boot, compare the resolved guard posture
16
+ * (every monitoring.* enabled flag + scheduler.enabled) against the persisted
17
+ * posture from the previous boot. Any guard that went enabled→disabled gets:
18
+ * 1. a loud boot log line,
19
+ * 2. one JSONL breadcrumb row in `logs/guard-posture.jsonl` (same home as
20
+ * sentinel-events.jsonl — the documented "why did X stop?" surface),
21
+ * 3. ONE aggregated Attention item listing every newly-disabled guard
22
+ * (aggregate per the Bounded Notification Surface rule — never one
23
+ * item per guard).
24
+ * Re-enabled guards get the log line + breadcrumb only (good news is not a
25
+ * to-do). First boot (no snapshot) records the posture and raises nothing.
26
+ *
27
+ * Signal-vs-authority: pure detector. It never re-enables anything, never
28
+ * blocks a boot, never edits config — a deliberate disable stays disabled;
29
+ * the Attention item is the consent surface where the operator either
30
+ * acknowledges the flip or goes and re-enables the guard. Errors are
31
+ * swallowed into the log: a broken tripwire must never break a boot.
32
+ */
33
+ export type GuardPosture = Record<string, boolean>;
34
+ export interface GuardPostureDiff {
35
+ /** Guards that were enabled last boot and are disabled now. */
36
+ disabled: string[];
37
+ /** Guards that were disabled last boot and are enabled now. */
38
+ enabled: string[];
39
+ }
40
+ export interface AttentionItemInput {
41
+ id: string;
42
+ title: string;
43
+ summary: string;
44
+ description?: string;
45
+ category: string;
46
+ priority: 'URGENT' | 'HIGH' | 'NORMAL' | 'LOW';
47
+ sourceContext?: string;
48
+ }
49
+ export interface GuardPostureTripwireOpts {
50
+ /** The RESOLVED config object the server is booting with. */
51
+ config: unknown;
52
+ /** Agent state dir (`<projectDir>/.instar`) — snapshot lives at `state/guard-posture.json`. */
53
+ stateDir: string;
54
+ /** Logs dir (`<projectDir>/logs`) — breadcrumb lives at `guard-posture.jsonl`. */
55
+ logsDir: string;
56
+ /** Aggregated Attention emit; absent (no Telegram) → breadcrumb-only. */
57
+ emitAttention?: (item: AttentionItemInput) => Promise<void>;
58
+ /** Boot logger (default console.log). */
59
+ log?: (msg: string) => void;
60
+ /** Clock override (tests). */
61
+ now?: () => Date;
62
+ }
63
+ export interface GuardPostureTripwireResult {
64
+ firstBoot: boolean;
65
+ disabled: string[];
66
+ enabled: string[];
67
+ attentionEmitted: boolean;
68
+ /** Non-fatal error message when the tripwire degraded (never throws). */
69
+ error?: string;
70
+ }
71
+ /**
72
+ * Extract the guard posture from a resolved config object.
73
+ *
74
+ * Covered surface (generic by design — a future guard is covered the moment
75
+ * it follows the `monitoring.<key>.enabled` convention, with no tripwire
76
+ * change):
77
+ * - `monitoring.<key>.enabled` (boolean) → `monitoring.<key>.enabled`
78
+ * - `monitoring.<key>` (plain boolean) → `monitoring.<key>`
79
+ * - `scheduler.enabled` (boolean) → `scheduler.enabled`
80
+ */
81
+ export declare function extractGuardPosture(config: unknown): GuardPosture;
82
+ /**
83
+ * Diff two postures. Only keys present in BOTH snapshots can transition —
84
+ * a key appearing for the first time (new feature) or vanishing (config
85
+ * cleanup) is a shape change, not a guard flip, and raises nothing.
86
+ */
87
+ export declare function diffGuardPosture(prev: GuardPosture, cur: GuardPosture): GuardPostureDiff;
88
+ /** Run the tripwire once at boot. Never throws. */
89
+ export declare function runGuardPostureTripwire(opts: GuardPostureTripwireOpts): Promise<GuardPostureTripwireResult>;
90
+ //# sourceMappingURL=GuardPostureTripwire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GuardPostureTripwire.d.ts","sourceRoot":"","sources":["../../src/monitoring/GuardPostureTripwire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,6DAA6D;IAC7D,MAAM,EAAE,OAAO,CAAC;IAChB,+FAA+F;IAC/F,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,yCAAyC;IACzC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,YAAY,CAwBjE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,YAAY,GAAG,gBAAgB,CASxF;AAeD,mDAAmD;AACnD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,0BAA0B,CAAC,CA2FrC"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * GuardPostureTripwire — a disabled guard is itself an incident.
3
+ *
4
+ * Triggering incident (2026-06-05): the meltdown load-shed at 2:54 PM PDT
5
+ * batch-flipped a set of monitoring guards off in `.instar/config.json` —
6
+ * scheduler.enabled (issue #882), contextWedgeSentinel, failureLearning,
7
+ * resourceLedger, burnDetection. Only the scheduler was noticed and
8
+ * re-enabled (5.5h later); the wedge sentinel stayed dark and watched the
9
+ * EXO 3.0 AUP-rejection wedge kill a session for an hour THAT SAME EVENING
10
+ * without a single audit row. No instar code writes those flags — the flip
11
+ * was emergency hand-editing — so nothing structural recorded it. Two
12
+ * silently-disabled guards discovered in one day is a class, not a
13
+ * coincidence.
14
+ *
15
+ * The tripwire: at every server boot, compare the resolved guard posture
16
+ * (every monitoring.* enabled flag + scheduler.enabled) against the persisted
17
+ * posture from the previous boot. Any guard that went enabled→disabled gets:
18
+ * 1. a loud boot log line,
19
+ * 2. one JSONL breadcrumb row in `logs/guard-posture.jsonl` (same home as
20
+ * sentinel-events.jsonl — the documented "why did X stop?" surface),
21
+ * 3. ONE aggregated Attention item listing every newly-disabled guard
22
+ * (aggregate per the Bounded Notification Surface rule — never one
23
+ * item per guard).
24
+ * Re-enabled guards get the log line + breadcrumb only (good news is not a
25
+ * to-do). First boot (no snapshot) records the posture and raises nothing.
26
+ *
27
+ * Signal-vs-authority: pure detector. It never re-enables anything, never
28
+ * blocks a boot, never edits config — a deliberate disable stays disabled;
29
+ * the Attention item is the consent surface where the operator either
30
+ * acknowledges the flip or goes and re-enables the guard. Errors are
31
+ * swallowed into the log: a broken tripwire must never break a boot.
32
+ */
33
+ import fs from 'node:fs';
34
+ import path from 'node:path';
35
+ /**
36
+ * Extract the guard posture from a resolved config object.
37
+ *
38
+ * Covered surface (generic by design — a future guard is covered the moment
39
+ * it follows the `monitoring.<key>.enabled` convention, with no tripwire
40
+ * change):
41
+ * - `monitoring.<key>.enabled` (boolean) → `monitoring.<key>.enabled`
42
+ * - `monitoring.<key>` (plain boolean) → `monitoring.<key>`
43
+ * - `scheduler.enabled` (boolean) → `scheduler.enabled`
44
+ */
45
+ export function extractGuardPosture(config) {
46
+ const posture = {};
47
+ if (!config || typeof config !== 'object')
48
+ return posture;
49
+ const cfg = config;
50
+ const monitoring = cfg.monitoring;
51
+ if (monitoring && typeof monitoring === 'object' && !Array.isArray(monitoring)) {
52
+ for (const [key, value] of Object.entries(monitoring)) {
53
+ if (typeof value === 'boolean') {
54
+ posture[`monitoring.${key}`] = value;
55
+ }
56
+ else if (value && typeof value === 'object' && !Array.isArray(value)) {
57
+ const enabled = value.enabled;
58
+ if (typeof enabled === 'boolean')
59
+ posture[`monitoring.${key}.enabled`] = enabled;
60
+ }
61
+ }
62
+ }
63
+ const scheduler = cfg.scheduler;
64
+ if (scheduler && typeof scheduler === 'object' && !Array.isArray(scheduler)) {
65
+ const enabled = scheduler.enabled;
66
+ if (typeof enabled === 'boolean')
67
+ posture['scheduler.enabled'] = enabled;
68
+ }
69
+ return posture;
70
+ }
71
+ /**
72
+ * Diff two postures. Only keys present in BOTH snapshots can transition —
73
+ * a key appearing for the first time (new feature) or vanishing (config
74
+ * cleanup) is a shape change, not a guard flip, and raises nothing.
75
+ */
76
+ export function diffGuardPosture(prev, cur) {
77
+ const disabled = [];
78
+ const enabled = [];
79
+ for (const key of Object.keys(cur).sort()) {
80
+ if (!(key in prev))
81
+ continue;
82
+ if (prev[key] === true && cur[key] === false)
83
+ disabled.push(key);
84
+ else if (prev[key] === false && cur[key] === true)
85
+ enabled.push(key);
86
+ }
87
+ return { disabled, enabled };
88
+ }
89
+ function snapshotPath(stateDir) {
90
+ return path.join(stateDir, 'state', 'guard-posture.json');
91
+ }
92
+ function breadcrumbPath(logsDir) {
93
+ return path.join(logsDir, 'guard-posture.jsonl');
94
+ }
95
+ /** Run the tripwire once at boot. Never throws. */
96
+ export async function runGuardPostureTripwire(opts) {
97
+ const log = opts.log ?? ((m) => console.log(m));
98
+ const now = (opts.now ?? (() => new Date()))();
99
+ const result = {
100
+ firstBoot: false,
101
+ disabled: [],
102
+ enabled: [],
103
+ attentionEmitted: false,
104
+ };
105
+ try {
106
+ const posture = extractGuardPosture(opts.config);
107
+ const snapPath = snapshotPath(opts.stateDir);
108
+ let prev = null;
109
+ if (fs.existsSync(snapPath)) {
110
+ try {
111
+ const parsed = JSON.parse(fs.readFileSync(snapPath, 'utf-8'));
112
+ if (parsed && typeof parsed === 'object' && parsed.posture && typeof parsed.posture === 'object') {
113
+ prev = parsed;
114
+ }
115
+ }
116
+ catch {
117
+ // @silent-fallback-ok — a corrupt snapshot degrades to first-boot
118
+ // semantics (re-baseline, no alarms); the new write below repairs it.
119
+ }
120
+ }
121
+ // Persist the new snapshot FIRST so even an emit failure below leaves the
122
+ // baseline current (no repeat alarms for the same transition next boot).
123
+ fs.mkdirSync(path.dirname(snapPath), { recursive: true });
124
+ fs.writeFileSync(snapPath, JSON.stringify({ ts: now.toISOString(), posture }, null, 2));
125
+ if (!prev) {
126
+ result.firstBoot = true;
127
+ log(`[guard-posture] baseline recorded (${Object.keys(posture).length} guards)`);
128
+ return result;
129
+ }
130
+ const diff = diffGuardPosture(prev.posture, posture);
131
+ result.disabled = diff.disabled;
132
+ result.enabled = diff.enabled;
133
+ if (diff.disabled.length === 0 && diff.enabled.length === 0)
134
+ return result;
135
+ // Breadcrumb — one aggregated row per boot that saw transitions.
136
+ try {
137
+ fs.mkdirSync(path.dirname(breadcrumbPath(opts.logsDir)), { recursive: true });
138
+ fs.appendFileSync(breadcrumbPath(opts.logsDir), JSON.stringify({
139
+ ts: now.toISOString(),
140
+ kind: 'guard-posture-change',
141
+ disabled: diff.disabled,
142
+ enabled: diff.enabled,
143
+ prevTs: prev.ts,
144
+ }) + '\n');
145
+ }
146
+ catch (err) {
147
+ result.error = `breadcrumb append failed: ${err instanceof Error ? err.message : String(err)}`;
148
+ }
149
+ for (const key of diff.enabled)
150
+ log(`[guard-posture] guard re-enabled since last boot: ${key}`);
151
+ for (const key of diff.disabled)
152
+ log(`[guard-posture] ⚠ GUARD DISABLED since last boot: ${key}`);
153
+ if (diff.disabled.length > 0 && opts.emitAttention) {
154
+ const list = diff.disabled.join(', ');
155
+ try {
156
+ await opts.emitAttention({
157
+ id: `guard-posture-disabled:${now.toISOString().slice(0, 10)}:${diff.disabled.join(',')}`,
158
+ title: `${diff.disabled.length} monitoring guard(s) disabled since last boot`,
159
+ summary: `These guards were ON at the previous server boot and are OFF now: ${list}. ` +
160
+ `Nothing in instar code flips these flags — this was a config edit. ` +
161
+ `If it was deliberate (e.g. load-shedding), acknowledge this item; otherwise re-enable them in .instar/config.json. ` +
162
+ `History: logs/guard-posture.jsonl.`,
163
+ category: 'monitoring',
164
+ priority: 'HIGH',
165
+ sourceContext: 'guard-posture-tripwire',
166
+ });
167
+ result.attentionEmitted = true;
168
+ }
169
+ catch (err) {
170
+ result.error = `attention emit failed: ${err instanceof Error ? err.message : String(err)}`;
171
+ }
172
+ }
173
+ return result;
174
+ }
175
+ catch (err) {
176
+ // A broken tripwire must never break a boot.
177
+ result.error = err instanceof Error ? err.message : String(err);
178
+ log(`[guard-posture] tripwire degraded: ${result.error}`);
179
+ return result;
180
+ }
181
+ }
182
+ //# sourceMappingURL=GuardPostureTripwire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GuardPostureTripwire.js","sourceRoot":"","sources":["../../src/monitoring/GuardPostureTripwire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AA6C7B;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAe;IACjD,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAC1D,MAAM,GAAG,GAAG,MAAiC,CAAC;IAE9C,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;IAClC,IAAI,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/E,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAqC,CAAC,EAAE,CAAC;YACjF,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC/B,OAAO,CAAC,cAAc,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC;YACvC,CAAC;iBAAM,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACvE,MAAM,OAAO,GAAI,KAAiC,CAAC,OAAO,CAAC;gBAC3D,IAAI,OAAO,OAAO,KAAK,SAAS;oBAAE,OAAO,CAAC,cAAc,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;IAChC,IAAI,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5E,MAAM,OAAO,GAAI,SAAqC,CAAC,OAAO,CAAC;QAC/D,IAAI,OAAO,OAAO,KAAK,SAAS;YAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,OAAO,CAAC;IAC3E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAkB,EAAE,GAAiB;IACpE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC;YAAE,SAAS;QAC7B,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,KAAK;YAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;aAC5D,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC/B,CAAC;AAOD,SAAS,YAAY,CAAC,QAAgB;IACpC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;AACnD,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,IAA8B;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IAC/C,MAAM,MAAM,GAA+B;QACzC,SAAS,EAAE,KAAK;QAChB,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,EAAE;QACX,gBAAgB,EAAE,KAAK;KACxB,CAAC;IAEF,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjD,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE7C,IAAI,IAAI,GAAoB,IAAI,CAAC;QACjC,IAAI,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAa,CAAC;gBAC1E,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;oBACjG,IAAI,GAAG,MAAM,CAAC;gBAChB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,kEAAkE;gBAClE,sEAAsE;YACxE,CAAC;QACH,CAAC;QAED,0EAA0E;QAC1E,yEAAyE;QACzE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,OAAO,EAAqB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAE3G,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;YACxB,GAAG,CAAC,sCAAsC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,UAAU,CAAC,CAAC;YACjF,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAChC,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC9B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QAE3E,iEAAiE;QACjE,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9E,EAAE,CAAC,cAAc,CACf,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAC5B,IAAI,CAAC,SAAS,CAAC;gBACb,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE;gBACrB,IAAI,EAAE,sBAAsB;gBAC5B,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,MAAM,EAAE,IAAI,CAAC,EAAE;aAChB,CAAC,GAAG,IAAI,CACV,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,GAAG,6BAA6B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;QACjG,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO;YAAE,GAAG,CAAC,qDAAqD,GAAG,EAAE,CAAC,CAAC;QAChG,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,QAAQ;YAAE,GAAG,CAAC,qDAAqD,GAAG,EAAE,CAAC,CAAC;QAEjG,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACnD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,aAAa,CAAC;oBACvB,EAAE,EAAE,0BAA0B,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;oBACzF,KAAK,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,+CAA+C;oBAC7E,OAAO,EACL,qEAAqE,IAAI,IAAI;wBAC7E,qEAAqE;wBACrE,qHAAqH;wBACrH,oCAAoC;oBACtC,QAAQ,EAAE,YAAY;oBACtB,QAAQ,EAAE,MAAM;oBAChB,aAAa,EAAE,wBAAwB;iBACxC,CAAC,CAAC;gBACH,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC;YACjC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,GAAG,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9F,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,6CAA6C;QAC7C,MAAM,CAAC,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChE,GAAG,CAAC,sCAAsC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1D,OAAO,MAAM,CAAC;IAChB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.341",
3
+ "version": "1.3.342",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-06T04:58:42.775Z",
5
- "instarVersion": "1.3.341",
4
+ "generatedAt": "2026-06-06T05:04:20.337Z",
5
+ "instarVersion": "1.3.342",
6
6
  "entryCount": 199,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -11,7 +11,7 @@
11
11
  "domain": "identity",
12
12
  "sourcePath": "src/core/PostUpdateMigrator.ts",
13
13
  "installedPath": ".instar/hooks/instar/session-start.sh",
14
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
14
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
15
15
  "since": "2025-01-01"
16
16
  },
17
17
  "hook:dangerous-command-guard": {
@@ -20,7 +20,7 @@
20
20
  "domain": "safety",
21
21
  "sourcePath": "src/core/PostUpdateMigrator.ts",
22
22
  "installedPath": ".instar/hooks/instar/dangerous-command-guard.sh",
23
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
23
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
24
24
  "since": "2025-01-01"
25
25
  },
26
26
  "hook:grounding-before-messaging": {
@@ -29,7 +29,7 @@
29
29
  "domain": "safety",
30
30
  "sourcePath": "src/core/PostUpdateMigrator.ts",
31
31
  "installedPath": ".instar/hooks/instar/grounding-before-messaging.sh",
32
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
32
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
33
33
  "since": "2025-01-01"
34
34
  },
35
35
  "hook:compaction-recovery": {
@@ -38,7 +38,7 @@
38
38
  "domain": "identity",
39
39
  "sourcePath": "src/core/PostUpdateMigrator.ts",
40
40
  "installedPath": ".instar/hooks/instar/compaction-recovery.sh",
41
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
41
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
42
42
  "since": "2025-01-01"
43
43
  },
44
44
  "hook:external-operation-gate": {
@@ -47,7 +47,7 @@
47
47
  "domain": "safety",
48
48
  "sourcePath": "src/core/PostUpdateMigrator.ts",
49
49
  "installedPath": ".instar/hooks/instar/external-operation-gate.js",
50
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
50
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
51
51
  "since": "2025-01-01"
52
52
  },
53
53
  "hook:deferral-detector": {
@@ -56,7 +56,7 @@
56
56
  "domain": "safety",
57
57
  "sourcePath": "src/core/PostUpdateMigrator.ts",
58
58
  "installedPath": ".instar/hooks/instar/deferral-detector.js",
59
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
59
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
60
60
  "since": "2025-01-01"
61
61
  },
62
62
  "hook:self-stop-guard": {
@@ -65,7 +65,7 @@
65
65
  "domain": "coherence",
66
66
  "sourcePath": "src/core/PostUpdateMigrator.ts",
67
67
  "installedPath": ".instar/hooks/instar/self-stop-guard.js",
68
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
68
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
69
69
  "since": "2025-01-01"
70
70
  },
71
71
  "hook:post-action-reflection": {
@@ -74,7 +74,7 @@
74
74
  "domain": "evolution",
75
75
  "sourcePath": "src/core/PostUpdateMigrator.ts",
76
76
  "installedPath": ".instar/hooks/instar/post-action-reflection.js",
77
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
77
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
78
78
  "since": "2025-01-01"
79
79
  },
80
80
  "hook:external-communication-guard": {
@@ -83,7 +83,7 @@
83
83
  "domain": "safety",
84
84
  "sourcePath": "src/core/PostUpdateMigrator.ts",
85
85
  "installedPath": ".instar/hooks/instar/external-communication-guard.js",
86
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
86
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
87
87
  "since": "2025-01-01"
88
88
  },
89
89
  "hook:scope-coherence-collector": {
@@ -92,7 +92,7 @@
92
92
  "domain": "coherence",
93
93
  "sourcePath": "src/core/PostUpdateMigrator.ts",
94
94
  "installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
95
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
95
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
96
96
  "since": "2025-01-01"
97
97
  },
98
98
  "hook:scope-coherence-checkpoint": {
@@ -101,7 +101,7 @@
101
101
  "domain": "coherence",
102
102
  "sourcePath": "src/core/PostUpdateMigrator.ts",
103
103
  "installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
104
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
104
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
105
105
  "since": "2025-01-01"
106
106
  },
107
107
  "hook:free-text-guard": {
@@ -110,7 +110,7 @@
110
110
  "domain": "safety",
111
111
  "sourcePath": "src/core/PostUpdateMigrator.ts",
112
112
  "installedPath": ".instar/hooks/instar/free-text-guard.sh",
113
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
113
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
114
114
  "since": "2025-01-01"
115
115
  },
116
116
  "hook:claim-intercept": {
@@ -119,7 +119,7 @@
119
119
  "domain": "coherence",
120
120
  "sourcePath": "src/core/PostUpdateMigrator.ts",
121
121
  "installedPath": ".instar/hooks/instar/claim-intercept.js",
122
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
122
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
123
123
  "since": "2025-01-01"
124
124
  },
125
125
  "hook:claim-intercept-response": {
@@ -128,7 +128,7 @@
128
128
  "domain": "coherence",
129
129
  "sourcePath": "src/core/PostUpdateMigrator.ts",
130
130
  "installedPath": ".instar/hooks/instar/claim-intercept-response.js",
131
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
131
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
132
132
  "since": "2025-01-01"
133
133
  },
134
134
  "hook:stop-gate-router": {
@@ -137,7 +137,7 @@
137
137
  "domain": "safety",
138
138
  "sourcePath": "src/core/PostUpdateMigrator.ts",
139
139
  "installedPath": ".instar/hooks/instar/stop-gate-router.js",
140
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
140
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "hook:auto-approve-permissions": {
@@ -146,7 +146,7 @@
146
146
  "domain": "safety",
147
147
  "sourcePath": "src/core/PostUpdateMigrator.ts",
148
148
  "installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
149
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
149
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
150
150
  "since": "2025-01-01"
151
151
  },
152
152
  "job:health-check": {
@@ -1538,7 +1538,7 @@
1538
1538
  "type": "subsystem",
1539
1539
  "domain": "updates",
1540
1540
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1541
- "contentHash": "72604345a56ff19867136d0457efe9c4757b0bcaeeaadd48f3558b41caf82941",
1541
+ "contentHash": "67606a9d2c94d2b88f5ec21a08965b25afbbb43e1291de2ebdd0cc77dfed949c",
1542
1542
  "since": "2025-01-01"
1543
1543
  },
1544
1544
  "subsystem:scheduler": {
@@ -0,0 +1,85 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ GuardPostureTripwire — "a disabled guard is itself an incident." Born from
9
+ 2026-06-05, when the morning meltdown load-shed batch-flipped FIVE guards off
10
+ in config.json (scheduler, contextWedgeSentinel, failureLearning,
11
+ resourceLedger, burnDetection) and only the scheduler was noticed: the wedge
12
+ sentinel stayed dark and watched a session die for an hour that same evening
13
+ with zero audit rows. No instar code writes those flags — emergency hand-edits
14
+ leave no trace, so each dark guard was discoverable only by a user-facing
15
+ failure.
16
+
17
+ Now: at every server boot, the resolved guard posture (every `monitoring.*`
18
+ enabled flag + `scheduler.enabled` — generic by convention, no per-guard
19
+ registration) is compared against the persisted posture from the previous
20
+ boot. Any enabled→disabled transition gets a loud boot log line, one
21
+ aggregated row in `logs/guard-posture.jsonl`, and ONE aggregated HIGH
22
+ Attention item listing every newly-disabled guard. Re-enables get the
23
+ breadcrumb only. Signal-only: nothing is ever auto-re-enabled, a broken
24
+ tripwire can never break a boot, and the snapshot advances before alarms so
25
+ the same transition never re-alarms.
26
+
27
+ Queued initial messages now survive server restarts. When a session is
28
+ spawned for an inbound message, the pending inject is recorded durably
29
+ (`<stateDir>/state/pending-injects/`) and cleared only after the message is
30
+ actually typed into the session. On boot, `recoverPendingInjects` sweeps
31
+ survivors: still-alive sessions get re-delivery through the normal readiness
32
+ path; dead or >6h-old records are reported via DegradationReporter and
33
+ retired — a loss is now always VISIBLE.
34
+
35
+ Why: the auto-updater restarted the server while a fresh codex session was
36
+ still booting; the in-memory pending inject died with the process, tmux
37
+ survived idle, and the operator waited 50+ minutes on a silently dropped
38
+ message (finding 8d300555). Delivery is at-least-once by design — a rare
39
+ duplicate beats a silent drop.
40
+
41
+ Also: the "Fresh-spawn fallback succeeded" log now honestly says "launched
42
+ (inject pending)" — it printed before the inject ran.
43
+
44
+ ## What to Tell Your User
45
+
46
+ If any of my safety monitors ever gets switched off — by an emergency
47
+ intervention, a config edit, or anything else — you now hear about it at the
48
+ next restart instead of finding out weeks later when something it should have
49
+ caught hits you. One consolidated heads-up, with the history kept in a log
50
+ file. Nothing gets re-enabled behind your back; the heads-up is where you
51
+ decide.
52
+
53
+ If your agent's server restarts at the exact moment you message it, your
54
+ message no longer risks silently vanishing — it is delivered when the session
55
+ finishes starting, even across the restart.
56
+
57
+ ## Summary of New Capabilities
58
+
59
+ - Boot-time guard-posture comparison with durable history at
60
+ `logs/guard-posture.jsonl` and snapshot at `state/guard-posture.json`.
61
+ Default-on, zero configuration; covers any future guard that follows the
62
+ `monitoring.<key>.enabled` convention automatically.
63
+ - ONE aggregated HIGH Attention item per boot that saw disables (Bounded
64
+ Notification Surface — never per-guard items).
65
+ - CLAUDE.md template + migration: agents are taught to check
66
+ `logs/guard-posture.jsonl` FIRST when asked "why didn't the watchdog catch
67
+ X?" — a silently-disabled guard explains more incidents than a broken one.
68
+
69
+ - `SessionManager.recoverPendingInjects()` — boot-time orphaned-inject sweep
70
+ (wired automatically; not a user-facing surface).
71
+
72
+ ## Evidence
73
+
74
+ Incident grounding: last sentinel-enabled boot 21:54Z = the exact minute of
75
+ the #882 load-shed; 3 more dark guards found by config audit and re-enabled.
76
+ Unit: 12 tests incl. the verbatim 5-guard incident config → exactly ONE
77
+ aggregated item, no-repeat-alarm across boots, re-enable breadcrumb-only,
78
+ corrupt-snapshot self-repair, emit-failure baseline-advance. Migrator: 2
79
+ (added + idempotent). E2E: two-boot lifecycle over real disk + WIRED source
80
+ guard. tsc clean; preflight PASS.
81
+
82
+ 13 new tests (store CRUD/corruption, all four sweep verdicts incl. the live
83
+ incident shape, and wiring-integrity through a real SessionManager: record
84
+ visible on disk DURING the spawn→inject window, cleared after). Adjacent
85
+ session suites green (53 tests); tsc clean.
@@ -0,0 +1,48 @@
1
+ # ELI16 — Guard-Posture Tripwire
2
+
3
+ ## The problem
4
+
5
+ Yesterday morning, during an emergency (the laptop was melting down), somebody
6
+ — one of my own sessions, acting fast — turned off a bunch of my safety
7
+ monitors by editing a config file. That's like flipping the breakers off while
8
+ fighting a kitchen fire: sometimes reasonable in the moment. The problem is
9
+ what came after: nobody wrote down which breakers got flipped. One (the job
10
+ scheduler) was noticed and turned back on 5.5 hours later. FOUR others stayed
11
+ off, silently. That same evening a session died in a brand-new way, and the
12
+ exact watchdog built to catch dying sessions... was one of the monitors still
13
+ switched off. It watched nothing. Justin found out via screenshot, again.
14
+
15
+ The deep issue: none of my code turns these monitors off — humans and
16
+ emergency interventions do, by hand, and hand-edits leave no trace. A monitor
17
+ that's secretly off is worse than a broken one, because everything LOOKS
18
+ covered.
19
+
20
+ ## What this change does
21
+
22
+ At every server start, I now compare "which guards are on right now?" against
23
+ a saved copy of "which guards were on last time I started?" If any guard went
24
+ ON → OFF in between:
25
+
26
+ 1. A loud line in the boot log,
27
+ 2. A permanent record in `logs/guard-posture.jsonl` (so "was the watchdog even
28
+ running in March?" has an answer forever),
29
+ 3. ONE attention item — "these N guards got disabled since last boot, was that
30
+ on purpose?" — not N separate pings.
31
+
32
+ Turning a guard back ON just gets the log entry — good news isn't homework.
33
+
34
+ ## What it deliberately does NOT do
35
+
36
+ It never flips anything back on by itself. If you disabled a monitor on
37
+ purpose, it stays disabled — you just acknowledge the one heads-up. The
38
+ tripwire is a smoke detector, not a sprinkler system. And it can't break
39
+ anything: if the tripwire itself hits an error, the server boots normally and
40
+ the error goes in the log.
41
+
42
+ ## Why this matters beyond yesterday
43
+
44
+ This was the SECOND silently-disabled guard found in one day. That's not bad
45
+ luck, that's a missing structure: there was no system that noticed "a
46
+ protection went away." Now there is, and it automatically covers every future
47
+ monitor too — anything that follows the standard config naming is watched
48
+ from the day it ships, with zero extra wiring.