immune-brain 5.2.0 → 5.3.0
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/package.json +1 -1
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/dist/BASELINE.md +5 -1
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +30 -26
- package/plugins/immune-brain/dist/imm-brainstorm.md +1 -1
- package/plugins/immune-brain/dist/imm-planner.md +8 -5
- package/plugins/immune-brain/hooks/hooks.json +1 -1
- package/plugins/immune-brain/runtime/assurance/phase_entry.ts +12 -22
- package/plugins/immune-brain/runtime/claude/lane_guard.ts +34 -17
- package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
- package/plugins/immune-brain/skills/BASELINE.md +5 -1
- package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +4 -3
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +3 -2
package/package.json
CHANGED
|
@@ -55,7 +55,11 @@ framing does not load that execution guidance.
|
|
|
55
55
|
|
|
56
56
|
Ordinary host input stays host-native and does not run natural-language Managed
|
|
57
57
|
routing. A new Managed workflow starts only from explicit `imm-brainstorm`,
|
|
58
|
-
`imm-planner`, or `imm-run` Skill entry.
|
|
58
|
+
`imm-planner`, or `imm-run` Skill entry. For `imm-planner` and `imm-brainstorm`
|
|
59
|
+
the user's request for that planning or clarification, by slash command or in
|
|
60
|
+
natural language, is such an entry; a request that asks for neither stays
|
|
61
|
+
host-native. `imm-run` starts only from a literal-user entry. A model-entered
|
|
62
|
+
Planner outside an enrolled task ends the session's run entry.
|
|
59
63
|
|
|
60
64
|
1. **Continue an existing Managed owner explicitly**: an active Assurance
|
|
61
65
|
projection, TaskIntent, TaskRecord, reviewer `follow_up`, or other nonterminal
|
|
@@ -39,7 +39,7 @@ function probeHost(env = process.env, platform = process.platform, hostVersion)
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// plugins/immune-brain/runtime/plugin_version.ts
|
|
42
|
-
var PLUGIN_VERSION = "5.
|
|
42
|
+
var PLUGIN_VERSION = "5.3.0";
|
|
43
43
|
|
|
44
44
|
// plugins/immune-brain/runtime/claude/interaction.ts
|
|
45
45
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -95,14 +95,13 @@ function recognizePhaseEntry(text, readAlias) {
|
|
|
95
95
|
return null;
|
|
96
96
|
return ALIAS_TARGETS[[...targets][0]] ?? null;
|
|
97
97
|
}
|
|
98
|
-
var NO_PHASE_ENTRY = {
|
|
98
|
+
var NO_PHASE_ENTRY = { run_live: false };
|
|
99
99
|
function applyPrompt(state, entry) {
|
|
100
|
-
const prompt_seq = state.prompt_seq + 1;
|
|
101
100
|
if (entry === "run")
|
|
102
|
-
return {
|
|
101
|
+
return { run_live: true };
|
|
103
102
|
if (entry === "plan")
|
|
104
|
-
return {
|
|
105
|
-
return
|
|
103
|
+
return { run_live: false };
|
|
104
|
+
return state;
|
|
106
105
|
}
|
|
107
106
|
function phaseEntryLogLine(entry) {
|
|
108
107
|
return `${JSON.stringify({ v: 1, entry })}
|
|
@@ -138,17 +137,11 @@ function admitPhaseOperation(operation, state, context) {
|
|
|
138
137
|
return { ok: true };
|
|
139
138
|
return refuse(RUN_ENTRY_REQUIRED, `${operation.name} needs a literal-user imm-run entry in this session`);
|
|
140
139
|
}
|
|
141
|
-
|
|
142
|
-
if (operation.skill === "imm-run") {
|
|
143
|
-
if (state?.run_live && state.run_prompt_seq === latest)
|
|
144
|
-
return { ok: true };
|
|
145
|
-
return refuse(PHASE_ENTRY_REQUIRED, "imm-run is entered by the user, not by a model Skill call");
|
|
146
|
-
}
|
|
147
|
-
if (state && state.plan_prompt_seq === latest)
|
|
140
|
+
if (operation.skill !== "imm-run")
|
|
148
141
|
return { ok: true };
|
|
149
|
-
if (
|
|
142
|
+
if (state?.run_live)
|
|
150
143
|
return { ok: true };
|
|
151
|
-
return refuse(PHASE_ENTRY_REQUIRED,
|
|
144
|
+
return refuse(PHASE_ENTRY_REQUIRED, "imm-run is entered by the user, not by a model Skill call");
|
|
152
145
|
}
|
|
153
146
|
function phaseRefusalMessage(refusal) {
|
|
154
147
|
return `${refusal.reason_code}: ${refusal.detail}; recovery: ${refusal.recovery_action}`;
|
|
@@ -9229,20 +9222,25 @@ function dropPrivate(path) {
|
|
|
9229
9222
|
rmSync5(path, { force: true });
|
|
9230
9223
|
} catch {}
|
|
9231
9224
|
}
|
|
9225
|
+
function appendPhaseEntry(sessionId, entry, root, locations) {
|
|
9226
|
+
const path = entryLogPath(sessionId, root);
|
|
9227
|
+
let recorded = false;
|
|
9228
|
+
try {
|
|
9229
|
+
recorded = (locations.append ?? appendPrivate)(path, cacheDir(root), phaseEntryLogLine(entry));
|
|
9230
|
+
} catch {}
|
|
9231
|
+
if (!recorded)
|
|
9232
|
+
dropPrivate(path);
|
|
9233
|
+
return recorded;
|
|
9234
|
+
}
|
|
9232
9235
|
function recordPhaseEntryPrompt(payload, locations = {}) {
|
|
9233
9236
|
if (payload.hook_event_name !== "UserPromptSubmit")
|
|
9234
9237
|
return false;
|
|
9235
9238
|
const sessionId = sessionIdOf(payload);
|
|
9236
9239
|
if (!sessionId)
|
|
9237
9240
|
return false;
|
|
9238
|
-
const root = locations.root ?? tmpdir2();
|
|
9239
9241
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
9240
9242
|
const entry = recognizePhaseEntry(prompt, claudeAliasReader(cwdOf(payload), locations.home ?? homedir()));
|
|
9241
|
-
|
|
9242
|
-
const recorded = (locations.append ?? appendPrivate)(path, cacheDir(root), phaseEntryLogLine(entry));
|
|
9243
|
-
if (!recorded)
|
|
9244
|
-
dropPrivate(path);
|
|
9245
|
-
return recorded;
|
|
9243
|
+
return appendPhaseEntry(sessionId, entry, locations.root ?? tmpdir2(), locations);
|
|
9246
9244
|
}
|
|
9247
9245
|
function gatedOperation(payload) {
|
|
9248
9246
|
const toolName = String(payload.tool_name ?? "");
|
|
@@ -9261,15 +9259,21 @@ function phaseEntryGateHookOutput(payload, locations = {}) {
|
|
|
9261
9259
|
if (!operation)
|
|
9262
9260
|
return null;
|
|
9263
9261
|
const root = locations.root ?? tmpdir2();
|
|
9262
|
+
if (operation.kind === "skill" && operation.skill !== "imm-run") {
|
|
9263
|
+
let activeClaim = false;
|
|
9264
|
+
try {
|
|
9265
|
+
activeClaim = readActiveClaimTaskId(cwdOf(payload)) !== null;
|
|
9266
|
+
} catch {}
|
|
9267
|
+
const sessionId = sessionIdOf(payload);
|
|
9268
|
+
if (!activeClaim && sessionId)
|
|
9269
|
+
appendPhaseEntry(sessionId, "plan", root, locations);
|
|
9270
|
+
return null;
|
|
9271
|
+
}
|
|
9264
9272
|
let state = null;
|
|
9265
9273
|
try {
|
|
9266
9274
|
state = readPhaseEntryState(sessionIdOf(payload), root, locations.read);
|
|
9267
9275
|
} catch {}
|
|
9268
|
-
|
|
9269
|
-
try {
|
|
9270
|
-
activeClaim = readActiveClaimTaskId(cwdOf(payload)) !== null;
|
|
9271
|
-
} catch {}
|
|
9272
|
-
const admission = admitPhaseOperation(operation, state, { activeClaim });
|
|
9276
|
+
const admission = admitPhaseOperation(operation, state);
|
|
9273
9277
|
if (!admission.ok)
|
|
9274
9278
|
return denyPreToolUse(phaseRefusalMessage(admission));
|
|
9275
9279
|
if (operation.kind === "skill")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: imm-planner
|
|
3
|
-
description: Use when the user
|
|
3
|
+
description: Use when the user asks for an Immune-Brain Spec, Plan, or TaskIntent for a repository change.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Immune-Brain: Planner
|
|
@@ -9,10 +9,13 @@ This skill adheres to the **[BASELINE.md](BASELINE.md)**.
|
|
|
9
9
|
|
|
10
10
|
## Managed Request Routing
|
|
11
11
|
|
|
12
|
-
`imm-planner`
|
|
13
|
-
|
|
12
|
+
`imm-planner` starts on the user's request for a Spec, Plan, or TaskIntent for a
|
|
13
|
+
repository change, by slash command or in natural language. A request that asks
|
|
14
|
+
for none of these stays host-native, and `imm-run` starts only from a
|
|
15
|
+
literal-user entry. A model-entered Planner outside an enrolled task ends the
|
|
16
|
+
session's run entry, so Enrollment needs a fresh `imm-run` entry.
|
|
14
17
|
An active Assurance projection remains authoritative and is resumed only through
|
|
15
|
-
an explicit `imm-run` entry;
|
|
18
|
+
an explicit `imm-run` entry; Planner entry owns planning only.
|
|
16
19
|
|
|
17
20
|
Planner output remains non-authoritative. Planner creates or validates a
|
|
18
21
|
candidate Spec/TaskIntent and never invokes the native Enrollment gate;
|
|
@@ -429,7 +432,7 @@ it, and structural Enrollment readiness is not proof that a descriptor executed.
|
|
|
429
432
|
- **Planning granularity**: Keep a coherent outcome together when acceptance, risk, rollback, and authority can settle together. Use the TaskIntent decomposition rules below for independent outcomes. File count, tokens, compactions, elapsed time, and review rounds are evidence for judgment, not universal gates.
|
|
430
433
|
- **Historical artifacts**: v3 prose Plan mutation is retired. `imm-plan` is a read-only validator for archived Plans; create no new Roadmap, Phase, successor Plan, or State Ledger.
|
|
431
434
|
- **Risk-Triggered Exploration**: Before authoring a TaskIntent, resolve only unknowns that could change scope, design, or verification using targeted read-only probes. Internal `arch-explorer` and explicit-lens `advisory-reviewer` roles return evidence without writing the Spec, TaskIntent, or workflow state. Stop probing when outcome, scope, and verification are concrete. Do not use retired Plan termination flags to classify discovery failures.
|
|
432
|
-
- **Simplicity**: Apply the BASELINE Workflow Activation gate first. Non-mutating host-native work creates no Planner artifact.
|
|
435
|
+
- **Simplicity**: Apply the BASELINE Workflow Activation gate first. Non-mutating host-native work creates no Planner artifact. The user's request for a Spec, Plan, or TaskIntent, by slash command or in natural language, starts this Planner phase; other host input stays host-native. Create one coherent outcome instead of expanding ceremony.
|
|
433
436
|
- **Design-Depth Classification**: Classify change design risk with the smallest sufficient tier: **Low risk** (copy, configuration, trivial rename, or contained local fix) may omit a separate Technical Design; **Medium risk** (non-trivial single-module behavior or internal contract) records affected components, decisions, invariants, failure behavior, and verification implications; **High risk** (cross-module/API/data-flow/state-machine, security, migration, concurrency, architecture ownership, cross-runtime/package-contract, or persisted-state work) records boundaries, interfaces or flow, alternatives, invariants, rollback/compatibility, and verification implications. Medium and High risk require Technical Design in the Spec. Do not classify a change as Low risk when it has a contract, ownership, security, persistence, compatibility, or multi-component concern. Every new or revised Spec records `**Design risk**: Low|Medium|High` with an adjacent rationale.
|
|
434
437
|
- **Design-view selection**: For Medium and High risk, select every materially relevant technical-design view from architecture layers, service/component interfaces, data flow, state transitions, and temporal sequence. Record a short `Design views` statement naming the selected views and why any omitted view cannot affect the design. Do not write empty architecture, interface, data-flow, state, or sequence sections. Low risk remains concise and may omit Technical Design. When a selected view is recorded, also record its required decision content: architecture layers need layer responsibilities, dependency direction, ownership, and prohibited coupling; service/component interfaces need inputs, outputs, errors, compatibility/versioning, and caller/callee ownership; data flow needs source, transformations, validation, destination, and failure handling; state transitions need states, legal transitions, trigger, invariant, terminal ownership, and recovery; temporal sequence needs ordered interactions, authority at each point, interruption behavior, and idempotency.
|
|
435
438
|
- **Technical Design Authority**: The Spec is the single Technical Design baseline. TaskIntent acceptance and scope reference the applicable design decisions or invariants without copying Technical Design prose. If discovery invalidates the baseline, stop execution and return to Planner to update the Spec and decide whether `replan` is required. TaskIntent and Initiative text do not duplicate Technical Design prose or become a prose Plan substitute.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Observe Claude Code Review and session lifecycle for Immune-Brain, keep a batch Lane's file edits inside its Lane, require a literal-user phase entry before Enrollment, batch start or
|
|
2
|
+
"description": "Observe Claude Code Review and session lifecycle for Immune-Brain, keep a batch Lane's file edits inside its Lane, require a literal-user phase entry before Enrollment, batch start or the imm-run Skill, and announce an active Managed task without resuming it. Hooks never mint authority.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"PreToolUse": [
|
|
5
5
|
{
|
|
@@ -63,23 +63,16 @@ export function recognizePhaseEntry(text: string, readAlias: AliasReader): Phase
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
export interface PhaseEntryState {
|
|
66
|
-
/** Count of literal prompts observed in the session. */
|
|
67
|
-
prompt_seq: number;
|
|
68
66
|
run_live: boolean;
|
|
69
|
-
/** The prompt that recorded the live run entry, or 0. */
|
|
70
|
-
run_prompt_seq: number;
|
|
71
|
-
/** The prompt that recorded the latest plan entry, or 0. */
|
|
72
|
-
plan_prompt_seq: number;
|
|
73
67
|
}
|
|
74
68
|
|
|
75
|
-
export const NO_PHASE_ENTRY: PhaseEntryState = {
|
|
69
|
+
export const NO_PHASE_ENTRY: PhaseEntryState = { run_live: false };
|
|
76
70
|
|
|
77
71
|
/** One literal prompt: a run entry goes live, a plan entry revokes it. */
|
|
78
72
|
export function applyPrompt(state: PhaseEntryState, entry: PhaseEntry | null): PhaseEntryState {
|
|
79
|
-
|
|
80
|
-
if (entry === "
|
|
81
|
-
|
|
82
|
-
return { ...state, prompt_seq };
|
|
73
|
+
if (entry === "run") return { run_live: true };
|
|
74
|
+
if (entry === "plan") return { run_live: false };
|
|
75
|
+
return state;
|
|
83
76
|
}
|
|
84
77
|
|
|
85
78
|
export function phaseEntryLogLine(entry: PhaseEntry | null): string {
|
|
@@ -115,26 +108,23 @@ function refuse(reason_code: typeof RUN_ENTRY_REQUIRED | typeof PHASE_ENTRY_REQU
|
|
|
115
108
|
|
|
116
109
|
/**
|
|
117
110
|
* Whether the session's entry state admits one operation. `state` is null when
|
|
118
|
-
* the store could not be read, which refuses
|
|
111
|
+
* the store could not be read, which refuses `enroll`, `start_unattended_batch`
|
|
112
|
+
* and the `imm-run` Skill. The Planner and Brainstorm Skills are never refused.
|
|
113
|
+
* `context` is accepted for Host callers; admission no longer depends on it.
|
|
119
114
|
*/
|
|
120
115
|
export function admitPhaseOperation(
|
|
121
116
|
operation: PhaseOperation,
|
|
122
117
|
state: PhaseEntryState | null,
|
|
123
|
-
context
|
|
118
|
+
context?: { activeClaim: boolean },
|
|
124
119
|
): PhaseAdmission {
|
|
120
|
+
void context;
|
|
125
121
|
if (operation.kind === "tool") {
|
|
126
122
|
if (state?.run_live) return { ok: true };
|
|
127
123
|
return refuse(RUN_ENTRY_REQUIRED, `${operation.name} needs a literal-user imm-run entry in this session`);
|
|
128
124
|
}
|
|
129
|
-
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
return refuse(PHASE_ENTRY_REQUIRED, "imm-run is entered by the user, not by a model Skill call");
|
|
133
|
-
}
|
|
134
|
-
if (state && state.plan_prompt_seq === latest) return { ok: true };
|
|
135
|
-
// Enrolled Intent Revision: the Loop routes to the Planner inside a run.
|
|
136
|
-
if (operation.skill === "imm-planner" && state?.run_live && context.activeClaim) return { ok: true };
|
|
137
|
-
return refuse(PHASE_ENTRY_REQUIRED, `${operation.skill} is entered by the user, not by a model Skill call`);
|
|
125
|
+
if (operation.skill !== "imm-run") return { ok: true };
|
|
126
|
+
if (state?.run_live) return { ok: true };
|
|
127
|
+
return refuse(PHASE_ENTRY_REQUIRED, "imm-run is entered by the user, not by a model Skill call");
|
|
138
128
|
}
|
|
139
129
|
|
|
140
130
|
/** The single-line message both Hosts return for a refusal. */
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Phase entry gate: UserPromptSubmit records which literal prompt entered
|
|
8
8
|
// planning or execution; PreToolUse denies `enroll`, `start_unattended_batch`
|
|
9
|
-
// and the
|
|
10
|
-
// admission the tool handler consumes by tool-use id.
|
|
9
|
+
// and the `imm-run` Skill without a live run entry, and otherwise leaves an
|
|
10
|
+
// admission the tool handler consumes by tool-use id. A model Skill call for
|
|
11
|
+
// `imm-planner` or `imm-brainstorm` is never denied; with no active claim it
|
|
12
|
+
// revokes the run entry.
|
|
11
13
|
import { lstatSync, rmSync } from "node:fs";
|
|
12
14
|
import { homedir, tmpdir } from "node:os";
|
|
13
15
|
import { join } from "node:path";
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
reducePhaseEntryLog,
|
|
26
28
|
RUN_ENTRY_REQUIRED,
|
|
27
29
|
type AliasReader,
|
|
30
|
+
type PhaseEntry,
|
|
28
31
|
type PhaseEntryState,
|
|
29
32
|
type PhaseOperation,
|
|
30
33
|
} from "../assurance/phase_entry";
|
|
@@ -126,22 +129,29 @@ function dropPrivate(path: string): void {
|
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
|
|
132
|
+
* Appends one entry line to the session log. A line that could not be written
|
|
133
|
+
* drops the log: an earlier entry must not outlive a line the store never saw,
|
|
134
|
+
* so the session is back to no entry.
|
|
135
|
+
*/
|
|
136
|
+
function appendPhaseEntry(sessionId: string, entry: PhaseEntry | null, root: string, locations: PhaseEntryLocations): boolean {
|
|
137
|
+
const path = entryLogPath(sessionId, root);
|
|
138
|
+
let recorded = false;
|
|
139
|
+
try { recorded = (locations.append ?? appendPrivate)(path, cacheDir(root), phaseEntryLogLine(entry)); } catch { /* an unwritten line drops the log */ }
|
|
140
|
+
if (!recorded) dropPrivate(path);
|
|
141
|
+
return recorded;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* UserPromptSubmit: record whether this literal prompt entered a phase. A
|
|
146
|
+
* prompt that could not be recorded drops the log.
|
|
133
147
|
*/
|
|
134
148
|
export function recordPhaseEntryPrompt(payload: Record<string, unknown>, locations: PhaseEntryLocations = {}): boolean {
|
|
135
149
|
if (payload.hook_event_name !== "UserPromptSubmit") return false;
|
|
136
150
|
const sessionId = sessionIdOf(payload);
|
|
137
151
|
if (!sessionId) return false;
|
|
138
|
-
const root = locations.root ?? tmpdir();
|
|
139
152
|
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
140
153
|
const entry = recognizePhaseEntry(prompt, claudeAliasReader(cwdOf(payload), locations.home ?? homedir()));
|
|
141
|
-
|
|
142
|
-
const recorded = (locations.append ?? appendPrivate)(path, cacheDir(root), phaseEntryLogLine(entry));
|
|
143
|
-
if (!recorded) dropPrivate(path);
|
|
144
|
-
return recorded;
|
|
154
|
+
return appendPhaseEntry(sessionId, entry, locations.root ?? tmpdir(), locations);
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
function gatedOperation(payload: Record<string, unknown>): PhaseOperation | null {
|
|
@@ -156,20 +166,27 @@ function gatedOperation(payload: Record<string, unknown>): PhaseOperation | null
|
|
|
156
166
|
}
|
|
157
167
|
|
|
158
168
|
/**
|
|
159
|
-
* PreToolUse for a gated tool: deny without a
|
|
160
|
-
* tools, otherwise write the admission the handler consumes.
|
|
161
|
-
*
|
|
169
|
+
* PreToolUse for a gated tool: deny without a live run entry; for the two MCP
|
|
170
|
+
* tools, otherwise write the admission the handler consumes. A Planner or
|
|
171
|
+
* Brainstorm Skill call is never denied; with no active claim it records a plan
|
|
172
|
+
* entry first, which revokes the run entry. Any other tool answers null and
|
|
173
|
+
* runs as configured.
|
|
162
174
|
*/
|
|
163
175
|
export function phaseEntryGateHookOutput(payload: Record<string, unknown>, locations: PhaseEntryLocations = {}): string | null {
|
|
164
176
|
if (payload.hook_event_name !== "PreToolUse") return null;
|
|
165
177
|
const operation = gatedOperation(payload);
|
|
166
178
|
if (!operation) return null;
|
|
167
179
|
const root = locations.root ?? tmpdir();
|
|
180
|
+
if (operation.kind === "skill" && operation.skill !== "imm-run") {
|
|
181
|
+
let activeClaim = false;
|
|
182
|
+
try { activeClaim = readActiveClaimTaskId(cwdOf(payload)) !== null; } catch { /* unreadable authority is no claim */ }
|
|
183
|
+
const sessionId = sessionIdOf(payload);
|
|
184
|
+
if (!activeClaim && sessionId) appendPhaseEntry(sessionId, "plan", root, locations);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
168
187
|
let state: PhaseEntryState | null = null;
|
|
169
188
|
try { state = readPhaseEntryState(sessionIdOf(payload), root, locations.read); } catch { /* an unreadable store refuses */ }
|
|
170
|
-
|
|
171
|
-
try { activeClaim = readActiveClaimTaskId(cwdOf(payload)) !== null; } catch { /* unreadable authority is no claim */ }
|
|
172
|
-
const admission = admitPhaseOperation(operation, state, { activeClaim });
|
|
189
|
+
const admission = admitPhaseOperation(operation, state);
|
|
173
190
|
if (!admission.ok) return denyPreToolUse(phaseRefusalMessage(admission));
|
|
174
191
|
if (operation.kind === "skill") return null;
|
|
175
192
|
const toolUseId = typeof payload.tool_use_id === "string" ? payload.tool_use_id : "";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/plugin_versioning.ts from the root package.json.
|
|
2
|
-
export const PLUGIN_VERSION = "5.
|
|
2
|
+
export const PLUGIN_VERSION = "5.3.0" as const;
|
|
@@ -55,7 +55,11 @@ framing does not load that execution guidance.
|
|
|
55
55
|
|
|
56
56
|
Ordinary host input stays host-native and does not run natural-language Managed
|
|
57
57
|
routing. A new Managed workflow starts only from explicit `imm-brainstorm`,
|
|
58
|
-
`imm-planner`, or `imm-run` Skill entry.
|
|
58
|
+
`imm-planner`, or `imm-run` Skill entry. For `imm-planner` and `imm-brainstorm`
|
|
59
|
+
the user's request for that planning or clarification, by slash command or in
|
|
60
|
+
natural language, is such an entry; a request that asks for neither stays
|
|
61
|
+
host-native. `imm-run` starts only from a literal-user entry. A model-entered
|
|
62
|
+
Planner outside an enrolled task ends the session's run entry.
|
|
59
63
|
|
|
60
64
|
1. **Continue an existing Managed owner explicitly**: an active Assurance
|
|
61
65
|
projection, TaskIntent, TaskRecord, reviewer `follow_up`, or other nonterminal
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: imm-brainstorm
|
|
3
|
-
description: Use when the user
|
|
3
|
+
description: Use when the user asks to clarify or explore requirements before Immune-Brain planning.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Immune-Brain: Brainstorm
|
|
7
7
|
|
|
8
8
|
Use [`../../dist/imm-brainstorm.md`](../../dist/imm-brainstorm.md) as the
|
|
9
|
-
canonical contract index, not a whole-document read.
|
|
10
|
-
|
|
9
|
+
canonical contract index, not a whole-document read. Starts on the user's request
|
|
10
|
+
to clarify or explore requirements, by slash command or in natural language;
|
|
11
|
+
ordinary host questions do not start this workflow.
|
|
11
12
|
|
|
12
13
|
Mandatory constraints before any action: Brainstorm is read-only — no code,
|
|
13
14
|
test, or runtime edits; no Spec, Plan, or workflow-state writes. All modes
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: imm-planner
|
|
3
|
-
description: Use when the user
|
|
3
|
+
description: Use when the user asks for an Immune-Brain Spec, Plan, or TaskIntent for a repository change.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Immune-Brain: Planner
|
|
7
7
|
|
|
8
8
|
Use [`../../dist/imm-planner.md`](../../dist/imm-planner.md) as the canonical contract
|
|
9
|
-
index, not a whole-document read.
|
|
9
|
+
index, not a whole-document read. Starts on the user's request for a Spec, Plan
|
|
10
|
+
or TaskIntent, by slash command or in natural language; other host requests stay
|
|
10
11
|
host-native.
|
|
11
12
|
|
|
12
13
|
Mandatory constraints before any action: Planner writes a TaskIntent, and a Spec
|