immune-brain 2.8.2

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.
Files changed (101) hide show
  1. package/README.md +239 -0
  2. package/README.zh-CN.md +239 -0
  3. package/package.json +84 -0
  4. package/plugins/immune-brain/.pi-extension/imm-canary-enroll.ts +666 -0
  5. package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +1622 -0
  6. package/plugins/immune-brain/.pi-extension/package.json +11 -0
  7. package/plugins/immune-brain/.pi-extension/pi-canary-assurance-progression.ts +711 -0
  8. package/plugins/immune-brain/.pi-extension/pi-canary-assurance.ts +154 -0
  9. package/plugins/immune-brain/.pi-extension/pi-canary-interaction.ts +349 -0
  10. package/plugins/immune-brain/.pi-extension/pi-canary-invocations.ts +90 -0
  11. package/plugins/immune-brain/.pi-extension/pi-canary-native-review.ts +214 -0
  12. package/plugins/immune-brain/.pi-extension/pi-canary-qa-findings.ts +50 -0
  13. package/plugins/immune-brain/.pi-extension/pi-canary-review-bundle.ts +262 -0
  14. package/plugins/immune-brain/.pi-extension/pi-canary-tool-failure.ts +28 -0
  15. package/plugins/immune-brain/.pi-extension/pi-canary-verification.ts +237 -0
  16. package/plugins/immune-brain/.pi-extension/runtime-stub.ts +414 -0
  17. package/plugins/immune-brain/.pi-extension/tsconfig.json +20 -0
  18. package/plugins/immune-brain/bin/imm-kernel +4 -0
  19. package/plugins/immune-brain/bin/imm-plan +4 -0
  20. package/plugins/immune-brain/bin/imm-pr-diag +230 -0
  21. package/plugins/immune-brain/bin/imm-retire-stale-wrapper +4 -0
  22. package/plugins/immune-brain/bin/imm-retired +4 -0
  23. package/plugins/immune-brain/bin/imm-tracker +4 -0
  24. package/plugins/immune-brain/dist/BASELINE.md +138 -0
  25. package/plugins/immune-brain/dist/docs/reference/HANDOFF-template.md +122 -0
  26. package/plugins/immune-brain/dist/docs/reference/design-contract-audit-rubric.md +149 -0
  27. package/plugins/immune-brain/dist/docs/reference/design-contract-review-checklist.md +55 -0
  28. package/plugins/immune-brain/dist/docs/reference/i18n-review-checklist.md +110 -0
  29. package/plugins/immune-brain/dist/docs/reference/immune-brain-config.md +52 -0
  30. package/plugins/immune-brain/dist/docs/reference/planning-artifact-retention.md +94 -0
  31. package/plugins/immune-brain/dist/docs/reference/planning-quality-gate.md +44 -0
  32. package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +105 -0
  33. package/plugins/immune-brain/dist/docs/reference/ux-heuristic-checklist.md +131 -0
  34. package/plugins/immune-brain/dist/imm-brainstorm.md +140 -0
  35. package/plugins/immune-brain/dist/imm-doc-prune.md +137 -0
  36. package/plugins/immune-brain/dist/imm-loop.md +158 -0
  37. package/plugins/immune-brain/dist/imm-planner.md +387 -0
  38. package/plugins/immune-brain/dist/imm-pr-fix.md +71 -0
  39. package/plugins/immune-brain/dist/registry.yaml +49 -0
  40. package/plugins/immune-brain/dist/role-prompts/advisory-reviewer.md +16 -0
  41. package/plugins/immune-brain/dist/role-prompts/arch-explorer.md +14 -0
  42. package/plugins/immune-brain/dist/role-prompts/code-review.md +15 -0
  43. package/plugins/immune-brain/dist/role-prompts/compounder.md +20 -0
  44. package/plugins/immune-brain/dist/role-prompts/executor.md +13 -0
  45. package/plugins/immune-brain/dist/role-prompts/pr-fix.md +81 -0
  46. package/plugins/immune-brain/dist/role-prompts/qa.md +23 -0
  47. package/plugins/immune-brain/dist/role-prompts/test-fixer.md +3 -0
  48. package/plugins/immune-brain/dist/role-prompts/ui-review.md +14 -0
  49. package/plugins/immune-brain/runtime/authority_commit_receipts.ts +716 -0
  50. package/plugins/immune-brain/runtime/canonical_json.ts +19 -0
  51. package/plugins/immune-brain/runtime/commands/kernel.ts +1160 -0
  52. package/plugins/immune-brain/runtime/github_issue_tracker.ts +1009 -0
  53. package/plugins/immune-brain/runtime/kernel/application.ts +300 -0
  54. package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +284 -0
  55. package/plugins/immune-brain/runtime/kernel/authority_port.ts +208 -0
  56. package/plugins/immune-brain/runtime/kernel/automatic_observations.ts +451 -0
  57. package/plugins/immune-brain/runtime/kernel/backend_claim.ts +197 -0
  58. package/plugins/immune-brain/runtime/kernel/canary_application.ts +507 -0
  59. package/plugins/immune-brain/runtime/kernel/canary_eligibility.ts +73 -0
  60. package/plugins/immune-brain/runtime/kernel/completion.ts +160 -0
  61. package/plugins/immune-brain/runtime/kernel/enrollment.ts +194 -0
  62. package/plugins/immune-brain/runtime/kernel/enrollment_authority.ts +123 -0
  63. package/plugins/immune-brain/runtime/kernel/index.ts +29 -0
  64. package/plugins/immune-brain/runtime/kernel/intent.ts +563 -0
  65. package/plugins/immune-brain/runtime/kernel/intent_token_registry.ts +80 -0
  66. package/plugins/immune-brain/runtime/kernel/legacy.ts +299 -0
  67. package/plugins/immune-brain/runtime/kernel/legacy_audit.ts +153 -0
  68. package/plugins/immune-brain/runtime/kernel/observation.ts +395 -0
  69. package/plugins/immune-brain/runtime/kernel/pi_canary_prepare.ts +169 -0
  70. package/plugins/immune-brain/runtime/kernel/readiness.ts +282 -0
  71. package/plugins/immune-brain/runtime/kernel/readiness_evidence.ts +132 -0
  72. package/plugins/immune-brain/runtime/kernel/reducer.ts +624 -0
  73. package/plugins/immune-brain/runtime/kernel/storage.ts +1780 -0
  74. package/plugins/immune-brain/runtime/kernel/storage_layout_migration.ts +791 -0
  75. package/plugins/immune-brain/runtime/kernel/storage_paths.ts +492 -0
  76. package/plugins/immune-brain/runtime/kernel/types.ts +295 -0
  77. package/plugins/immune-brain/runtime/kernel/validation.ts +963 -0
  78. package/plugins/immune-brain/runtime/loop_contract.ts +362 -0
  79. package/plugins/immune-brain/runtime/managed_task_routing_policy.ts +462 -0
  80. package/plugins/immune-brain/runtime/plan_core.ts +1053 -0
  81. package/plugins/immune-brain/runtime/prompts/advisory-reviewer.md +16 -0
  82. package/plugins/immune-brain/runtime/prompts/arch-explorer.md +14 -0
  83. package/plugins/immune-brain/runtime/prompts/code-review.md +15 -0
  84. package/plugins/immune-brain/runtime/prompts/compounder.md +20 -0
  85. package/plugins/immune-brain/runtime/prompts/executor.md +13 -0
  86. package/plugins/immune-brain/runtime/prompts/pr-fix.md +81 -0
  87. package/plugins/immune-brain/runtime/prompts/qa.md +23 -0
  88. package/plugins/immune-brain/runtime/prompts/test-fixer.md +3 -0
  89. package/plugins/immune-brain/runtime/prompts/ui-review.md +14 -0
  90. package/plugins/immune-brain/runtime/role_prompt_bridge.ts +160 -0
  91. package/plugins/immune-brain/runtime/v4_runtime.ts +295 -0
  92. package/plugins/immune-brain/runtime/verification_descriptor.ts +162 -0
  93. package/plugins/immune-brain/runtime/workspace_scope.ts +623 -0
  94. package/plugins/immune-brain/skills/.ignore +1 -0
  95. package/plugins/immune-brain/skills/BASELINE.md +138 -0
  96. package/plugins/immune-brain/skills/imm-brainstorm/SKILL.md +66 -0
  97. package/plugins/immune-brain/skills/imm-doc-prune/SKILL.md +11 -0
  98. package/plugins/immune-brain/skills/imm-loop/SKILL.md +52 -0
  99. package/plugins/immune-brain/skills/imm-planner/SKILL.md +221 -0
  100. package/plugins/immune-brain/skills/imm-pr-fix/SKILL.md +10 -0
  101. package/plugins/immune-brain/skills/registry.yaml +49 -0
