instar 1.3.30 → 1.3.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,95 @@
1
+ /**
2
+ * instarSettingsHooks.ts — the single source of truth for the instar
3
+ * PreToolUse hook ENTRIES written into `.claude/settings.json`.
4
+ *
5
+ * Why this module exists (the dark-guardrail migration gap, 2026-05-27):
6
+ * `init.ts` wired a full set of PreToolUse Bash hooks for NEW agents, but the
7
+ * existing-agent path (`PostUpdateMigrator.migrateSettings`) only ever ensured
8
+ * two of them (slopcheck-guard + the MCP gate). So four guardrails —
9
+ * grounding-before-messaging, deferral-detector (the false-blocker pre-filter),
10
+ * external-communication-guard, post-action-reflection — shipped to disk on
11
+ * every existing agent but were never switched on in settings. They looked
12
+ * installed and did nothing. Root cause: the "what hooks does an agent get"
13
+ * list lived in two places that drifted.
14
+ *
15
+ * Both the new-agent path (init.ts) and the existing-agent path
16
+ * (PostUpdateMigrator.ensureInstarPreToolUseBashHooks) now import these
17
+ * constants, so the two can never drift again — add a hook here and both
18
+ * paths get it. An anti-drift unit test locks the canonical filename set.
19
+ *
20
+ * NOTE: the objects here are written verbatim into settings.json, so their
21
+ * shape (type/command/blocking/timeout) must stay byte-stable. Presence
22
+ * detection during migration derives the script filename from the command
23
+ * via `instarHookFilename()` — never add a `filename` field to these objects,
24
+ * or new agents' settings would gain a stray key.
25
+ *
26
+ * Scope: the Claude-Code `Bash` and `mcp__.*` PreToolUse matchers only.
27
+ * slopcheck-guard keeps its own dedicated ensure-block in migrateSettings
28
+ * (it is intentionally NOT in this set — it is added by that block for both
29
+ * new and existing agents). Codex hooks (installCodexHooks.ts, `.*` matcher)
30
+ * are a separate path and are not governed by this module.
31
+ */
32
+ export interface InstarSettingsHookEntry {
33
+ type: 'command';
34
+ command: string;
35
+ blocking?: boolean;
36
+ timeout?: number;
37
+ }
38
+ /**
39
+ * The canonical instar PreToolUse `Bash`-matcher hook entries, in canonical
40
+ * order. Mirrors (and is consumed by) init.ts's former `instarBashHooks`.
41
+ */
42
+ export declare const INSTAR_BASH_PRETOOLUSE_HOOKS: ReadonlyArray<InstarSettingsHookEntry>;
43
+ /**
44
+ * The canonical instar PreToolUse `mcp__.*`-matcher hook entries.
45
+ * Mirrors (and is consumed by) init.ts's former `instarMcpHooks`.
46
+ */
47
+ export declare const INSTAR_MCP_PRETOOLUSE_HOOKS: ReadonlyArray<InstarSettingsHookEntry>;
48
+ /**
49
+ * The canonical script filenames for the Bash set, in order. Used by the
50
+ * anti-drift test and as the documented contract. Derived once from the
51
+ * command strings so it can never silently disagree with the entries above.
52
+ */
53
+ export declare const INSTAR_BASH_PRETOOLUSE_FILENAMES: ReadonlyArray<string>;
54
+ /**
55
+ * Extract the instar hook script filename from a settings hook command, e.g.
56
+ * `node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/deferral-detector.js` ->
57
+ * `deferral-detector.js`. Returns null if the command does not reference an
58
+ * instar hook script. Used for idempotent presence detection during migration
59
+ * (substring/filename match is robust to ${CLAUDE_PROJECT_DIR} vs absolute
60
+ * path variations, unlike an exact-command compare).
61
+ */
62
+ export declare function instarHookFilename(command: string): string | null;
63
+ /** Minimal structural shape of a settings PreToolUse matcher entry. */
64
+ export interface SettingsMatcherEntry {
65
+ matcher?: string;
66
+ hooks?: Array<{
67
+ command?: string;
68
+ type?: string;
69
+ blocking?: boolean;
70
+ timeout?: number;
71
+ }>;
72
+ }
73
+ /**
74
+ * Idempotently ensure every canonical instar Bash PreToolUse hook is present
75
+ * in an existing agent's settings. Mirrors init.ts (both consume
76
+ * INSTAR_BASH_PRETOOLUSE_HOOKS), closing the dark-guardrail migration gap.
77
+ *
78
+ * Behavior:
79
+ * - Finds the `Bash` matcher (creates one if absent).
80
+ * - For each canonical hook, adds it ONLY IF no existing hook command already
81
+ * references that script filename (filename/substring match — robust to
82
+ * ${CLAUDE_PROJECT_DIR} vs absolute path; prevents duplicates).
83
+ * - APPENDS missing hooks in canonical order; never reorders or removes
84
+ * existing entries (hand-curated settings are preserved).
85
+ * - Pushes fresh copies of the canonical entries (never the shared objects).
86
+ *
87
+ * Returns the list of filenames that were added (empty array == no-op, so the
88
+ * caller can decide whether to flag the settings file as patched). Safe to run
89
+ * repeatedly: once every hook is present, every subsequent call returns [].
90
+ *
91
+ * Does NOT touch slopcheck-guard (its own dedicated ensure-block owns it) or
92
+ * any non-Bash matcher.
93
+ */
94
+ export declare function ensureInstarBashPreToolUseHooks(preToolUse: SettingsMatcherEntry[]): string[];
95
+ //# sourceMappingURL=instarSettingsHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instarSettingsHooks.d.ts","sourceRoot":"","sources":["../../src/core/instarSettingsHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,aAAa,CAAC,uBAAuB,CA0B/E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,aAAa,CAAC,uBAAuB,CAO9E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,EAAE,aAAa,CAAC,MAAM,CACW,CAAC;AAE/E;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AAED,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,+BAA+B,CAAC,UAAU,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,CAmB5F"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * instarSettingsHooks.ts — the single source of truth for the instar
3
+ * PreToolUse hook ENTRIES written into `.claude/settings.json`.
4
+ *
5
+ * Why this module exists (the dark-guardrail migration gap, 2026-05-27):
6
+ * `init.ts` wired a full set of PreToolUse Bash hooks for NEW agents, but the
7
+ * existing-agent path (`PostUpdateMigrator.migrateSettings`) only ever ensured
8
+ * two of them (slopcheck-guard + the MCP gate). So four guardrails —
9
+ * grounding-before-messaging, deferral-detector (the false-blocker pre-filter),
10
+ * external-communication-guard, post-action-reflection — shipped to disk on
11
+ * every existing agent but were never switched on in settings. They looked
12
+ * installed and did nothing. Root cause: the "what hooks does an agent get"
13
+ * list lived in two places that drifted.
14
+ *
15
+ * Both the new-agent path (init.ts) and the existing-agent path
16
+ * (PostUpdateMigrator.ensureInstarPreToolUseBashHooks) now import these
17
+ * constants, so the two can never drift again — add a hook here and both
18
+ * paths get it. An anti-drift unit test locks the canonical filename set.
19
+ *
20
+ * NOTE: the objects here are written verbatim into settings.json, so their
21
+ * shape (type/command/blocking/timeout) must stay byte-stable. Presence
22
+ * detection during migration derives the script filename from the command
23
+ * via `instarHookFilename()` — never add a `filename` field to these objects,
24
+ * or new agents' settings would gain a stray key.
25
+ *
26
+ * Scope: the Claude-Code `Bash` and `mcp__.*` PreToolUse matchers only.
27
+ * slopcheck-guard keeps its own dedicated ensure-block in migrateSettings
28
+ * (it is intentionally NOT in this set — it is added by that block for both
29
+ * new and existing agents). Codex hooks (installCodexHooks.ts, `.*` matcher)
30
+ * are a separate path and are not governed by this module.
31
+ */
32
+ const PD = '${CLAUDE_PROJECT_DIR}';
33
+ /**
34
+ * The canonical instar PreToolUse `Bash`-matcher hook entries, in canonical
35
+ * order. Mirrors (and is consumed by) init.ts's former `instarBashHooks`.
36
+ */
37
+ export const INSTAR_BASH_PRETOOLUSE_HOOKS = [
38
+ {
39
+ type: 'command',
40
+ command: `bash ${PD}/.instar/hooks/instar/dangerous-command-guard.sh "$TOOL_INPUT"`,
41
+ blocking: true,
42
+ },
43
+ {
44
+ type: 'command',
45
+ command: `bash ${PD}/.instar/hooks/instar/grounding-before-messaging.sh "$TOOL_INPUT"`,
46
+ blocking: false,
47
+ },
48
+ {
49
+ type: 'command',
50
+ command: `node ${PD}/.instar/hooks/instar/deferral-detector.js`,
51
+ timeout: 5000,
52
+ },
53
+ {
54
+ type: 'command',
55
+ command: `node ${PD}/.instar/hooks/instar/external-communication-guard.js`,
56
+ timeout: 5000,
57
+ },
58
+ {
59
+ type: 'command',
60
+ command: `node ${PD}/.instar/hooks/instar/post-action-reflection.js`,
61
+ timeout: 5000,
62
+ },
63
+ ];
64
+ /**
65
+ * The canonical instar PreToolUse `mcp__.*`-matcher hook entries.
66
+ * Mirrors (and is consumed by) init.ts's former `instarMcpHooks`.
67
+ */
68
+ export const INSTAR_MCP_PRETOOLUSE_HOOKS = [
69
+ {
70
+ type: 'command',
71
+ command: `node ${PD}/.instar/hooks/instar/external-operation-gate.js`,
72
+ blocking: true,
73
+ timeout: 5000,
74
+ },
75
+ ];
76
+ /**
77
+ * The canonical script filenames for the Bash set, in order. Used by the
78
+ * anti-drift test and as the documented contract. Derived once from the
79
+ * command strings so it can never silently disagree with the entries above.
80
+ */
81
+ export const INSTAR_BASH_PRETOOLUSE_FILENAMES = INSTAR_BASH_PRETOOLUSE_HOOKS.map((h) => instarHookFilename(h.command) ?? '');
82
+ /**
83
+ * Extract the instar hook script filename from a settings hook command, e.g.
84
+ * `node ${CLAUDE_PROJECT_DIR}/.instar/hooks/instar/deferral-detector.js` ->
85
+ * `deferral-detector.js`. Returns null if the command does not reference an
86
+ * instar hook script. Used for idempotent presence detection during migration
87
+ * (substring/filename match is robust to ${CLAUDE_PROJECT_DIR} vs absolute
88
+ * path variations, unlike an exact-command compare).
89
+ */
90
+ export function instarHookFilename(command) {
91
+ const m = command.match(/\/\.instar\/hooks\/instar\/([\w.-]+\.(?:sh|js|mjs|cjs))/);
92
+ return m ? m[1] : null;
93
+ }
94
+ /**
95
+ * Idempotently ensure every canonical instar Bash PreToolUse hook is present
96
+ * in an existing agent's settings. Mirrors init.ts (both consume
97
+ * INSTAR_BASH_PRETOOLUSE_HOOKS), closing the dark-guardrail migration gap.
98
+ *
99
+ * Behavior:
100
+ * - Finds the `Bash` matcher (creates one if absent).
101
+ * - For each canonical hook, adds it ONLY IF no existing hook command already
102
+ * references that script filename (filename/substring match — robust to
103
+ * ${CLAUDE_PROJECT_DIR} vs absolute path; prevents duplicates).
104
+ * - APPENDS missing hooks in canonical order; never reorders or removes
105
+ * existing entries (hand-curated settings are preserved).
106
+ * - Pushes fresh copies of the canonical entries (never the shared objects).
107
+ *
108
+ * Returns the list of filenames that were added (empty array == no-op, so the
109
+ * caller can decide whether to flag the settings file as patched). Safe to run
110
+ * repeatedly: once every hook is present, every subsequent call returns [].
111
+ *
112
+ * Does NOT touch slopcheck-guard (its own dedicated ensure-block owns it) or
113
+ * any non-Bash matcher.
114
+ */
115
+ export function ensureInstarBashPreToolUseHooks(preToolUse) {
116
+ const added = [];
117
+ let bashEntry = preToolUse.find((e) => e.matcher === 'Bash');
118
+ if (!bashEntry) {
119
+ bashEntry = { matcher: 'Bash', hooks: [] };
120
+ preToolUse.push(bashEntry);
121
+ }
122
+ if (!bashEntry.hooks)
123
+ bashEntry.hooks = [];
124
+ for (const canonical of INSTAR_BASH_PRETOOLUSE_HOOKS) {
125
+ const fname = instarHookFilename(canonical.command);
126
+ if (!fname)
127
+ continue;
128
+ const present = bashEntry.hooks.some((h) => typeof h.command === 'string' && h.command.includes(fname));
129
+ if (!present) {
130
+ bashEntry.hooks.push({ ...canonical });
131
+ added.push(fname);
132
+ }
133
+ }
134
+ return added;
135
+ }
136
+ //# sourceMappingURL=instarSettingsHooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instarSettingsHooks.js","sourceRoot":"","sources":["../../src/core/instarSettingsHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AASH,MAAM,EAAE,GAAG,uBAAuB,CAAC;AAEnC;;;GAGG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAA2C;IAClF;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE,gEAAgE;QACnF,QAAQ,EAAE,IAAI;KACf;IACD;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE,mEAAmE;QACtF,QAAQ,EAAE,KAAK;KAChB;IACD;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE,4CAA4C;QAC/D,OAAO,EAAE,IAAI;KACd;IACD;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE,uDAAuD;QAC1E,OAAO,EAAE,IAAI;KACd;IACD;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE,iDAAiD;QACpE,OAAO,EAAE,IAAI;KACd;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAA2C;IACjF;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,QAAQ,EAAE,kDAAkD;QACrE,QAAQ,EAAE,IAAI;QACd,OAAO,EAAE,IAAI;KACd;CACF,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAC3C,4BAA4B,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,yDAAyD,CAAC,CAAC;IACnF,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAQD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,+BAA+B,CAAC,UAAkC;IAChF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;IAC7D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,SAAS,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;QAC3C,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,KAAK;QAAE,SAAS,CAAC,KAAK,GAAG,EAAE,CAAC;IAE3C,KAAK,MAAM,SAAS,IAAI,4BAA4B,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACxG,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;YACvC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.30",
3
+ "version": "1.3.32",
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-05-27T21:58:29.713Z",
5
- "instarVersion": "1.3.30",
4
+ "generatedAt": "2026-05-27T22:28:16.266Z",
5
+ "instarVersion": "1.3.32",
6
6
  "entryCount": 192,
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": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
14
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
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": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
23
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
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": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
32
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
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": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
41
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
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": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
50
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
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": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
59
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
60
60
  "since": "2025-01-01"
