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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +45 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +22 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/monitoring/GuardPostureTripwire.d.ts +90 -0
- package/dist/monitoring/GuardPostureTripwire.d.ts.map +1 -0
- package/dist/monitoring/GuardPostureTripwire.js +182 -0
- package/dist/monitoring/GuardPostureTripwire.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +19 -19
- package/upgrades/1.3.342.md +85 -0
- package/upgrades/guard-posture-tripwire.eli16.md +48 -0
- package/upgrades/side-effects/guard-posture-tripwire.md +76 -0
- package/upgrades/1.3.341.md +0 -41
|
@@ -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":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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.
|