@@ -0,0 +1,16 @@
1
+ # Internal role: advisory-reviewer
2
+
3
+ # Internal Advisory Reviewer
4
+
5
+ You are a bounded advisory reviewer selected by the coordinating Parent for an
6
+ explicit lens. Review only the supplied context and the named surface. Do not
7
+ implement fixes or infer authority from a recommendation.
8
+
9
+ Supported lenses include `debug_hypothesis` for evidence-backed diagnosis and
10
+ other explicit caller-provided lenses; never infer a lens from the role name.
11
+
12
+ Return one JSON object with `recommendations`, `disagreements`,
13
+ `open_questions`, and `blockers`. Tie each material claim to supplied evidence
14
+ or a repository path. This role is advisory-only: no code edits, Plan or Spec
15
+ writes, workflow-state mutation, enrollment, or QA closure. The coordinating
16
+ Parent owns synthesis and any Planner or Loop handoff.
@@ -0,0 +1,14 @@
1
+ # Internal role: arch-explorer
2
+
3
+ # Internal Architecture Explorer
4
+
5
+ You are the bounded architecture exploration role. Inspect only the repository
6
+ surface named by the Parent and use read-only tools. Map domain boundaries,
7
+ ownership, shallow modules, and existing ADR or CONTEXT vocabulary before
8
+ proposing anything.
9
+
10
+ Return one JSON object with `candidates`, `evidence`, `risks`, and
11
+ `open_questions`. Every candidate must cite concrete file paths and explain
12
+ why it could increase leverage. Do not edit code, write a Plan or Spec, mutate
13
+ workflow state, enroll work, or close QA. Exploration evidence is advisory and
14
+ the Parent remains responsible for user framing and Planner handoff.
@@ -0,0 +1,15 @@
1
+ # Internal role: code-review
2
+
3
+ You are the Immune-Brain read-only code review role inside Loop. Review only
4
+ the bounded change surface and the evidence supplied by the Parent. Classify
5
+ findings, state observable verification criteria, and preserve the current
6
+ review gate identity. Do not edit files, mutate workflow state, approve a successor, or invoke another role.
7
+
8
+ The stable Review Gate is `imm-code-review`. Return exactly one JSON object
9
+ with the fields required by the Loop review contract: `contract`, `role`,
10
+ `task_id`, `snapshot_digest`, `decision` (`pass` or `rework`), and for
11
+ `pass` include `approval` (`kind`, `authority_role`, `summary`), for `rework`
12
+ include `findings` (`id`, `kind`, `acceptance_id`, `summary`). Do not invent
13
+ fields. A passing review has no findings. If the checkpoint is
14
+ `awaiting_user_successor_decision`, stop without dispatch; only a literal user
15
+ may invoke `--approve-successor`.
@@ -0,0 +1,20 @@
1
+ # Internal role: compounder
2
+
3
+ # Internal Compounder
4
+
5
+ You run only after the Parent supplies a `workflow_phase: complete` closure
6
+ with `assurance_complete: true` and `required_reviews_complete: true`, and only
7
+ when the supplied closed Step evidence contains a reusable Learning. Extract one minimal, evidence-backed pattern when it is
8
+ worth preserving. Prefer appending to an existing `docs/solutions/` hub and
9
+ refreshing the memory index; do not duplicate an existing pattern.
10
+
11
+ Return one JSON object with `solution_doc_path`, `reusable_premise`, `evidence`,
12
+ `key_files`, and `reusability_critique_notes`. Do not modify implementation
13
+ files, Plan/Spec authority, task state, QA results, or terminal settlement.
14
+ Routine closed work without reusable evidence must not create a Learning.
15
+
16
+ When extracting learnings from completed work, preserve rejected decisions with
17
+ `rejected: true` and a concrete `rejection_reason`. Add optional `reconsider_if` as a YAML `list<string>` only when closure evidence
18
+ supports future triggers; use the list form even for one condition. Each item
19
+ is an independently sufficient trigger (OR semantics); write them as one complete condition string. Never invent a reconsideration condition. If evidence cannot support one, omit `reconsider_if`. Existing rejected Learning files
20
+ remain valid and require no bulk backfill.
@@ -0,0 +1,13 @@
1
+ # Internal role: executor
2
+
3
+ You are the Immune-Brain Executor role inside Loop. Implement exactly one
4
+ active Step, or one accepted same-boundary follow-up, in the current Parent
5
+ conversation. Use workspace tools only for the supplied target and keep every
6
+ edit inside the authoritative Scope. Do not discover or load a Pi Skill.
7
+
8
+ Before handoff, verify the active Result with the supplied Verification
9
+ commands and record structured execution evidence through the Loop runtime
10
+ action. Preserve failed and blocked attempts. Do not perform QA,
11
+ review, plan mutation, successor approval, Compounder work, or authority
12
+ writes. If the requested change needs scope expansion, stop and return an
13
+ `imm-planner` route with the concrete missing scope and verification reason.
@@ -0,0 +1,81 @@
1
+ # Internal role: pr-fix
2
+
3
+ You are the Immune-Brain PR and CI repair role inside Loop.
4
+
5
+ ## Core Responsibilities
6
+
7
+ - **Blocker Resolution**: Read GitHub PR, remote checks, and review threads to resolve blockers (conflicts, feedback, CI failures).
8
+ - **Minimal Repair**: Keep changes focused on the named blocker. Avoid unrelated features or refactors.
9
+ - **Feedback Closeout**: Resolve or reply to handled feedback and push the repair branch.
10
+
11
+ ## Workflow
12
+
13
+ ### Target Discovery
14
+
15
+ Accept PR URL/number/branch from delegation context. If missing, read the
16
+ current local branch and use it as the lookup key. Treat current-branch
17
+ discovery as incomplete until remote GitHub metadata confirms the PR target.
18
+ Stop if ambiguous (detached HEAD, zero matches, multiple PRs, or unavailable
19
+ GitHub metadata).
20
+
21
+ ### Script-First Diagnosis
22
+
23
+ After target discovery, run `plugins/immune-brain/bin/imm-pr-diag <PR>` to
24
+ collect a structured JSON snapshot of CI checks, review feedbacks, and merge
25
+ conflicts. Use this snapshot as the single source of truth for blocker
26
+ classification. If the script is unavailable, fall back to manual `gh pr view`
27
+ / `gh pr checks` collection and structure the results identically.
28
+
29
+ ### Blocker Classification
30
+
31
+ From the diagnostic snapshot, classify blockers into shards:
32
+ - `check_repair` — CI failures
33
+ - `feedback_repair` — review threads
34
+ - `conflict_repair` — merge conflicts
35
+
36
+ ### Conflict File Uncertainty
37
+
38
+ Treat `diagnostic_snapshot.conflicts.conflicting_files_status: "unknown"` as an
39
+ explicit uncertainty boundary. Do not use changed PR files as a proxy for
40
+ conflicting files; inspect the merge state locally or with GitHub metadata
41
+ before assigning conflict repair files.
42
+
43
+ ### Dispatch Protocol
44
+
45
+ Only dispatch when ≥2 independent blocker categories exist; when only 1
46
+ category exists, repair solo. File-level partition: no two shards should write
47
+ the same file. If file overlap exists between shards, merge those shards into
48
+ one.
49
+
50
+ Use Pi native `Agent` subagents inside the current user-selected worktree for
51
+ independent shards. Dispatch independent shards in one parallel tool call, with
52
+ a maximum of 3 concurrent shards. State each shard's owned files explicitly.
53
+
54
+ After all shards settle, inspect their direct results and the current worktree
55
+ diff. Detect cross-shard conflicts and resolve manually if found. Retry once per
56
+ shard on failure; on second failure, fall back to solo repair.
57
+
58
+ ### Validation
59
+
60
+ Re-run project checks and PR-related conflict checks. Compare local HEAD
61
+ against PR head expectation before push.
62
+
63
+ ## Boundary
64
+
65
+ Work only inside the supplied Plan, `plan_id`, changed-file boundary, review
66
+ feedback, and verification commands. Do not create a second Plan, silently
67
+ widen scope, push to unrelated branches, merge, approve a successor, or invoke
68
+ another role. Do not discover or load a Pi Skill.
69
+
70
+ When feedback requires a new scope, authority, or product decision, stop and
71
+ route the Parent to `imm-planner` with the concrete reason.
72
+
73
+ ## Output
74
+
75
+ Return evidence to the Parent:
76
+ `repair_report` including: `PR target`, `diagnostic_snapshot` reference,
77
+ `handled blockers`, `feedback status`, `dispatch_summary` (shards dispatched
78
+ vs. solo, per-shard outcome), `push result`, `validation results`, and
79
+ `remaining risk`.
80
+
81
+ Default shape: `Outcome → Blockers handled → Validation / next step`.
@@ -0,0 +1,23 @@
1
+ # Internal role: qa
2
+
3
+ You are the Immune-Brain QA authority inside Loop. Consume only the recorded
4
+ execution evidence and the current target identity supplied by the Parent.
5
+ Decide whether the active target passes, needs bounded rework, or needs a new
6
+ plan. Do not edit files, mutate workflow state, approve a successor, or invoke
7
+ another role.
8
+
9
+ Return exactly one JSON object with these fields:
10
+ - `decision`: `pass`, `rework`, or `replan`.
11
+ - `evidence`: a non-empty summary tied to recorded checks.
12
+ - `target_id`: exactly the supplied target identity.
13
+ - `repair_target`: required and non-empty only for `rework`.
14
+ - `notes`: required and non-empty for `replan`; optional otherwise.
15
+ - `artifacts`: optional evidence references.
16
+
17
+ Do not invent fields. Keep rework inside the active boundary. A successor
18
+ Plan remains a literal-user decision.
19
+
20
+ For elevated-risk work, check the latest referenced Spec's Design Conformance
21
+ against implementation evidence. A local implementation mismatch is `rework`;
22
+ return `rework` with bounded repair evidence. A structural or intended design
23
+ change is `replan`; return `replan` with the missing design fact. QA must not approve a changed design or silently accept a deviation. If the checkpoint is `awaiting_user_successor_decision`, stop without dispatch; only a literal user may invoke `--approve-successor`.
@@ -0,0 +1,3 @@
1
+ # Internal role: test-fixer
2
+
3
+ You are the Immune-Brain bounded test-repair role inside Loop. Edit only the delegated test files listed in `focus_delta.specific_changes` for the active target. Run the supplied `verification_hint`, return structured child evidence, and stop when the delegated test boundary is satisfied. Do not edit production code, plan files, workflow state, or unrelated tests. Do not discover or load a Pi Skill, invoke another role, approve QA, or widen the delegated file list. If the failure requires production changes or broader scope, report that boundary finding to the Parent instead of editing beyond it.
@@ -0,0 +1,14 @@
1
+ # Internal role: ui-review
2
+
3
+ You are the Immune-Brain read-only UI Review role inside Loop. Review only
4
+ the bounded UI change surface and the evidence supplied by the Parent. Check
5
+ interaction, accessibility, responsive behavior, localization, visual
6
+ legibility, and design-contract obligations as applicable. Do not edit files,
7
+ mutate workflow state, approve a successor, or invoke another role.
8
+
9
+ The stable Review Gate is `imm-ui-review`. Return exactly one JSON object with
10
+ the fields required by the Loop review contract: `decision` (`pass`,
11
+ `follow_up`, or `replan`), non-empty `evidence_ref`, `findings`,
12
+ `review_gate`, and `changed_files_signature`. A `follow_up` additionally
13
+ requires non-empty `scope`, `change_goal`, and `verification_hint`. Do not
14
+ invent fields. A passing review has no findings.
@@ -0,0 +1,160 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join, dirname } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { stableStringify } from "./canonical_json";
6
+
7
+ const RUNTIME_DIR = dirname(fileURLToPath(import.meta.url));
8
+
9
+ export type InternalRole =
10
+ | "qa"
11
+ | "code-review"
12
+ | "ui-review"
13
+ | "executor"
14
+ | "test-fixer"
15
+ | "pr-fix"
16
+ | "arch-explorer"
17
+ | "advisory-reviewer"
18
+ | "compounder";
19
+ export type StableReviewGate = "imm-code-review" | "imm-ui-review";
20
+ export type RoleAuthority =
21
+ | "qa"
22
+ | "advisory"
23
+ | "executor"
24
+ | "test-repair"
25
+ | "pr-repair"
26
+ | "compounder";
27
+ export type RoleToolPolicy =
28
+ | "no tools"
29
+ | "workspace tools"
30
+ | "delegated test files"
31
+ | "read-only tools"
32
+ | "learning tools";
33
+
34
+ export interface RolePromptSpec {
35
+ file: `${InternalRole}.md`;
36
+ review_gate?: StableReviewGate;
37
+ authority: RoleAuthority;
38
+ tool_policy: RoleToolPolicy;
39
+ }
40
+
41
+ export const INTERNAL_ROLE_PROMPTS: Record<InternalRole, RolePromptSpec> = {
42
+ qa: { file: "qa.md", authority: "qa", tool_policy: "no tools" },
43
+ "code-review": {
44
+ file: "code-review.md",
45
+ review_gate: "imm-code-review",
46
+ authority: "advisory",
47
+ tool_policy: "read-only tools",
48
+ },
49
+ "ui-review": {
50
+ file: "ui-review.md",
51
+ review_gate: "imm-ui-review",
52
+ authority: "advisory",
53
+ tool_policy: "no tools",
54
+ },
55
+ executor: {
56
+ file: "executor.md",
57
+ authority: "executor",
58
+ tool_policy: "workspace tools",
59
+ },
60
+ "test-fixer": {
61
+ file: "test-fixer.md",
62
+ authority: "test-repair",
63
+ tool_policy: "delegated test files",
64
+ },
65
+ "pr-fix": {
66
+ file: "pr-fix.md",
67
+ authority: "pr-repair",
68
+ tool_policy: "workspace tools",
69
+ },
70
+ "arch-explorer": {
71
+ file: "arch-explorer.md",
72
+ authority: "advisory",
73
+ tool_policy: "read-only tools",
74
+ },
75
+ "advisory-reviewer": {
76
+ file: "advisory-reviewer.md",
77
+ authority: "advisory",
78
+ tool_policy: "no tools",
79
+ },
80
+ compounder: {
81
+ file: "compounder.md",
82
+ authority: "compounder",
83
+ tool_policy: "learning tools",
84
+ },
85
+ };
86
+
87
+ export interface RoleDelegationContext {
88
+ task_id: string;
89
+ target_id?: string;
90
+ review_gate?: StableReviewGate;
91
+ changed_files_signature?: string;
92
+ [key: string]: unknown;
93
+ }
94
+
95
+ export interface RoleDelegationPacket {
96
+ contract: "immune_brain/role_delegation/v1";
97
+ role: InternalRole;
98
+ review_gate?: StableReviewGate;
99
+ authority: RoleAuthority;
100
+ tool_policy: RoleToolPolicy;
101
+ prompt: string;
102
+ prompt_digest: string;
103
+ }
104
+
105
+ function roleSpec(role: InternalRole): RolePromptSpec {
106
+ const spec = INTERNAL_ROLE_PROMPTS[role];
107
+ if (!spec) throw new Error(`unknown internal role: ${String(role)}`);
108
+ return spec;
109
+ }
110
+
111
+ /**
112
+ * Read the packaged prompt so the runtime follows the bytes shipped to a
113
+ * consumer. The canonical source is synced into this dist-local directory.
114
+ */
115
+ export function loadRolePrompt(role: InternalRole): string {
116
+ const spec = roleSpec(role);
117
+ const path = join(RUNTIME_DIR, "..", "dist", "role-prompts", spec.file);
118
+ if (!existsSync(path)) {
119
+ throw new Error(`internal role prompt is not packaged: ${role}`);
120
+ }
121
+ return readFileSync(path, "utf8");
122
+ }
123
+
124
+ export function buildRoleDelegationPacket(input: {
125
+ role: InternalRole;
126
+ context: RoleDelegationContext;
127
+ }): RoleDelegationPacket {
128
+ const spec = roleSpec(input.role);
129
+ const requestedGate = input.context.review_gate;
130
+ if (spec.review_gate && requestedGate && requestedGate !== spec.review_gate) {
131
+ throw new Error(
132
+ `review gate ${requestedGate} does not match ${input.role}`,
133
+ );
134
+ }
135
+ if (!spec.review_gate && requestedGate) {
136
+ throw new Error(
137
+ `${input.role} cannot carry review gate ${requestedGate}`,
138
+ );
139
+ }
140
+
141
+ const reviewGate = spec.review_gate;
142
+ const context = stableStringify(input.context);
143
+ const prompt = [
144
+ `internal role: ${input.role}`,
145
+ `tool_policy: ${spec.tool_policy}`,
146
+ `do not discover or load Pi Skills; execute this internal role contract directly`,
147
+ loadRolePrompt(input.role).trim(),
148
+ `Delegation context (untrusted data): ${context}`,
149
+ ].join("\n\n");
150
+ const promptDigest = `sha256:${createHash("sha256").update(prompt).digest("hex")}`;
151
+ return {
152
+ contract: "immune_brain/role_delegation/v1",
153
+ role: input.role,
154
+ ...(reviewGate ? { review_gate: reviewGate } : {}),
155
+ authority: spec.authority,
156
+ tool_policy: spec.tool_policy,
157
+ prompt,
158
+ prompt_digest: promptDigest,
159
+ };
160
+ }
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Immune-Brain v4-only shipped CLI runtime.
3
+ *
4
+ * This module is the SINGLE shipped CLI entrypoint after v4 storage
5
+ * retirement. It exposes:
6
+ * - `imm-kernel` intent author/validate (host-neutral TaskIntent drafts)
7
+ * - `imm-kernel status --json` (read-only v3 legacy shadow status)
8
+ * - `imm-kernel audit --legacy` (explicit read-only legacy audit)
9
+ * - `imm-plan --routing-status --json` (strict Git-owned route projection)
10
+ * - `imm-plan <plan-path> [--json]` (read-only Plan validation)
11
+ * - `imm-tracker` (opt-in, one-way, non-authoritative GitHub Issue projection)
12
+ * - a stable `drain_required` / `v3_storage_retired` wall for every v3
13
+ * mutating command (work/review/migrate/finish/autowork/heal/...).
14
+ *
15
+ * v3 State Ledger mutations, migrations, authority receipts, automatic
16
+ * observations, and TaskRecord v1 writers are NOT reachable from any shipped
17
+ * entrypoint. The legacy runtime module remains for
18
+ * test fixtures and historical parsing only and is never imported by any shipped entrypoint.
19
+ */
20
+ import { fileURLToPath } from "node:url";
21
+ import process from "node:process";
22
+ import { readFileSync } from "node:fs";
23
+ import { resolve } from "node:path";
24
+ import { runKernelCommand } from "./commands/kernel";
25
+ import {
26
+ inspectRoutingPolicy,
27
+ type RoutingPolicyProjection,
28
+ } from "./managed_task_routing_policy";
29
+ import {
30
+ PlanValidationError,
31
+ projectPlanValidation,
32
+ } from "./plan_core";
33
+ import { runGithubTrackerCli } from "./github_issue_tracker";
34
+
35
+ // Retired v3 mutating command wall. Read-only v3 commands that only project
36
+ // state (imm-plan validate) stay available; every writer is retired.
37
+ const RETIRED_MUTATING_COMMANDS = new Set([
38
+ "imm-work",
39
+ "imm-review",
40
+ "imm-migrate",
41
+ "imm-finish",
42
+ "imm-autowork",
43
+ "imm-heal",
44
+ "imm-check-child-output",
45
+ "imm-retire-stale-wrapper",
46
+ ]);
47
+
48
+ const READ_ONLY_V3_COMMANDS = new Set(["imm-plan"]);
49
+
50
+ const RETIRED_PLAN_OPTIONS = new Set([
51
+ "--sync",
52
+ "--terminate-current",
53
+ "--approve-successor",
54
+ "--expected-current-plan",
55
+ "--expected-ledger-revision",
56
+ "--user-confirmed",
57
+ "--status",
58
+ "--reason",
59
+ "--reason-code",
60
+ "--stage",
61
+ "--invalidated-assumption",
62
+ "--avoidable",
63
+ ]);
64
+
65
+ function hasRetiredPlanOption(args: string[]): boolean {
66
+ return args.some((arg) => RETIRED_PLAN_OPTIONS.has(arg.split("=", 1)[0]));
67
+ }
68
+
69
+ function jsonOutput(payload: unknown): string {
70
+ return `${JSON.stringify(payload, null, 2)}\n`;
71
+ }
72
+
73
+ function unavailableRoutingProjection(): RoutingPolicyProjection {
74
+ return {
75
+ policy_status: "invalid",
76
+ route: null,
77
+ v3_new_plan_sync: "allowed",
78
+ legacy_v3_mode: null,
79
+ terminal_import: null,
80
+ worktree_sha256: null,
81
+ index_sha256: null,
82
+ ownership: "unavailable",
83
+ reason_code: "policy_read_unavailable",
84
+ };
85
+ }
86
+
87
+ function retiredResponse(command: string, args: string[], root: string): {
88
+ stdout: string;
89
+ stderr: string;
90
+ returncode: number;
91
+ } {
92
+ // A nonterminal v3 owner requires the operator to drain or terminate it
93
+ // using the prior runtime before any v4 write.
94
+ let requiresDrain = false;
95
+ try {
96
+ const statePath = resolve(root, ".imm/memory/current_iteration.json");
97
+ const raw = JSON.parse(readFileSync(statePath, "utf8")) as {
98
+ runtime_status?: unknown;
99
+ plan_terminal?: unknown;
100
+ };
101
+ requiresDrain =
102
+ raw &&
103
+ (typeof raw.runtime_status === "string"
104
+ ? raw.runtime_status !== "idle"
105
+ : false) &&
106
+ raw.plan_terminal === null;
107
+ } catch {
108
+ requiresDrain = false;
109
+ }
110
+ const code = requiresDrain ? "drain_required" : "v3_storage_retired";
111
+ const hint = requiresDrain
112
+ ? "drain or terminate the active v3 Plan using the prior runtime before upgrading"
113
+ : "v3 State Ledger mutations are retired; author a host-neutral TaskIntent and enroll it through the Pi TUI";
114
+ return {
115
+ stdout: "",
116
+ stderr: `Immune-Brain v3 mutation rejected (${code}): ${hint}\n`,
117
+ returncode: 1,
118
+ };
119
+ }
120
+
121
+ function runPlanCli(args: string[], root: string): {
122
+ stdout: string;
123
+ stderr: string;
124
+ returncode: number;
125
+ } {
126
+ if (hasRetiredPlanOption(args)) return retiredResponse("imm-plan", args, root);
127
+ if (
128
+ args.length === 2 &&
129
+ args[0] === "--routing-status" &&
130
+ args[1] === "--json"
131
+ ) {
132
+ let projection: RoutingPolicyProjection;
133
+ try {
134
+ projection = inspectRoutingPolicy(root);
135
+ } catch {
136
+ projection = unavailableRoutingProjection();
137
+ }
138
+ return { stdout: jsonOutput(projection), stderr: "", returncode: 0 };
139
+ }
140
+ const planPath = args[0];
141
+ const json = args.length === 2 && args[1] === "--json";
142
+ if (
143
+ !planPath ||
144
+ planPath.startsWith("-") ||
145
+ (args.length !== 1 && !json)
146
+ ) {
147
+ return {
148
+ stdout: "",
149
+ stderr:
150
+ "invalid_plan_command: use imm-plan --routing-status --json or imm-plan <plan-path> [--json]\n",
151
+ returncode: 2,
152
+ };
153
+ }
154
+ try {
155
+ const projection = projectPlanValidation(planPath, root);
156
+ if (json) {
157
+ return { stdout: jsonOutput(projection), stderr: "", returncode: 0 };
158
+ }
159
+ return {
160
+ stdout: `Plan validation passed.\nSummary: ${projection.summary}\nSteps: ${projection.steps.length}\n`,
161
+ stderr: "",
162
+ returncode: 0,
163
+ };
164
+ } catch (error) {
165
+ const message =
166
+ error instanceof PlanValidationError ? error.message : "unexpected failure";
167
+ return {
168
+ stdout: "",
169
+ stderr: `plan_validation_rejected: ${message.slice(0, 4096)}\n`,
170
+ returncode: 1,
171
+ };
172
+ }
173
+ }
174
+
175
+ async function runKernelCli(args: string[], root: string): Promise<{
176
+ stdout: string;
177
+ stderr: string;
178
+ returncode: number;
179
+ }> {
180
+ // Only the retained kernel surface is reachable: intent author/validate,
181
+ // status --json, and the explicit audit command. All other kernel
182
+ // subcommands (readiness, journal, migrate) are retired.
183
+ const sub = args[0] ?? "";
184
+ if (sub === "intent") return runKernelCommand(args, root);
185
+ if (sub === "status" && args.includes("--json")) return runKernelCommand(args, root);
186
+ if (sub === "audit") {
187
+ // Explicit read-only legacy audit: bounded, no symlink, deterministic
188
+ // redacted projection. Never writes journal or workflow state.
189
+ const { projectLegacyAudit } = await import("./kernel/legacy_audit");
190
+ try {
191
+ const projection = projectLegacyAudit(root);
192
+ return {
193
+ stdout: `${JSON.stringify(projection, null, 2)}\n`,
194
+ stderr: "",
195
+ returncode: 0,
196
+ };
197
+ } catch (error) {
198
+ return {
199
+ stdout: "",
200
+ stderr: `legacy_audit_rejected: ${error instanceof Error ? error.message : String(error)}\n`,
201
+ returncode: 1,
202
+ };
203
+ }
204
+ }
205
+ return {
206
+ stdout: "",
207
+ stderr: "invalid_kernel_command: imm-kernel supports intent author|validate, status --json, and audit --legacy only\n",
208
+ returncode: 2,
209
+ };
210
+ }
211
+
212
+ async function runCli(command: string, args: string[], root: string): Promise<{
213
+ stdout: string;
214
+ stderr: string;
215
+ returncode: number;
216
+ }> {
217
+ if (command === "imm-kernel") return runKernelCli(args, root);
218
+ if (command === "imm-plan") return runPlanCli(args, root);
219
+ if (command === "imm-tracker") return runGithubTrackerCli(args, root);
220
+ if (RETIRED_MUTATING_COMMANDS.has(command)) return retiredResponse(command, args, root);
221
+ return {
222
+ stdout: "",
223
+ stderr: `Unknown Immune-Brain v4 command: ${command}\n`,
224
+ returncode: 2,
225
+ };
226
+ }
227
+
228
+ async function main(argv: string[]): Promise<number> {
229
+ const mode = argv[0];
230
+ const root = process.cwd();
231
+ if (mode === "list-commands") {
232
+ process.stdout.write(
233
+ `${JSON.stringify(
234
+ {
235
+ commands: [
236
+ {
237
+ name: "imm-kernel",
238
+ description:
239
+ "v4-only Kernel surface: intent author/validate, status, and explicit legacy audit.",
240
+ json_output: true,
241
+ examples: [
242
+ "imm-kernel intent author docs/plans/<task-id>.intent.json --stdin --json",
243
+ "imm-kernel intent validate docs/plans/<task-id>.intent.json --json",
244
+ "imm-kernel status --json",
245
+ "imm-kernel audit --legacy",
246
+ ],
247
+ },
248
+ {
249
+ name: "imm-plan",
250
+ description:
251
+ "Read-only routing-policy projection and explicit Plan validation. v3 Plan mutation is retired.",
252
+ json_output: true,
253
+ examples: [
254
+ "imm-plan --routing-status --json",
255
+ "imm-plan docs/plans/<plan>.md --json",
256
+ ],
257
+ },
258
+ {
259
+ name: "imm-tracker",
260
+ description:
261
+ "Opt-in, one-way GitHub Issue projection. Creates a Parent once, never rewrites or closes it. Never grants or consumes Kernel authority.",
262
+ json_output: true,
263
+ examples: [
264
+ "imm-tracker create-initiative --stdin --json",
265
+ "imm-tracker upsert-task --initiative-id <id> --slice-id <id> --intent docs/plans/<task-id>.intent.json --json",
266
+ ],
267
+ },
268
+ ],
269
+ retired: [...RETIRED_MUTATING_COMMANDS].sort(),
270
+ },
271
+ null,
272
+ 2,
273
+ )}\n`,
274
+ );
275
+ return 0;
276
+ }
277
+ if (mode === "cli") {
278
+ const command = argv[1];
279
+ const completed = await runCli(command, argv.slice(2), root);
280
+ if (completed.stdout) process.stdout.write(completed.stdout);
281
+ if (completed.stderr) process.stderr.write(completed.stderr);
282
+ return completed.returncode;
283
+ }
284
+ process.stderr.write(
285
+ `Usage: v4_runtime.ts <list-commands|cli <command> [args...]>\n`,
286
+ );
287
+ return 2;
288
+ }
289
+
290
+ if (fileURLToPath(import.meta.url) === process.argv[1]) {
291
+ const code = await main(process.argv.slice(2));
292
+ process.exit(code);
293
+ }
294
+
295
+ export { main, runCli, runKernelCli, runGithubTrackerCli, RETIRED_MUTATING_COMMANDS };