61
61
  },
62
62
  "hook:post-action-reflection": {
@@ -65,7 +65,7 @@
65
65
  "domain": "evolution",
66
66
  "sourcePath": "src/core/PostUpdateMigrator.ts",
67
67
  "installedPath": ".instar/hooks/instar/post-action-reflection.js",
68
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
68
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
69
69
  "since": "2025-01-01"
70
70
  },
71
71
  "hook:external-communication-guard": {
@@ -74,7 +74,7 @@
74
74
  "domain": "safety",
75
75
  "sourcePath": "src/core/PostUpdateMigrator.ts",
76
76
  "installedPath": ".instar/hooks/instar/external-communication-guard.js",
77
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
77
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
78
78
  "since": "2025-01-01"
79
79
  },
80
80
  "hook:scope-coherence-collector": {
@@ -83,7 +83,7 @@
83
83
  "domain": "coherence",
84
84
  "sourcePath": "src/core/PostUpdateMigrator.ts",
85
85
  "installedPath": ".instar/hooks/instar/scope-coherence-collector.js",
86
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
86
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
87
87
  "since": "2025-01-01"
88
88
  },
89
89
  "hook:scope-coherence-checkpoint": {
@@ -92,7 +92,7 @@
92
92
  "domain": "coherence",
93
93
  "sourcePath": "src/core/PostUpdateMigrator.ts",
94
94
  "installedPath": ".instar/hooks/instar/scope-coherence-checkpoint.js",
95
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
95
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
96
96
  "since": "2025-01-01"
97
97
  },
