immune-brain 5.1.2 → 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/README.md +13 -6
- package/README.zh-CN.md +12 -5
- package/package.json +1 -1
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/.pi-extension/imm-canary-enroll.ts +14 -2
- package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +73 -0
- package/plugins/immune-brain/.pi-extension/imm-unattended-batch.ts +15 -2
- package/plugins/immune-brain/agents/immune-brain-reviewer.md +5 -0
- package/plugins/immune-brain/dist/BASELINE.md +6 -2
- package/plugins/immune-brain/dist/claude/mcp-server.mjs +429 -140
- package/plugins/immune-brain/dist/imm-brainstorm.md +3 -3
- package/plugins/immune-brain/dist/imm-doc-prune.md +2 -1
- package/plugins/immune-brain/dist/imm-planner.md +33 -37
- package/plugins/immune-brain/dist/imm-run.md +14 -3
- package/plugins/immune-brain/dist/imm-test-prune.md +233 -0
- package/plugins/immune-brain/dist/registry.yaml +9 -0
- package/plugins/immune-brain/dist/role-prompts/code-review.md +5 -0
- package/plugins/immune-brain/dist/role-prompts/executor.md +8 -0
- package/plugins/immune-brain/hooks/hooks.json +10 -1
- package/plugins/immune-brain/runtime/assurance/alias_file.ts +24 -0
- package/plugins/immune-brain/runtime/assurance/phase_entry.ts +133 -0
- package/plugins/immune-brain/runtime/claude/interaction.ts +5 -1
- package/plugins/immune-brain/runtime/claude/lane_guard.ts +197 -3
- package/plugins/immune-brain/runtime/claude/mcp_server.ts +21 -3
- package/plugins/immune-brain/runtime/claude/review_host.ts +6 -6
- package/plugins/immune-brain/runtime/plugin_version.ts +1 -1
- package/plugins/immune-brain/runtime/prompts/code-review.md +5 -0
- package/plugins/immune-brain/runtime/prompts/executor.md +8 -0
- package/plugins/immune-brain/skills/BASELINE.md +6 -2
- package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +6 -4
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +8 -7
- package/plugins/immune-brain/skills/imm-run/SKILL.md +2 -1
- package/plugins/immune-brain/skills/imm-test-prune/SKILL.md +28 -0
- package/plugins/immune-brain/skills/registry.yaml +9 -0
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Pi and Claude Code are the supported hosts. Undeclared adapters remain unsupport
|
|
|
28
28
|
- [Recommended Workflows: Composing with mattpocock/skills](#recommended-workflows-composing-with-mattpocockskills)
|
|
29
29
|
- [Real-World Case Studies](#real-world-case-studies)
|
|
30
30
|
- [Core Philosophy: Turn "Judgment" into "Table Lookup"](#core-philosophy-turn-judgment-into-table-lookup-harnessing-multi-model-tiers-with-determinism)
|
|
31
|
-
- [The
|
|
31
|
+
- [The 8 Skills](#the-8-skills)
|
|
32
32
|
- [Lifecycle](#lifecycle)
|
|
33
33
|
- [Unattended Batch Runs](#unattended-batch-runs)
|
|
34
34
|
- [Configuration](#configuration)
|
|
@@ -54,7 +54,7 @@ Pi discovers Skills and extensions from `package.json` (or your global Pi config
|
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
No extra server config is needed. Installing the package via Pi makes all
|
|
57
|
+
No extra server config is needed. Installing the package via Pi makes all 8 Skills available automatically.
|
|
58
58
|
|
|
59
59
|
### In Claude Code
|
|
60
60
|
|
|
@@ -123,6 +123,7 @@ Immune-Brain provides two clean modes: **Host-native** for daily coding, and **M
|
|
|
123
123
|
| PR has review comments or failing CI | `/imm-pr-fix` on that PR | → Standalone repair: minimal scoped fix in place, no managed task created |
|
|
124
124
|
| Project docs out of date | `/imm-doc-prune` | → Read-only audit; deletes only user-approved stale docs from manifest |
|
|
125
125
|
| Agent instructions bloated | `/imm-doc-slim` | → Minimizes tracked `AGENTS.md` / `CLAUDE.md` to essential non-discoverable rules |
|
|
126
|
+
| Test suite bloated with redundant cases | `/imm-test-prune` | → Audits tests read-only, then merges or deletes only approved entries backed by retirement or coverage evidence |
|
|
126
127
|
| Which model's edits keep coming back for review | `/imm-retro` | → Ranks models by review load and reports project usage from session logs |
|
|
127
128
|
|
|
128
129
|
> **Core Principle: Skill-Explicit Entry**
|
|
@@ -412,7 +413,7 @@ Without explicit cost levers, multi-agent workflows compound expenses exponentia
|
|
|
412
413
|
|
|
413
414
|
---
|
|
414
415
|
|
|
415
|
-
## The
|
|
416
|
+
## The 8 Skills
|
|
416
417
|
|
|
417
418
|
| Skill | Type | When to use | What it does |
|
|
418
419
|
|---|---|---|---|
|
|
@@ -422,11 +423,12 @@ Without explicit cost levers, multi-agent workflows compound expenses exponentia
|
|
|
422
423
|
| `imm-pr-fix` | Standalone | CI failed / review comments on a PR | Repairs one PR in place, no managed authority |
|
|
423
424
|
| `imm-doc-prune` | Standalone | Stale current docs | Deletes only the hash-approved manifest entries |
|
|
424
425
|
| `imm-doc-slim` | Standalone | Bloated agent instructions | Minimizes tracked AGENTS/CLAUDE/GEMINI.md to necessary context |
|
|
426
|
+
| `imm-test-prune` | Standalone | Bloated or redundant tests | Merges, parameterizes, or deletes approved test entries without weakening assertions |
|
|
425
427
|
| `imm-retro` | Standalone | Compare models by review load | Ranks authors of reviewed code and reports project usage |
|
|
426
428
|
|
|
427
429
|
Internal roles (Executor, QA, Review, Compounder) are dispatched by `imm-run` — you never invoke them directly.
|
|
428
430
|
|
|
429
|
-
All
|
|
431
|
+
All 8 skills are invoked explicitly. For new features, start with `imm-brainstorm` (if requirements are uncertain) or `imm-planner` (if requirements are clear), then proceed to `imm-run` once enrolled.
|
|
430
432
|
|
|
431
433
|
### Managed Path entries (brainstorm → planner → loop)
|
|
432
434
|
|
|
@@ -474,6 +476,11 @@ The three repair/maintenance skills are host-native: they never create a managed
|
|
|
474
476
|
- **Trigger:** explicit request to minimize tracked `AGENTS.md` / `CLAUDE.md` / `GEMINI.md`.
|
|
475
477
|
- **What it does:** keeps only the necessary non-discoverable rules in agent instruction files, under the same read-only-audit + hash-bound-manifest-approval model as `imm-doc-prune`.
|
|
476
478
|
|
|
479
|
+
#### `imm-test-prune` — test suite pruning
|
|
480
|
+
|
|
481
|
+
- **Trigger:** explicit request to audit or prune redundant, bloated, or orphaned tests.
|
|
482
|
+
- **What it does:** inventories tests with the repository's own test and coverage commands, then merges, parameterizes, or deletes only entries you approved in an exact hash-bound manifest. Deletion needs retirement proof or coverage-backed subsumption; without coverage tooling those entries stay unverified. It never weakens an assertion or touches production source.
|
|
483
|
+
|
|
477
484
|
#### `imm-retro` — review load and project usage
|
|
478
485
|
|
|
479
486
|
- **Trigger:** explicit request for a cross-model review retro or project usage look-back.
|
|
@@ -652,7 +659,7 @@ See [`docs/reference/immune-brain-config.md`](docs/reference/immune-brain-config
|
|
|
652
659
|
package.json # Pi package manifest (skills + extensions)
|
|
653
660
|
plugins/immune-brain/
|
|
654
661
|
├── .pi-extension/ # Pi TUI + Kernel authority extension
|
|
655
|
-
├── skills/ #
|
|
662
|
+
├── skills/ # 8 public Skills (trigger shims)
|
|
656
663
|
├── dist/ # Built skill contracts & references
|
|
657
664
|
├── runtime/ # Bun + TypeScript runtime & Kernel
|
|
658
665
|
└── bin/ # CLI wrappers (→ runtime/v4_runtime.ts)
|
|
@@ -670,7 +677,7 @@ docs/specs/ # Living specs (updated in place)
|
|
|
670
677
|
|
|
671
678
|
## FAQ
|
|
672
679
|
|
|
673
|
-
**Do I need to learn all
|
|
680
|
+
**Do I need to learn all 8 skills?** No. Most of the time you only need `/imm-planner` (to plan and enroll a task) and `/imm-run` (to build and verify it). Use `imm-brainstorm` when requirements need clarifying first, and the maintenance skills (`imm-pr-fix`, etc.) only when specific repair needs arise. Ordinary chat and simple edits don't need any skills at all.
|
|
674
681
|
|
|
675
682
|
**What if I interrupt or close the session mid-task?** State is safely stored on disk (`.imm/` + TaskIntent). In Pi or Claude Code, simply re-enter `/imm-run` to resume — the Kernel projection is authoritative.
|
|
676
683
|
|
package/README.zh-CN.md
CHANGED
|
@@ -28,7 +28,7 @@ Pi 与 Claude Code 是支持的宿主。未声明的适配器仍不受支持。C
|
|
|
28
28
|
- [推荐工作流:与 mattpocock/skills 组合使用](#推荐工作流与-mattpocockskills-组合使用)
|
|
29
29
|
- [实战案例](#实战案例)
|
|
30
30
|
- [核心设计哲学:把“判断”变成“查表”](#核心设计哲学把判断变成查表用确定性工程驾驭多模型)
|
|
31
|
-
- [
|
|
31
|
+
- [8 个 Skills](#8-个-skills)
|
|
32
32
|
- [生命周期](#生命周期)
|
|
33
33
|
- [无人值守批次运行](#无人值守批次运行)
|
|
34
34
|
- [配置](#配置)
|
|
@@ -54,7 +54,7 @@ Pi 通过 `package.json`(或全局 Pi 配置)自动发现 Skills 与扩展
|
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
无需额外 server 配置,通过 Pi 安装本 package 后
|
|
57
|
+
无需额外 server 配置,通过 Pi 安装本 package 后 8 个 Skill 即自动可用。
|
|
58
58
|
|
|
59
59
|
### 在 Claude Code 中使用
|
|
60
60
|
|
|
@@ -123,6 +123,7 @@ Immune-Brain 提供两种清晰的工作模式:日常轻量编码走 **Host-na
|
|
|
123
123
|
| PR 被评论 / CI 挂了 | 对该 PR 使用 `/imm-pr-fix` | → 独立修复:在当前 PR 内针对性修复,不创建新 managed 任务 |
|
|
124
124
|
| 文档过时需要清理 | `/imm-doc-prune` | → 只读审计过时文档,仅删除经哈希审批的条目 |
|
|
125
125
|
| Agent 指令文件膨胀 | `/imm-doc-slim` | → 将 tracked `AGENTS.md` / `CLAUDE.md` 压到最小必要上下文 |
|
|
126
|
+
| 测试用例膨胀、重复 | `/imm-test-prune` | → 只读审计测试,仅合并或删除经审批、有退休或覆盖率证据的条目 |
|
|
126
127
|
| 想知道哪个模型的改动总被审查 | `/imm-retro` | → 按模型排名审查负载,并从 session logs 汇报项目使用量 |
|
|
127
128
|
|
|
128
129
|
> **核心原则:Skill 显式调用**
|
|
@@ -415,7 +416,7 @@ Immune-Brain 的核心假设是:**不要让弱模型做架构决策,把它
|
|
|
415
416
|
|
|
416
417
|
---
|
|
417
418
|
|
|
418
|
-
##
|
|
419
|
+
## 8 个 Skills
|
|
419
420
|
|
|
420
421
|
| Skill | 类型 | 何时使用 | 职责 |
|
|
421
422
|
|---|---|---|---|
|
|
@@ -425,11 +426,12 @@ Immune-Brain 的核心假设是:**不要让弱模型做架构决策,把它
|
|
|
425
426
|
| `imm-pr-fix` | 独立 | PR 需修复 | 原地修复单个 PR,不触及 managed authority |
|
|
426
427
|
| `imm-doc-prune` | 独立 | 清理过时文档 | 仅删除哈希绑定的 manifest 条目 |
|
|
427
428
|
| `imm-doc-slim` | 独立 | Agent instruction 膨胀 | 将 tracked AGENTS/CLAUDE/GEMINI.md 压到最小必要上下文 |
|
|
429
|
+
| `imm-test-prune` | 独立 | 测试膨胀或重复 | 合并、参数化或删除经审批的测试条目,不弱化断言 |
|
|
428
430
|
| `imm-retro` | 独立 | 比较模型的审查负载 | 排名被审查代码的作者并汇报项目使用量 |
|
|
429
431
|
|
|
430
432
|
Executor、QA、Review、Compounder 等为 `imm-run` 内部调度的角色,无需手动调用。
|
|
431
433
|
|
|
432
|
-
所有
|
|
434
|
+
所有 8 个 Skill 均显式调用。新需求开发时:若需求含糊先调 `imm-brainstorm`,目标清晰直接调 `imm-planner`,完成确认后调 `imm-run` 推进闭环。
|
|
433
435
|
|
|
434
436
|
### Managed Path 入口(brainstorm → planner → loop)
|
|
435
437
|
|
|
@@ -477,6 +479,11 @@ Executor、QA、Review、Compounder 等为 `imm-run` 内部调度的角色,无
|
|
|
477
479
|
- **触发方式:** 显式要求精简版本控制下的 `AGENTS.md` / `CLAUDE.md` / `GEMINI.md`。
|
|
478
480
|
- **职责:** 遵循与 `imm-doc-prune` 相同的「只读审计 + 哈希清单审批」模式,仅保留无法直接推导的必要规则。
|
|
479
481
|
|
|
482
|
+
#### `imm-test-prune` — 测试套件瘦身
|
|
483
|
+
|
|
484
|
+
- **触发方式:** 显式要求审计或清理冗余、膨胀或孤立的测试。
|
|
485
|
+
- **职责:** 使用仓库自身的测试与覆盖率命令盘点测试,按用户明确审批的哈希绑定 manifest 合并、参数化或删除条目。删除需要行为退休证明或覆盖率支撑的包含关系;项目没有覆盖率工具时这类条目保持 unverified。从不弱化断言,也不修改生产代码。
|
|
486
|
+
|
|
480
487
|
#### `imm-retro` — 审查负载与项目使用回顾
|
|
481
488
|
|
|
482
489
|
- **触发方式:** 显式要求跨模型审查复盘或项目使用量回顾。
|
|
@@ -660,7 +667,7 @@ Immune-Brain **没有独立配置文件**,偏好设置写在仓库根目录下
|
|
|
660
667
|
package.json # Pi package manifest(skills + extensions)
|
|
661
668
|
plugins/immune-brain/
|
|
662
669
|
├── .pi-extension/ # Pi TUI + Kernel 扩展
|
|
663
|
-
├── skills/ #
|
|
670
|
+
├── skills/ # 8 个公开 Skills(触发 shim)
|
|
664
671
|
├── dist/ # 构建后的 skill 契约与参考文档
|
|
665
672
|
├── runtime/ # Bun + TypeScript 运行时与 Kernel
|
|
666
673
|
└── bin/ # CLI wrappers(→ runtime/v4_runtime.ts)
|
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
enrollmentGitBaseNotice,
|
|
27
27
|
initializeEnrollmentGitBase,
|
|
28
28
|
} from "../runtime/assurance/enrollment_git_base";
|
|
29
|
-
import { readTaskIntentForRecord } from "./imm-canary-work";
|
|
29
|
+
import { piRunEntryRefusal, readTaskIntentForRecord } from "./imm-canary-work";
|
|
30
30
|
import {
|
|
31
31
|
presentTaskRail,
|
|
32
32
|
presentTaskRailResult,
|
|
@@ -609,7 +609,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
609
609
|
description: "Run one exact Kernel new-task enrollment synchronously in the foreground with host cancellation before authority commit.",
|
|
610
610
|
promptSnippet: "Kernel enrollment: invoke once in foreground and consume the direct terminal result.",
|
|
611
611
|
promptGuidelines: [
|
|
612
|
-
"Call from the Parent
|
|
612
|
+
"Call from the Parent only as the first step of an explicit imm-run entry, never from imm-planner or imm-brainstorm; execute once in the foreground and consume the terminal Tool result.",
|
|
613
613
|
"Do not run this Tool in background, poll for completion, or issue a cancel subcommand; host cancellation is the only pre-commit cancellation path.",
|
|
614
614
|
],
|
|
615
615
|
parameters: Type.Object(
|
|
@@ -647,6 +647,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
647
647
|
message: "imm_canary_enrollment is TUI-only",
|
|
648
648
|
next_action: "invoke the TUI launcher",
|
|
649
649
|
});
|
|
650
|
+
// The phase entry gate (ADR 0019), before any preparation or dialog.
|
|
651
|
+
const entryRefusal = piRunEntryRefusal("enroll", ctx);
|
|
652
|
+
if (entryRefusal)
|
|
653
|
+
throwToolFailure({
|
|
654
|
+
tool: "imm_canary_enrollment",
|
|
655
|
+
task_id: taskId,
|
|
656
|
+
operation: action,
|
|
657
|
+
state: "blocked",
|
|
658
|
+
code: entryRefusal.reason_code,
|
|
659
|
+
message: entryRefusal.detail,
|
|
660
|
+
next_action: entryRefusal.recovery_action,
|
|
661
|
+
});
|
|
650
662
|
const authorityRegistry = await registry();
|
|
651
663
|
const result = await coordinator.run(action, taskId, signal, (foregroundSignal, beginCommit) =>
|
|
652
664
|
executeForegroundEnrollment(
|
|
@@ -21,6 +21,7 @@ import type { ConfirmationReferenceSource } from "../runtime/assurance/host_port
|
|
|
21
21
|
import { execFileSync } from "node:child_process";
|
|
22
22
|
import { createHash, randomUUID } from "node:crypto";
|
|
23
23
|
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
24
|
+
import { homedir } from "node:os";
|
|
24
25
|
import { join, resolve } from "node:path";
|
|
25
26
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
26
27
|
import { Type } from "typebox";
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
writeNativeReviewEvidence,
|
|
36
37
|
} from "./pi-canary-review-bundle";
|
|
37
38
|
import type { InvocationToken } from "./pi-canary-invocations";
|
|
39
|
+
import { readAliasFile } from "../runtime/assurance/alias_file";
|
|
38
40
|
import { runDeterministicQa } from "../runtime/assurance/qa";
|
|
39
41
|
import {
|
|
40
42
|
captureStagedIntent,
|
|
@@ -132,6 +134,15 @@ import {
|
|
|
132
134
|
import { reviewReworkFindings } from "../runtime/assurance/coordinator";
|
|
133
135
|
import { laneWriteRefusal } from "../runtime/unattended/lane_workspace";
|
|
134
136
|
import { activeTaskNotice } from "../runtime/assurance/active_task_notice";
|
|
137
|
+
import {
|
|
138
|
+
admitPhaseOperation,
|
|
139
|
+
applyPrompt,
|
|
140
|
+
NO_PHASE_ENTRY,
|
|
141
|
+
recognizePhaseEntry,
|
|
142
|
+
type AliasReader,
|
|
143
|
+
type PhaseAdmission,
|
|
144
|
+
type PhaseEntryState,
|
|
145
|
+
} from "../runtime/assurance/phase_entry";
|
|
135
146
|
|
|
136
147
|
const LOOP_OWNERS = ["plan", "kernel", "brainstorm", "planner", "loop"] as const;
|
|
137
148
|
const LOOP_TARGETS = [
|
|
@@ -287,6 +298,67 @@ export async function advancePiTask(root: string, taskId: string): Promise<any>
|
|
|
287
298
|
return { state: "failed", reason: (result as { reason?: string }).reason ?? "advance failed", ...facts };
|
|
288
299
|
}
|
|
289
300
|
|
|
301
|
+
const GLOBAL_PI_PHASE_ENTRY_KEY = Symbol.for("immune_brain.pi_phase_entry");
|
|
302
|
+
|
|
303
|
+
type PhaseEntryContext = { cwd?: string; sessionManager?: { getSessionId?: () => unknown } } | undefined;
|
|
304
|
+
|
|
305
|
+
// Pi loads each extension file as its own module graph, so the entry state
|
|
306
|
+
// lives under a global key that every copy of this module shares. It is
|
|
307
|
+
// process memory: a resumed session starts without an entry and refuses.
|
|
308
|
+
function piPhaseEntries(): Map<string, PhaseEntryState> {
|
|
309
|
+
const holder = globalThis as unknown as Record<symbol, Map<string, PhaseEntryState> | undefined>;
|
|
310
|
+
return holder[GLOBAL_PI_PHASE_ENTRY_KEY] ??= new Map();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function piSessionId(ctx: PhaseEntryContext): string {
|
|
314
|
+
try {
|
|
315
|
+
const id = ctx?.sessionManager?.getSessionId?.();
|
|
316
|
+
return typeof id === "string" ? id : "";
|
|
317
|
+
} catch {
|
|
318
|
+
return "";
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Prompt templates in the order Pi resolves them: the user's, then the project's. */
|
|
323
|
+
function piAliasReader(cwd: string): AliasReader {
|
|
324
|
+
const agentDir = process.env.PI_CODING_AGENT_DIR || join(homedir(), ".pi", "agent");
|
|
325
|
+
return (name) => {
|
|
326
|
+
for (const dir of [join(agentDir, "prompts"), join(cwd, ".pi", "prompts")]) {
|
|
327
|
+
const body = readAliasFile(join(dir, `${name}.md`));
|
|
328
|
+
if (body !== null) return body;
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* One Host input: record whether this literal prompt entered a phase. Input an
|
|
336
|
+
* extension injected is never the user's and leaves the entry state untouched.
|
|
337
|
+
*/
|
|
338
|
+
export function recordPiPhaseInput(event: { text?: unknown; source?: unknown }, ctx: PhaseEntryContext): void {
|
|
339
|
+
if (event.source === "extension") return;
|
|
340
|
+
const sessionId = piSessionId(ctx);
|
|
341
|
+
if (!sessionId) return;
|
|
342
|
+
const entries = piPhaseEntries();
|
|
343
|
+
const text = typeof event.text === "string" ? event.text : "";
|
|
344
|
+
const entry = recognizePhaseEntry(text, piAliasReader(ctx?.cwd ?? process.cwd()));
|
|
345
|
+
entries.set(sessionId, applyPrompt(entries.get(sessionId) ?? NO_PHASE_ENTRY, entry));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* The phase entry gate for a Pi tool, checked before any preparation or
|
|
350
|
+
* authority dialog. A session without an identity refuses.
|
|
351
|
+
*/
|
|
352
|
+
export function piRunEntryRefusal(
|
|
353
|
+
operation: "enroll" | "start_unattended_batch",
|
|
354
|
+
ctx: PhaseEntryContext,
|
|
355
|
+
): Extract<PhaseAdmission, { ok: false }> | null {
|
|
356
|
+
const sessionId = piSessionId(ctx);
|
|
357
|
+
const state = sessionId ? piPhaseEntries().get(sessionId) ?? NO_PHASE_ENTRY : null;
|
|
358
|
+
const admission = admitPhaseOperation({ kind: "tool", name: operation }, state, { activeClaim: false });
|
|
359
|
+
return admission.ok ? null : admission;
|
|
360
|
+
}
|
|
361
|
+
|
|
290
362
|
export default function (
|
|
291
363
|
pi: ExtensionAPI,
|
|
292
364
|
dependencies: CanaryWorkExtensionDependencies = {},
|
|
@@ -342,6 +414,7 @@ export default function (
|
|
|
342
414
|
});
|
|
343
415
|
pi.on("input", async (event, ctx) => {
|
|
344
416
|
if (event.source === "extension") return { action: "continue" } as const;
|
|
417
|
+
recordPiPhaseInput(event, ctx);
|
|
345
418
|
railContext = ctx;
|
|
346
419
|
clearTerminalTaskRailOnInput(ctx);
|
|
347
420
|
await refreshTaskRail(ctx);
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
import { createBatchKernelPort } from "../runtime/unattended/batch_kernel_port";
|
|
21
21
|
import type { BatchRunnerGitPort } from "../runtime/unattended/batch_git";
|
|
22
22
|
import type { InitiativeObservationReader } from "../runtime/unattended/types";
|
|
23
|
-
import { advancePiTask } from "./imm-canary-work";
|
|
23
|
+
import { advancePiTask, piRunEntryRefusal } from "./imm-canary-work";
|
|
24
24
|
import { batchReason } from "../runtime/unattended/batch_reasons";
|
|
25
25
|
import { parseFinalVerification } from "../runtime/unattended/batch_final_verification";
|
|
26
26
|
import { parseLaneRevision, REVISION_DELEGATION_TEXT, type LaneRevisionRequest } from "../runtime/unattended/batch_delegation";
|
|
@@ -427,7 +427,7 @@ export default function (
|
|
|
427
427
|
description: "Start an unattended serial batch run for an Initiative after native confirmation.",
|
|
428
428
|
promptSnippet: "Start unattended batch run: invoke once in foreground after plan confirmation.",
|
|
429
429
|
promptGuidelines: [
|
|
430
|
-
"Call only after plan confirmation; execute once in the foreground and consume the direct terminal result.",
|
|
430
|
+
"Call only inside an explicit imm-run entry, after plan confirmation; execute once in the foreground and consume the direct terminal result.",
|
|
431
431
|
"Do not run this Tool in background, poll for completion, or issue a cancel subcommand.",
|
|
432
432
|
],
|
|
433
433
|
parameters: Type.Object({
|
|
@@ -469,6 +469,19 @@ export default function (
|
|
|
469
469
|
next_action: refusal.recovery_action,
|
|
470
470
|
});
|
|
471
471
|
}
|
|
472
|
+
// The phase entry gate (ADR 0019), before any preflight or dialog. The
|
|
473
|
+
// batch's own reuseOnly continuation never passes through this Tool.
|
|
474
|
+
const entryRefusal = piRunEntryRefusal("start_unattended_batch", ctx);
|
|
475
|
+
if (entryRefusal)
|
|
476
|
+
throwToolFailure({
|
|
477
|
+
tool: "imm_canary_enrollment",
|
|
478
|
+
task_id: initiativeSlug,
|
|
479
|
+
operation: "start_unattended_batch",
|
|
480
|
+
state: "blocked",
|
|
481
|
+
code: entryRefusal.reason_code,
|
|
482
|
+
message: entryRefusal.detail,
|
|
483
|
+
next_action: entryRefusal.recovery_action,
|
|
484
|
+
});
|
|
472
485
|
|
|
473
486
|
const maxParallel = parseMaxParallel(params.max_parallel);
|
|
474
487
|
const laneOffers = parseLaneOffers(params.lane_offers);
|
|
@@ -32,6 +32,11 @@ material task-local maintenance risks. Pure naming, length, complexity
|
|
|
32
32
|
thresholds, formatting, and design preference are not findings and must not
|
|
33
33
|
cause style-only rework.
|
|
34
34
|
|
|
35
|
+
Test-suite growth is advisory, never blocking: note a delivered test that
|
|
36
|
+
duplicates an existing assertion, asserts only private implementation, or
|
|
37
|
+
leaves a test protecting behavior the change retired. Weakened or missing
|
|
38
|
+
tests for an acceptance remain blocking under the rules above.
|
|
39
|
+
|
|
35
40
|
When a finding repeats an acceptance id or anchor that already produced rework in
|
|
36
41
|
this task, do not file it as a fresh single-trigger defect. Name the invariant
|
|
37
42
|
class it belongs to and state in `evidence.trigger` why the previously accepted
|
|
@@ -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
|
|
@@ -108,7 +112,7 @@ An existing grant for one of these never inherits another: a stage-only
|
|
|
108
112
|
instruction stays stage-only, a commit is not push or publication authority,
|
|
109
113
|
and a valid batch capability commits only its own scope-bounded child.
|
|
110
114
|
|
|
111
|
-
Routine Managed enrollment uses one current-Host native confirmation bound to the TaskIntent content hash after Planner validation.
|
|
115
|
+
Routine Managed enrollment is the first step of an explicit `imm-run` entry and uses one current-Host native confirmation bound to the TaskIntent content hash after Planner validation. Planner and Brainstorm stop with candidate artifacts and never invoke Enrollment, whether or not the request also asks for execution; explicit Plan-only requests stop the same way. `imm-run` opens the native gate directly without chat pre-confirmation. Enrollment validates intent, Git ownership, scope, workspace claim, and final authority preconditions without executing acceptance descriptors; deterministic QA executes them after implementation. Once the user has entered `imm-run`, the routine task proceeds from that single confirmation through enrollment, execution, and QA with no further confirmation. Do not request confirmation for local in-scope edits, local verification, ordinary Direct rework, scoped diff review, or completion reporting. Managed evidence, QA, Review, and completion authority remain governed by their Managed contracts; R2 does not weaken them. Managed native-authority failures fail closed with one stable reason and exactly one same-Host recovery action; never offer a Pi, Direct Path, cross-Host/worktree, unmanaged, or automatic-retry fallback.
|
|
112
116
|
|
|
113
117
|
For an unborn symbolic Git HEAD, the same Enrollment confirmation discloses an empty root commit. After approval, the Host creates that commit without staging project content or changing the index/worktree, then revalidates ownership and rehearses Enrollment against the new base. Git identity uses existing environment/config values with an explicit automation fallback, never config writes. Decline or pre-initialization cancellation leaves HEAD unborn; if later Enrollment fails or is cancelled, report the remaining commit. Invalid Git refs are errors, not initialization candidates.
|
|
114
118
|
|