98
98
  "hook:free-text-guard": {
@@ -101,7 +101,7 @@
101
101
  "domain": "safety",
102
102
  "sourcePath": "src/core/PostUpdateMigrator.ts",
103
103
  "installedPath": ".instar/hooks/instar/free-text-guard.sh",
104
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
104
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
105
105
  "since": "2025-01-01"
106
106
  },
107
107
  "hook:claim-intercept": {
@@ -110,7 +110,7 @@
110
110
  "domain": "coherence",
111
111
  "sourcePath": "src/core/PostUpdateMigrator.ts",
112
112
  "installedPath": ".instar/hooks/instar/claim-intercept.js",
113
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
113
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
114
114
  "since": "2025-01-01"
115
115
  },
116
116
  "hook:claim-intercept-response": {
@@ -119,7 +119,7 @@
119
119
  "domain": "coherence",
120
120
  "sourcePath": "src/core/PostUpdateMigrator.ts",
121
121
  "installedPath": ".instar/hooks/instar/claim-intercept-response.js",
122
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
122
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
123
123
  "since": "2025-01-01"
124
124
  },
125
125
  "hook:stop-gate-router": {
@@ -128,7 +128,7 @@
128
128
  "domain": "safety",
129
129
  "sourcePath": "src/core/PostUpdateMigrator.ts",
130
130
  "installedPath": ".instar/hooks/instar/stop-gate-router.js",
131
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
131
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
132
132
  "since": "2025-01-01"
133
133
  },
134
134
  "hook:auto-approve-permissions": {
@@ -137,7 +137,7 @@
137
137
  "domain": "safety",
138
138
  "sourcePath": "src/core/PostUpdateMigrator.ts",
139
139
  "installedPath": ".instar/hooks/instar/auto-approve-permissions.js",
140
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
140
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
141
141
  "since": "2025-01-01"
142
142
  },
143
143
  "job:health-check": {
@@ -1481,7 +1481,7 @@
1481
1481
  "type": "subsystem",
1482
1482
  "domain": "updates",
1483
1483
  "sourcePath": "src/core/PostUpdateMigrator.ts",
1484
- "contentHash": "d7515c04edc8ced2590d4f8d671ba8f6e3a9ec2142b3fc77bf54cf5ac8a67d4d",
1484
+ "contentHash": "7df6ffd4a8d6e5e8c98e888bed89fffec63347268d0a26819b8de60ab32cd347",
1485
1485
  "since": "2025-01-01"
1486
1486
  },
1487
1487
  "subsystem:scheduler": {
@@ -0,0 +1,29 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ Notify-on-stop, Layer A: when an autonomous run reaches a terminal exit — it completed, ran out of its time budget, or was emergency-stopped — the agent now sends one plain-English Telegram to the run's topic explaining why it stopped. Previously these reasons were only written to stderr (the terminal the user can't see), so an autonomous run could end in silence unless the agent remembered to send a wrap-up. This bakes the "a session either keeps going OR tells you why it stopped" guarantee into the stop hook itself.
8
+
9
+ The notice is best-effort and non-blocking (a delivery hiccup never blocks the exit), fires at most once per run, and reuses the existing Telegram reply transport + the run's existing topic (no new topic is ever created).
10
+
11
+ Existing agents receive the notify-enabled hook automatically: the autonomous-stop-hook capability marker is bumped, so the marker+fingerprint migration re-deploys the updated hook to stock installs on update (customized hooks are left untouched).
12
+
13
+ ## What to Tell Your User
14
+
15
+ - When one of my autonomous runs finishes (or times out, or is stopped), you now get a short heads-up explaining why — no more silent endings.
16
+
17
+ ## Summary of New Capabilities
18
+
19
+ | Capability | How to Use |
20
+ |-----------|-----------|
21
+ | Autonomous-run terminal exits send a Telegram explaining why | Automatic; one message per run to the run's topic |
22
+
23
+ ## Evidence
24
+
25
+ - Hook: `.claude/skills/autonomous/hooks/autonomous-stop-hook.sh` (`notify_terminal_stop` at all 6 terminal exits).
26
+ - Migration: `src/core/PostUpdateMigrator.ts` (capability marker → `notify_terminal_stop`).
27
+ - Tests: `tests/unit/autonomous-stop-hook-notify.test.ts` (9 — static wiring, functional delivery via the real extracted helper, migration).
28
+ - Spec: `docs/specs/NOTIFY-ON-STOP-SPEC.md` (approved) + `.eli16.md`. Side-effects: `upgrades/side-effects/notify-on-stop-layer-a.md`.
29
+ - Layer B (watchdog-caught mid-task stalls) ships separately under the same spec.
@@ -0,0 +1,28 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- bump: patch -->
4
+
5
+ ## What Changed
6
+
7
+ Closes a fleet-wide "dark guardrail" migration gap. `instar init` wires a full set of PreToolUse `Bash` guardrail hooks for new agents, but the existing-agent update path (`PostUpdateMigrator.migrateSettings`) only ever switched on two of them. So four guardrails — `deferral-detector.js` (the false-blocker / anti-deferral pre-filter), `grounding-before-messaging.sh`, `external-communication-guard.js`, and `post-action-reflection.js` — were copied to disk on every existing agent but never wired into `.claude/settings.json`, leaving them installed-but-inert. Same failure class as the 2026-05-27 silent-stall incident.
8
+
9
+ This release introduces a single source of truth for the canonical instar PreToolUse hook set (`src/core/instarSettingsHooks.ts`), consumed by BOTH the new-agent path (`init.ts`) and the existing-agent path, so the two can never drift again. `migrateSettings` now idempotently ensures every canonical Bash guardrail is present (appends only the missing ones; never reorders or removes; leaves custom hooks alone).
10
+
11
+ ## What to Tell Your User
12
+
13
+ - Four guardrail hooks that were quietly switched off on existing agents — including the "false blocker" catcher that stops the agent handing a doable task back to you — come on at this update. New agents already had them; no action needed.
14
+
15
+ ## Summary of New Capabilities
16
+
17
+ | Capability | How to Use |
18
+ |-----------|-----------|
19
+ | Existing agents get the full instar PreToolUse guardrail set on update | Automatic on update; no action needed |
20
+ | Anti-drift: new-agent + existing-agent hook lists share one constant | Adding a hook to `INSTAR_BASH_PRETOOLUSE_HOOKS` wires both paths; a unit test locks the set |
21
+
22
+ ## Evidence
23
+
24
+ - New module + tests: `src/core/instarSettingsHooks.ts`, `tests/unit/instar-settings-hooks.test.ts` (16), `tests/unit/PostUpdateMigrator-pretooluse-parity.test.ts` (3, real migrateSettings).
25
+ - Spec: `docs/specs/EXISTING-AGENT-PRETOOLUSE-HOOK-PARITY-SPEC.md` (approved)
26
+ - ELI16: `docs/specs/EXISTING-AGENT-PRETOOLUSE-HOOK-PARITY-SPEC.eli16.md`
27
+ - Side-effects review: `upgrades/side-effects/existing-agent-pretooluse-hook-parity.md`
28
+ - Context: Task 3 of the 2026-05-27 silent-stalls postmortem.
@@ -0,0 +1,39 @@
1
+ # Side-Effects Review — Existing-Agent PreToolUse Hook Parity
2
+
3
+ **Spec:** docs/specs/EXISTING-AGENT-PRETOOLUSE-HOOK-PARITY-SPEC.md (approved: true, internal-adversarial review 2026-05-27)
4
+
5
+ Closes the dark-guardrail migration gap: `init.ts` wired a full instar Bash PreToolUse hook set for NEW agents, but `PostUpdateMigrator.migrateSettings` only ensured slopcheck-guard + the MCP gate for EXISTING agents — so `deferral-detector.js` (the false-blocker pre-filter; this is Task 3 of the silent-stalls postmortem), `grounding-before-messaging.sh`, `external-communication-guard.js`, and `post-action-reflection.js` shipped to disk on every existing agent but were never switched on in settings. Same failure class as the 2026-05-27 silent-stall incident (looks installed, does nothing).
6
+
7
+ ## What changed
8
+ - `src/core/instarSettingsHooks.ts` — NEW. Single source of truth for the canonical instar `Bash` and `mcp__.*` PreToolUse hook ENTRIES (`INSTAR_BASH_PRETOOLUSE_HOOKS`, `INSTAR_MCP_PRETOOLUSE_HOOKS`), a `instarHookFilename()` helper, and the idempotent `ensureInstarBashPreToolUseHooks()`.
9
+ - `src/commands/init.ts` — refactored the inline `instarBashHooks` / `instarMcpHooks` literals to consume the shared constants (fresh copies via `.map(h => ({...h}))`). Behavior-preserving: byte-identical settings output for new agents (verified by the existing `hook-installation.test.ts`, all green).
10
+ - `src/core/PostUpdateMigrator.ts` — `migrateSettings` now calls `ensureInstarBashPreToolUseHooks(preToolUse)` before the slopcheck block; appends only missing canonical hooks, idempotently, reporting each as a `dark-guardrail wiring` upgrade.
11
+ - Tests: `tests/unit/instar-settings-hooks.test.ts` (16) + `tests/unit/PostUpdateMigrator-pretooluse-parity.test.ts` (3).
12
+
13
+ ## Over-block / under-block
14
+ - UNDER: a custom hook with the same script filename would be treated as "present" and not re-added. Acceptable — that's the intent (don't duplicate). Custom hooks under `.claude/hooks/custom/` have different filenames, so they're never matched/displaced.
15
+ - OVER: none. The function only APPENDS missing canonical hooks; it never reorders or removes existing entries, and it touches only the `Bash` matcher. Slopcheck-guard and all non-Bash matchers are untouched.
16
+
17
+ ## Signal vs authority
18
+ N/A for blocking — this wires guardrail hooks into settings. Within the false-blocker design, `deferral-detector` is the SIGNAL (non-blocking checklist injection); `MessagingToneGate` B17 is the AUTHORITY (already live). This change lights up the signal layer on existing agents; it adds no new blocking authority.
19
+
20
+ ## Idempotency / migration parity
21
+ - IS a migration-parity fix. New-agent path (init.ts) and existing-agent path (migrateSettings) now consume the SAME constant — drift is impossible by construction; an anti-drift unit test locks the canonical filename set.
22
+ - `ensureInstarBashPreToolUseHooks` is idempotent (filename-substring presence check, robust to `${CLAUDE_PROJECT_DIR}` vs absolute path); re-runs are no-ops (verified).
23
+ - No config or CLAUDE.md change. No new hook scripts (all four already ship + are ESM-safe).
24
+
25
+ ## Interactions
26
+ - Runs before the existing slopcheck ensure-block; since it creates the `Bash` matcher when absent, slopcheck's "no Bash matcher" branch becomes a no-op while its "add slopcheck" branch still fires. Both end states verified together in the parity test.
27
+ - Codex hooks (`installCodexHooks.ts`, `.*` matcher) are a separate path, intentionally not governed by this module.
28
+
29
+ ## Rollback
30
+ Revert the migrator call + the init refactor + delete the shared module (3 files) and the 2 test files. Worst case: existing agents return to the dark-guardrail state.
31
+
32
+ ## Tests
33
+ - `tests/unit/instar-settings-hooks.test.ts` (16) — anti-drift contract, filename extraction, add/idempotent/preserve/no-matcher/no-dup/fresh-copy, init-imports-the-constant.
34
+ - `tests/unit/PostUpdateMigrator-pretooluse-parity.test.ts` (3) — real `migrateSettings` against a stale agent home: wires the four, idempotent, reports each.
35
+ - Regression-safety: `hook-installation.test.ts`, `installCodexHooks.test.ts`, `codexHookContractCanary.test.ts`, `autonomous-skill-deployment.test.ts`, `capabilities-discoverability.test.ts` all green post-refactor.
36
+
37
+ ## NOT in this commit (tracked)
38
+ - Notify-on-stop (Task 2 — separate PR/spec).
39
+ - Self-propagation harness (Task 4).
@@ -0,0 +1,31 @@
1
+ # Side-Effects Review — Notify-on-Stop, Layer A (autonomous-run-ended notice)
2
+
3
+ **Spec:** docs/specs/NOTIFY-ON-STOP-SPEC.md (approved: true) — Layer A of two. Layer B (gate-fed unjustified-stop notice) ships as a separate PR.
4
+
5
+ Closes the primary silent-stall scenario: an autonomous run reaching a terminal exit (completion / duration-expiry / emergency-stop) previously only echoed the reason to stderr — the terminal the user can't see — so the run could end in silence unless the agent *remembered* to send a final report (willpower, not structure). Justin's standing requirement: a session either keeps going OR tells you why it stopped.
6
+
7
+ ## What changed
8
+ - `.claude/skills/autonomous/hooks/autonomous-stop-hook.sh` — added two helpers (`goal_snippet`, `notify_terminal_stop`) and a `notify_terminal_stop "<plain message>"` call before EACH of the six terminal exits (native-mode emergency + duration; legacy duration, emergency, completion-condition, completion-promise). Each sends ONE plain-English Telegram to the run's `report_topic` via `telegram-reply.sh` — the same transport the existing restart-resume recovery note uses.
9
+ - `src/core/PostUpdateMigrator.ts` — bumped the autonomous-stop-hook capability marker from `Native /goal delegation` to `notify_terminal_stop` so existing agents on a prior stock hook receive the notify-enabled version on update (the marker+fingerprint mechanism; customized hooks left untouched).
10
+ - `tests/unit/autonomous-stop-hook-notify.test.ts` — static wiring (helper defined + called at all 6 exits), functional delivery (runs the REAL extracted helper against a stub telegram-reply.sh; both no-op gates), and migration (a stock pre-notify hook gains `notify_terminal_stop`).
11
+
12
+ ## Behavior / safety
13
+ - **Best-effort, non-blocking:** `notify_terminal_stop` ends every path with `|| true` and returns early if there's no report topic or the channel isn't telegram. A delivery failure NEVER changes the hook's exit code or blocks the terminal exit.
14
+ - **Fires at most once per run:** every terminal branch removes the state file immediately after, so the hook can't re-enter and re-notify.
15
+ - **No new transport / topic:** reuses `telegram-reply.sh` to the run's existing report topic. No new Telegram topic is ever created.
16
+
17
+ ## Over/under-notify
18
+ - OVER: at completion, the agent's own final report (if it sent one) plus Layer A's brief "run finished" backstop = a possible second short message. Accepted — the structural guarantee that *something* is sent outweighs minor redundancy; the backstop is one line.
19
+ - UNDER: a heavily-customized hook (no stock fingerprint) won't receive the migration — same accepted limit as every autonomous-hook migration. Channel != telegram → no send (Layer A is telegram-scoped; other channels await Channel Parity).
20
+
21
+ ## Near-silent compliance
22
+ An autonomous run ending is an action-relevant, once-per-run event — exactly the kind the user asked to be told about. Not routine churn. No throttling needed (one message per terminal exit, max once per run).
23
+
24
+ ## Migration parity
25
+ `migrateAutonomousStopHookTopicKeyed` (marker bump) ships the updated hook to existing agents; new agents get it via the bundled file at init. Verified by the migration test.
26
+
27
+ ## Rollback
28
+ Revert the hook edits + the marker bump (2 files) + the test. Worst case: terminal exits go back to stderr-only.
29
+
30
+ ## NOT in this PR (tracked)
31
+ - Layer B: gate-fed unjustified-mid-task-stop notice via the stop-gate evaluate route + a StopNotifier reusing SentinelNotifier's coalescing sink. Separate PR under the same spec.