immune-brain 3.2.1 → 3.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.
Files changed (26) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +5 -3
  4. package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
  5. package/plugins/immune-brain/.pi-extension/imm-canary-work.ts +19 -73
  6. package/plugins/immune-brain/.pi-extension/pi-canary-interaction.ts +1 -2
  7. package/plugins/immune-brain/.pi-extension/runtime-stub.ts +2 -2
  8. package/plugins/immune-brain/dist/claude/mcp-server.mjs +27 -85
  9. package/plugins/immune-brain/dist/docs/reference/subagent-dispatch-protocol.md +1 -1
  10. package/plugins/immune-brain/dist/imm-agent-doc-maintain.md +28 -13
  11. package/plugins/immune-brain/dist/imm-loop.md +25 -16
  12. package/plugins/immune-brain/dist/imm-planner.md +1 -1
  13. package/plugins/immune-brain/runtime/assurance/coordinator.ts +0 -8
  14. package/plugins/immune-brain/runtime/claude/interaction.ts +0 -1
  15. package/plugins/immune-brain/runtime/claude/kernel_ports.ts +5 -24
  16. package/plugins/immune-brain/runtime/claude/mcp_server.ts +12 -8
  17. package/plugins/immune-brain/runtime/kernel/application.ts +0 -1
  18. package/plugins/immune-brain/runtime/kernel/assurance_projection.ts +1 -7
  19. package/plugins/immune-brain/runtime/kernel/canary_application.ts +0 -7
  20. package/plugins/immune-brain/runtime/kernel/completion.ts +1 -3
  21. package/plugins/immune-brain/runtime/kernel/reducer.ts +0 -31
  22. package/plugins/immune-brain/runtime/kernel/types.ts +0 -2
  23. package/plugins/immune-brain/runtime/kernel/validation.ts +1 -3
  24. package/plugins/immune-brain/runtime/plugin_version.ts +2 -0
  25. package/plugins/immune-brain/skills/imm-loop/SKILL.md +3 -45
  26. package/plugins/immune-brain/skills/imm-planner/SKILL.md +3 -226
package/README.md CHANGED
@@ -14,7 +14,7 @@ Immune-Brain adds a structured engineering workflow on top of Pi:
14
14
  - **Plans become trackable tasks** (`TaskIntent` + `TaskRecord`) so progress survives across sessions, not just chat history.
15
15
  - **Quality is enforced by code, not promises** — automated QA and isolated review must pass before a task is marked done.
16
16
 
17
- Pi and Claude Code are the supported hosts. Undeclared adapters remain unsupported. Minimum Claude Code is `2.1.199`. Real Host evidence lives in [docs/verification/claude-code-host-conformance.md](docs/verification/claude-code-host-conformance.md). Either host can use the model provider you configure — Immune-Brain works on top of Kernel authority, not a vendor chat.
17
+ Pi and Claude Code are the supported hosts. Undeclared adapters remain unsupported. Minimum Claude Code is `2.1.199`. Historical real-Host evidence is archived under [docs/verification/archive/](docs/verification/archive/); deterministic package and authority checks gate current releases. Either host can use the model provider you configure — Immune-Brain works on top of Kernel authority, not a vendor chat.
18
18
 
19
19
  ---
20
20
 
package/README.zh-CN.md CHANGED
@@ -14,7 +14,7 @@ Immune-Brain 在 Pi 之上提供结构化的工程工作流:
14
14
  - **计划变为可追踪的任务**(`TaskIntent` + `TaskRecord`),进度落盘持久化,不依赖对话历史。
15
15
  - **质量由代码强制保障** — 自动化 QA 与隔离式 Review 必须通过,任务才会完成。
16
16
 
17
- Pi 与 Claude Code 是支持的宿主。未声明的适配器仍不受支持。Claude Code 最低版本为 `2.1.199`。真实 Host 证据见 `docs/verification/claude-code-host-conformance.md`。
17
+ Pi 与 Claude Code 是支持的宿主。未声明的适配器仍不受支持。Claude Code 最低版本为 `2.1.199`。历史真实 Host 证据归档于 `docs/verification/archive/`;当前发布由确定性的 package 与 authority 检查把关。
18
18
 
19
19
  ---
20
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "immune-brain",
3
- "version": "3.2.1",
3
+ "version": "3.3.0",
4
4
  "description": "Immune-Brain agent skill system",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -29,8 +29,9 @@
29
29
  },
30
30
  "scripts": {
31
31
  "changeset": "changeset",
32
- "changeset:version": "changeset version && bun scripts/plugin_versioning.ts stamp && bun scripts/plugin_versioning.ts validate",
33
- "changeset:publish": "bun scripts/plugin_versioning.ts validate && changeset publish",
32
+ "changeset:version": "changeset version && bun scripts/build-claude-plugin.ts && bun scripts/plugin_versioning.ts validate",
33
+ "verify:release": "bun scripts/plugin_versioning.ts validate && bun scripts/build-claude-plugin.ts --check && bun scripts/sync-dist-docs.ts --check && bun test && npm pack --dry-run --ignore-scripts",
34
+ "changeset:publish": "bun run verify:release && changeset publish",
34
35
  "release": "bun run changeset:publish"
35
36
  },
36
37
  "devDependencies": {
@@ -76,6 +77,7 @@
76
77
  "plugins/immune-brain/runtime/verification_descriptor.ts",
77
78
  "plugins/immune-brain/runtime/role_prompt_bridge.ts",
78
79
  "plugins/immune-brain/runtime/loop_contract.ts",
80
+ "plugins/immune-brain/runtime/plugin_version.ts",
79
81
  "plugins/immune-brain/runtime/assurance",
80
82
  "plugins/immune-brain/runtime/claude",
81
83
  "plugins/immune-brain/runtime/prompts",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "immune-brain",
3
- "version": "3.2.1",
3
+ "version": "3.3.0",
4
4
  "description": "Immune-Brain Claude Code Host: native Enrollment, QA, Review, and Kernel settlement.",
5
5
  "author": {
6
6
  "name": "Immune-Brain Team"
@@ -20,6 +20,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
20
20
  import { join, resolve } from "node:path";
21
21
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
22
22
  import { Type } from "typebox";
23
+ import { PLUGIN_VERSION } from "../runtime/plugin_version";
23
24
  import {
24
25
  parseVerificationDescriptor,
25
26
  canonicalDescriptorBytes,
@@ -171,7 +172,6 @@ const TASK_INTENT_SCHEMA = Type.Object({
171
172
 
172
173
  export type { AssuranceRole } from "./pi-canary-assurance";
173
174
  export type AuthorizeOperation =
174
- | "record-user-approval"
175
175
  | "approve-breaking-intent-revision"
176
176
  | "resolve-user-decision"
177
177
  | "stop";
@@ -391,7 +391,7 @@ export default function (
391
391
  "After implementation and focused verification, freeze the artifacts, call advance_assurance, and consume its direct terminal result; do not poll or create a detached job.",
392
392
  "When advance_assurance returns review_ready, invoke the foreground Agent from agent_params once, then pass its structured verdict to submit_review.",
393
393
  "For a complete breaking revision, call approve_breaking_intent_revision with the complete next_intent directly; do not ask for chat pre-confirmation because the host opens the single native confirmation before applying it.",
394
- "For a proven stale authority claim, call repair_authority_state directly; do not ask for chat pre-confirmation because the host opens the single native confirmation.",
394
+ "For a proven stale authority claim, call repair_authority_state directly; the Kernel revalidates and removes only the redundant claim without user interaction.",
395
395
  "After awaiting_user, call request_authorization directly so the host opens the single native confirmation; do not ask for chat pre-confirmation or ask the user to copy or report a command.",
396
396
  ],
397
397
  parameters: Type.Object({
@@ -474,43 +474,6 @@ export default function (
474
474
  presentTaskRailResult(ctx, taskId, blocked);
475
475
  return failCanaryTool(taskId, action.op, "authority_conflict", "authority_conflict", blocked.result, blocked.next_action);
476
476
  }
477
- presentTaskRail(ctx, {
478
- task_id: taskId,
479
- state: "Approval required",
480
- result: "Authority repair needs your approval",
481
- next: "Decide whether to repair the stale claim",
482
- });
483
- const repairSelection = await requestAuthorityDialog(pi, ctx, {
484
- attention_id: randomUUID(),
485
- task_id: taskId,
486
- reason: "authority_repair",
487
- label: "Kernel authority repair required",
488
- }, {
489
- title: "Repair Kernel authority state?",
490
- summary: [
491
- `Owner: ${taskId}`,
492
- `Terminal lifecycle: ${authority.owner_lifecycle}`,
493
- `Claim lifecycle: ${authority.claim_lifecycle_status}`,
494
- ].join("\n"),
495
- details: [
496
- `Projection revision: ${authority.revision}`,
497
- "Action: remove only the stale global claim; preserve TaskRecord and tombstone.",
498
- ].join("\n"),
499
- signal: ctx.signal ?? signal,
500
- actions: [
501
- { value: "repair", label: "Repair stale claim", description: "Remove only the stale global claim" },
502
- { value: "cancel", label: "Cancel", description: "Preserve all authority state unchanged" },
503
- ],
504
- });
505
- if (repairSelection !== "repair") {
506
- const cancelled = {
507
- state: "cancelled",
508
- operation: action.op,
509
- result: "Authority repair cancelled with zero writes",
510
- next_action: "request authorization again if repair is still intended",
511
- };
512
- return toolResult(JSON.stringify(cancelled, null, 2), cancelled);
513
- }
514
477
  try {
515
478
  const repaired = await repairKernelAuthority(ctx.cwd, taskId, authority.revision);
516
479
  const result = {
@@ -574,9 +537,11 @@ export default function (
574
537
  const blockers = state.blocking_finding_ids.length
575
538
  + state.unresolved_user_decision_ids.length
576
539
  + state.replan_required_ids.length;
540
+ const status = { plugin_version: PLUGIN_VERSION, ...state };
577
541
  const details = {
578
542
  state: "status",
579
543
  operation: "status",
544
+ plugin_version: PLUGIN_VERSION,
580
545
  lifecycle: state.lifecycle,
581
546
  artifact_state: state.artifact_state,
582
547
  task_state: state,
@@ -584,7 +549,7 @@ export default function (
584
549
  next_action: state.next_obligation,
585
550
  };
586
551
  presentTaskRailResult(ctx, taskId, details);
587
- return toolResult(JSON.stringify(state, null, 2), details);
552
+ return toolResult(JSON.stringify(status, null, 2), details);
588
553
  }
589
554
  const claim = projection.claim;
590
555
  if (!claim || claim.task_id !== taskId) {
@@ -823,6 +788,17 @@ export default function (
823
788
  `Decision: ${operation}`,
824
789
  `State: ${projection.projection.lifecycle}:${projection.projection.artifact_state} | Claim: ${projection.claim.lifecycle_status}`,
825
790
  ].join("\n");
791
+ const intentDelta = nextIntent
792
+ ? [
793
+ `Risk: ${priorIntent.intent.risk} -> ${nextIntent.risk}`,
794
+ `Goal: ${priorIntent.intent.goal} -> ${nextIntent.goal}`,
795
+ `Scope added: ${nextIntent.scope_hint.filter((path) => !priorIntent.intent.scope_hint.includes(path)).join(", ") || "none"}`,
796
+ `Scope removed: ${priorIntent.intent.scope_hint.filter((path) => !nextIntent.scope_hint.includes(path)).join(", ") || "none"}`,
797
+ `Acceptance added: ${nextIntent.acceptance.filter((item) => !priorIntent.intent.acceptance.some((prior) => prior.id === item.id)).map((item) => item.id).join(", ") || "none"}`,
798
+ `Acceptance removed: ${priorIntent.intent.acceptance.filter((item) => !nextIntent.acceptance.some((next) => next.id === item.id)).map((item) => item.id).join(", ") || "none"}`,
799
+ `Acceptance changed: ${nextIntent.acceptance.filter((item) => { const prior = priorIntent.intent.acceptance.find((candidate) => candidate.id === item.id); return prior && (prior.assertion !== item.assertion || prior.verification !== item.verification); }).map((item) => item.id).join(", ") || "none"}`,
800
+ ]
801
+ : [];
826
802
  const dialogDetails = [
827
803
  `Operation: ${operation}`,
828
804
  ...(userDecisionOperation
@@ -834,9 +810,8 @@ export default function (
834
810
  ...(nextIntent
835
811
  ? [
836
812
  `Next Intent: rev ${nextIntent.revision} (${nextIntentHash})`,
837
- `Next Goal: ${nextIntent.goal}`,
838
- `Next Scope: ${nextIntent.scope_hint.join(", ")}`,
839
- `Next Acceptance Items: ${nextIntent.acceptance.length}`,
813
+ ...intentDelta,
814
+ `Acceptance Items: ${priorIntent.intent.acceptance.length} -> ${nextIntent.acceptance.length}`,
840
815
  `Next staged diff: ${stagedNextDiffHash}`,
841
816
  ]
842
817
  : []),
@@ -922,23 +897,6 @@ export default function (
922
897
  content_hash: nextIntentHash!,
923
898
  };
924
899
  }
925
- // record-user-approval: literal-user approval for critical-task-completion. The approval payload is bound to the fresh
926
- // projection (task revision, intent content hash, diff hash) and
927
- // applied through the same exact-action capability path; the
928
- // The reducer requires kind user, user authority, and active:frozen state.
929
- const isUserApproval = operation === "record-user-approval";
930
- const approval = isUserApproval
931
- ? {
932
- id: `approval-user-${randomUUID().slice(0, 8)}`,
933
- kind: "user" as const,
934
- authority_role: "user" as const,
935
- task_revision: projection.projection.intent_revision,
936
- intent_content_hash: projection.projection.intent_content_hash,
937
- diff_hash: projection.projection.diff_hash,
938
- actor_id: "literal-user",
939
- summary: "literal user approval",
940
- }
941
- : undefined;
942
900
  const exactOperation = operation === "stop"
943
901
  ? { op: "stop" as const, reason: "literal user stopped task parked for replan" }
944
902
  : operation === "approve-breaking-intent-revision"
@@ -947,7 +905,7 @@ export default function (
947
905
  next_intent: nextIntent!,
948
906
  next_intent_ref: nextIntentRef!,
949
907
  }
950
- : userDecisionOperation ?? userOperationFor(operation, approval);
908
+ : userDecisionOperation!;
951
909
  // The exact host-built operation is shared by capability digest and
952
910
  // application payload; command arguments cannot inject authority fields.
953
911
  try {
@@ -971,7 +929,6 @@ export default function (
971
929
  intent_content_hash: nextIntentHash ?? projection.projection.intent_content_hash,
972
930
  diff_hash: operationDiffHash,
973
931
  actor_id: "literal-user",
974
- ...(exactOperation.op === "record_user_approval" ? { approval: exactOperation.approval } : {}),
975
932
  ...(exactOperation.op === "approve_breaking_intent_revision"
976
933
  ? { next_intent: exactOperation.next_intent, next_intent_ref: exactOperation.next_intent_ref }
977
934
  : {}),
@@ -1039,7 +996,6 @@ export default function (
1039
996
 
1040
997
  export type DerivedAuthorizationOperation =
1041
998
  | "resolve-user-decision"
1042
- | "record-user-approval"
1043
999
  | "stop";
1044
1000
 
1045
1001
  // Kernel projection is the sole source of authorization readiness.
@@ -1049,7 +1005,6 @@ export function deriveAuthorizationOperation(input: {
1049
1005
  }): { operation: DerivedAuthorizationOperation } | { blocked: string } {
1050
1006
  if (input.hasOpenReplanRequired) return { operation: "stop" };
1051
1007
  if (input.readiness.state === "resolve_user_decision") return { operation: "resolve-user-decision" };
1052
- if (input.readiness.state === "record_user_approval") return { operation: "record-user-approval" };
1053
1008
  if (input.readiness.blocked) return { blocked: input.readiness.blocked };
1054
1009
  return { blocked: "no unique host-derived authorization operation" };
1055
1010
  }
@@ -1126,15 +1081,6 @@ export function buildUserDecisionOperation(record: {
1126
1081
  };
1127
1082
  }
1128
1083
 
1129
- export function userOperationFor(operation: AuthorizeOperation, approval?: unknown) {
1130
- if (operation !== "record-user-approval")
1131
- throw new Error(`unsupported authorize operation: ${operation}`);
1132
- // The approval payload is constructed by the authorize handler from
1133
- // the fresh projection; it is never derived from untrusted input.
1134
- if (approval === undefined) throw new Error("record-user-approval requires an approval payload");
1135
- return { op: "record_user_approval" as const, approval };
1136
- }
1137
-
1138
1084
  /**
1139
1085
  * One record-aware freshness identity. v4 derives the scoped revision digest
1140
1086
  * from the immutable Enrollment base so committed and staged task work share a
@@ -8,8 +8,7 @@ export type UserAttentionReason =
8
8
  | "enrollment"
9
9
  | "descriptor_waiver"
10
10
  | "breaking_intent_revision"
11
- | "review_authorization"
12
- | "authority_repair";
11
+ | "review_authorization";
13
12
 
14
13
  export interface UserAttentionEventV1 {
15
14
  active: boolean;
@@ -179,7 +179,7 @@ export interface TaskRecordRead {
179
179
  // --- Assurance projection (host-neutral Kernel facts, not exported from the
180
180
  // public Kernel index) ---
181
181
  export interface AssuranceAuthorizationReadiness {
182
- state: "resolve_user_decision" | "record_user_approval" | "none";
182
+ state: "resolve_user_decision" | "none";
183
183
  blocked: string | null;
184
184
  }
185
185
  export interface AssuranceProjection {
@@ -191,7 +191,7 @@ export interface AssuranceProjection {
191
191
  lifecycle: "active" | "done" | "stopped" | "";
192
192
  artifact_state: "active" | "frozen" | "";
193
193
  risk: "routine" | "material" | "critical" | "";
194
- next_obligation: "resolve_findings" | "resolve_user_decision" | "revise_intent" | "submit_assurance" | "run_qa" | "run_review" | "authorize_user" | "complete" | "none";
194
+ next_obligation: "resolve_findings" | "resolve_user_decision" | "revise_intent" | "submit_assurance" | "run_qa" | "run_review" | "complete" | "none";
195
195
  fresh_acceptance_ids: string[];
196
196
  missing_acceptance_ids: string[];
197
197
  stale_attestation_ids: string[];
@@ -52,14 +52,16 @@ function probeHost(env = process.env, platform = process.platform, hostVersion)
52
52
  return { ok: true, version, permissionMode: "manual", platform };
53
53
  }
54
54
 
55
+ // plugins/immune-brain/runtime/plugin_version.ts
56
+ var PLUGIN_VERSION = "3.3.0";
57
+
55
58
  // plugins/immune-brain/runtime/claude/interaction.ts
56
59
  import { createHash, randomUUID } from "node:crypto";
57
60
  var PRIVILEGED_OPERATIONS = [
58
61
  "enroll",
59
62
  "request_authorization",
60
63
  "approve_breaking_intent_revision",
61
- "stop",
62
- "repair_authority_state"
64
+ "stop"
63
65
  ];
64
66
  function isPrivilegedOperation(operation) {
65
67
  return PRIVILEGED_OPERATIONS.includes(operation);
@@ -1484,8 +1486,6 @@ class AssuranceCoordinator {
1484
1486
  return this.unknownAfterCommit(taskId, "qa", operationId, boundedAssuranceError(error));
1485
1487
  }
1486
1488
  }
1487
- if (projection.projection.next_obligation === "authorize_user")
1488
- return { state: "awaiting_user", operation: "record-user-approval", operation_id: operationId };
1489
1489
  if (projection.projection.next_obligation !== "run_qa" && projection.projection.next_obligation !== "run_review")
1490
1490
  return { state: "blocked", reason: `Kernel requires ${projection.projection.next_obligation}` };
1491
1491
  const qaAlreadySettled = projection.projection.next_obligation === "run_review";
@@ -1583,8 +1583,6 @@ class AssuranceCoordinator {
1583
1583
  return this.unknownAfterCommit(taskId, "qa", operationId, boundedAssuranceError(error));
1584
1584
  }
1585
1585
  }
1586
- if (fresh.projection.next_obligation === "authorize_user")
1587
- return { state: "awaiting_user", operation: "record-user-approval", operation_id: operationId };
1588
1586
  if (fresh.projection.next_obligation !== "run_review") {
1589
1587
  if (authorityCommitted && aborted())
1590
1588
  return this.unknownAfterCommit(taskId, "qa", operationId, "QA settlement projection did not require Review after cancellation");
@@ -1747,8 +1745,6 @@ class AssuranceCoordinator {
1747
1745
  return this.unknownAfterCommit(taskId, "review", reservation.operationId, boundedAssuranceError(error));
1748
1746
  }
1749
1747
  }
1750
- if (settled.projection.next_obligation === "authorize_user")
1751
- return { state: "awaiting_user", operation: "record-user-approval", operation_id: reservation.operationId };
1752
1748
  return { state: "blocked", reason: `Kernel requires ${settled.projection.next_obligation} after Review` };
1753
1749
  }
1754
1750
  abandonReview(taskId, reason) {
@@ -3633,7 +3629,6 @@ var ACTION_V2_TYPES = [
3633
3629
  "record_finding",
3634
3630
  "resolve_finding",
3635
3631
  "record_approval",
3636
- "record_user_approval",
3637
3632
  "revise_intent",
3638
3633
  "approve_breaking_intent_revision",
3639
3634
  "request_rework",
@@ -3696,8 +3691,7 @@ function parseTaskAction(raw) {
3696
3691
  };
3697
3692
  break;
3698
3693
  }
3699
- case "record_approval":
3700
- case "record_user_approval": {
3694
+ case "record_approval": {
3701
3695
  rejectUnknown2(value, [...ACTION_BASE_FIELDS, "approval"], "action", violations);
3702
3696
  const approval = parseApprovalV2(value.approval, 0, violations, true);
3703
3697
  if (approval.review_revision && approval.kind !== "review")
@@ -4904,7 +4898,7 @@ function commitTerminalLocked(root, taskId, transaction, tombstone) {
4904
4898
  var REQUIRED_ATTESTATIONS = {
4905
4899
  routine: ["qa"],
4906
4900
  material: ["qa", "review"],
4907
- critical: ["qa", "review", "user"]
4901
+ critical: ["qa", "review"]
4908
4902
  };
4909
4903
  function archiveActivePlanningPath(path) {
4910
4904
  const matched = path.match(/^docs\/(plans|specs)\/([^/]+)$/);
@@ -5009,8 +5003,6 @@ function projectTask(intent, record, currentDiffHash, currentIntentContentHash,
5009
5003
  nextObligation = "run_qa";
5010
5004
  } else if (decision.missing_approval_kinds.includes("review")) {
5011
5005
  nextObligation = "run_review";
5012
- } else if (decision.missing_approval_kinds.includes("user")) {
5013
- nextObligation = "authorize_user";
5014
5006
  } else if (decision.complete) {
5015
5007
  nextObligation = "complete";
5016
5008
  }
@@ -5036,8 +5028,6 @@ function deriveAssuranceAuthorization(input) {
5036
5028
  state: "none",
5037
5029
  blocked: `resolve-user-decision requires exactly one open user decision; found ${input.open_user_decision_count}`
5038
5030
  };
5039
- if (input.next_obligation === "authorize_user")
5040
- return { state: "record_user_approval", blocked: null };
5041
5031
  return { state: "none", blocked: null };
5042
5032
  }
5043
5033
  function emptyProjection() {
@@ -5288,7 +5278,7 @@ function intentRefMatches(intent, ref) {
5288
5278
  return ref.path === `docs/plans/${intent.task_id}.intent.json` && ref.content_hash === canonicalIntentHash(intent);
5289
5279
  }
5290
5280
  function hasPrivilegedKind(action) {
5291
- return action.type === "record_approval" || action.type === "record_user_approval" || action.type === "approve_breaking_intent_revision" || action.type === "request_rework" || action.type === "stop" || action.type === "resolve_user_decision";
5281
+ return action.type === "record_approval" || action.type === "approve_breaking_intent_revision" || action.type === "request_rework" || action.type === "stop" || action.type === "resolve_user_decision";
5292
5282
  }
5293
5283
  function findingsDigestV2(findings) {
5294
5284
  const normalized = findings.map((finding) => ({
@@ -5440,36 +5430,6 @@ function reduceTask(recordRaw, actionRaw, authorityAudit = null, changedPaths) {
5440
5430
  appendHistory(record, action, from, approval.id, authorityAudit);
5441
5431
  break;
5442
5432
  }
5443
- case "record_user_approval": {
5444
- if (record.lifecycle !== "active" || record.artifact_state !== "frozen")
5445
- throw new KernelInvariantError([
5446
- `cannot record user approval while state is ${stateOf(record)}`
5447
- ]);
5448
- const approval = action.approval;
5449
- if (approval.kind !== "user")
5450
- throw new KernelInvariantError([
5451
- "record_user_approval requires kind user"
5452
- ]);
5453
- if (!authorityAudit || authorityAudit.authority_kind !== "user")
5454
- throw new KernelInvariantError([
5455
- "record_user_approval requires user authority"
5456
- ]);
5457
- if (approval.task_revision !== record.intent_snapshot.revision)
5458
- throw new KernelInvariantError(["approval task_revision must equal the current intent revision"]);
5459
- if (approval.intent_content_hash !== record.intent_ref.content_hash)
5460
- throw new KernelInvariantError(["approval intent_content_hash must equal the current intent hash"]);
5461
- if (approval.diff_hash !== diffHash)
5462
- throw new KernelInvariantError(["approval diff_hash must equal the action diff hash"]);
5463
- if (record.attestations.some((item) => item.id === approval.id))
5464
- throw new KernelInvariantError([
5465
- `attestations contains duplicate id ${approval.id}`
5466
- ]);
5467
- if (approval.review_revision)
5468
- throw new KernelInvariantError(["review_revision is only valid on review approvals"]);
5469
- record.attestations.push({ ...approval, acceptance_results: [] });
5470
- appendHistory(record, action, from, approval.id, authorityAudit);
5471
- break;
5472
- }
5473
5433
  case "revise_intent":
5474
5434
  case "approve_breaking_intent_revision": {
5475
5435
  if (record.lifecycle !== "active")
@@ -5706,7 +5666,7 @@ function applyTaskAction(input) {
5706
5666
  "intent token does not match the committed record intent"
5707
5667
  ]);
5708
5668
  }
5709
- const privileged = action.type === "record_approval" || action.type === "record_user_approval" || action.type === "approve_breaking_intent_revision" || action.type === "request_rework" || action.type === "stop" || action.type === "resolve_user_decision";
5669
+ const privileged = action.type === "record_approval" || action.type === "approve_breaking_intent_revision" || action.type === "request_rework" || action.type === "stop" || action.type === "resolve_user_decision";
5710
5670
  const expectedAuthority = privileged ? {
5711
5671
  task_id,
5712
5672
  action,
@@ -5828,8 +5788,6 @@ function capabilityActionFor(input) {
5828
5788
  switch (input.op) {
5829
5789
  case "record_approval":
5830
5790
  return { ...base, approval: input.approval };
5831
- case "record_user_approval":
5832
- return { ...base, approval: input.approval };
5833
5791
  case "request_rework":
5834
5792
  return { ...base, findings: input.findings };
5835
5793
  case "stop":
@@ -6014,10 +5972,6 @@ function createCanaryApplication(registry) {
6014
5972
  capability = operation.capability;
6015
5973
  action = { ...base, type: "record_approval", approval: operation.approval };
6016
5974
  break;
6017
- case "record_user_approval":
6018
- capability = operation.capability;
6019
- action = { ...base, type: "record_user_approval", approval: operation.approval };
6020
- break;
6021
5975
  case "revise_intent":
6022
5976
  action = {
6023
5977
  ...base,
@@ -6914,22 +6868,17 @@ class ClaudeRuntime {
6914
6868
  return submitClaudeReview(this.host, this.coordinator, { cwd: this.cwd }, taskId, verdictInput);
6915
6869
  }
6916
6870
  async authorize(taskId, operation, meta, extra = {}) {
6917
- if (!isPrivilegedOperation(operation) && operation !== "request_authorization")
6918
- throw new Error(`unsupported privileged operation ${operation}`);
6919
- this.rejectBeforePreparation(operation, { ...meta, taskId });
6920
6871
  if (operation === "repair_authority_state") {
6921
6872
  const authority = reconcileKernelAuthority(this.cwd, taskId);
6922
6873
  if (authority.state !== "repairable_stale_claim" || authority.owner_task_id !== taskId) {
6923
6874
  throw new Error(authority.diagnostic ?? "authority repair requires a repairable stale claim");
6924
6875
  }
6925
- const gate2 = this.gate(operation, { ...meta, taskId }, { bindingDigest: `repair:${authority.revision}` });
6926
- const current = reconcileKernelAuthority(this.cwd, taskId);
6927
- if (current.state !== authority.state || current.owner_task_id !== authority.owner_task_id || current.revision !== authority.revision) {
6928
- throw new Error("authority changed after native confirmation; repair aborted before capability issuance");
6929
- }
6930
- return repairKernelAuthority(this.cwd, taskId, current.revision);
6876
+ return repairKernelAuthority(this.cwd, taskId, authority.revision);
6931
6877
  }
6932
- let op = operation === "request_authorization" ? "record_user_approval" : operation;
6878
+ if (!isPrivilegedOperation(operation) && operation !== "request_authorization")
6879
+ throw new Error(`unsupported privileged operation ${operation}`);
6880
+ this.rejectBeforePreparation(operation, { ...meta, taskId });
6881
+ let op = operation;
6933
6882
  let decisionOp;
6934
6883
  const projection = await this.status(taskId);
6935
6884
  if (projection.error || !projection.claim)
@@ -6943,7 +6892,7 @@ class ClaudeRuntime {
6943
6892
  throw new Error(`resolve-user-decision requires exactly one open user decision; found ${open.length}`);
6944
6893
  op = "resolve_user_decision";
6945
6894
  decisionOp = { finding_id: open[0].id, resolution: `resume after literal-user decision: ${open[0].summary}` };
6946
- } else if (readiness.state !== "record_user_approval") {
6895
+ } else {
6947
6896
  throw new Error(readiness.blocked ?? "no unique host-derived authorization operation");
6948
6897
  }
6949
6898
  }
@@ -7010,16 +6959,6 @@ class ClaudeRuntime {
7010
6959
  }
7011
6960
  const { registry, app } = this.authority();
7012
6961
  const confirmation = gate.confirmation_ref;
7013
- const approval = op === "record_user_approval" ? {
7014
- id: `approval-user-${randomUUID5().slice(0, 8)}`,
7015
- kind: "user",
7016
- authority_role: "user",
7017
- task_revision: projection.projection.intent_revision,
7018
- intent_content_hash: projection.projection.intent_content_hash,
7019
- diff_hash: projection.projection.diff_hash,
7020
- actor_id: actorId,
7021
- summary: "literal user approval"
7022
- } : undefined;
7023
6962
  try {
7024
6963
  const capabilityProjection = await this.status(taskId);
7025
6964
  assertProjectionBinding(projection, capabilityProjection, Boolean(nextIntent));
@@ -7038,7 +6977,6 @@ class ClaudeRuntime {
7038
6977
  actor_id: actorId,
7039
6978
  now,
7040
6979
  confirmation_ref: confirmation,
7041
- ...approval ? { approval } : {},
7042
6980
  ...op === "approve_breaking_intent_revision" ? { next_intent: nextIntent, next_intent_ref: nextIntentRef } : {},
7043
6981
  ...op === "resolve_user_decision" && decisionOp ? decisionOp : {},
7044
6982
  ...op === "stop" ? { reason: extra.reason ?? "user stop" } : {}
@@ -7050,7 +6988,6 @@ class ClaudeRuntime {
7050
6988
  op,
7051
6989
  capability,
7052
6990
  actor_id: actorId,
7053
- ...approval ? { approval } : {},
7054
6991
  ...op === "approve_breaking_intent_revision" ? { next_intent: nextIntent, next_intent_ref: nextIntentRef } : {},
7055
6992
  ...op === "resolve_user_decision" && decisionOp ? decisionOp : {},
7056
6993
  ...op === "stop" ? { reason: extra.reason ?? "user stop" } : {}
@@ -7194,7 +7131,7 @@ var TOOLS = [
7194
7131
  { name: "request_authorization", description: "Apply exact literal-user authorization.", privileged: true },
7195
7132
  { name: "approve_breaking_intent_revision", description: "Approve a breaking TaskIntent revision.", privileged: true },
7196
7133
  { name: "stop", description: "Stop the active task with literal-user authority.", privileged: true },
7197
- { name: "repair_authority_state", description: "Repair a recoverable stale backend claim.", privileged: true }
7134
+ { name: "repair_authority_state", description: "Repair a proven recoverable stale backend claim.", privileged: false }
7198
7135
  ];
7199
7136
  function listMcpTools() {
7200
7137
  return TOOLS.map((tool) => ({
@@ -7242,10 +7179,10 @@ function createMcpRuntime(options = {}) {
7242
7179
  if ("native_decision" in args)
7243
7180
  throw new Error("native_decision cannot be supplied in tool arguments");
7244
7181
  if (name === "status")
7245
- return runtime.status(taskId);
7182
+ return { plugin_version: PLUGIN_VERSION, ...await runtime.status(taskId) };
7246
7183
  if (!negotiatedVersion)
7247
7184
  throw new Error("Claude Code version is unavailable");
7248
- if (!negotiatedInteractive)
7185
+ if (!negotiatedInteractive && name !== "repair_authority_state")
7249
7186
  throw new Error("non-interactive host session cannot execute authority tools");
7250
7187
  const probe = probeHost(options.env ?? process.env, process.platform, negotiatedVersion);
7251
7188
  if (!probe.ok)
@@ -7306,7 +7243,12 @@ async function handleJsonRpc(message, mcp = createMcpRuntime()) {
7306
7243
  id: message.id ?? null,
7307
7244
  result: {
7308
7245
  protocolVersion: "2024-11-05",
7309
- serverInfo: { name: HOST_ID, version: MIN_CLAUDE_CODE_VERSION, contract: CORE_CONTRACT },
7246
+ serverInfo: {
7247
+ name: HOST_ID,
7248
+ version: PLUGIN_VERSION,
7249
+ contract: CORE_CONTRACT,
7250
+ minimumHostVersion: MIN_CLAUDE_CODE_VERSION
7251
+ },
7310
7252
  capabilities: { tools: {} }
7311
7253
  }
7312
7254
  };
@@ -7506,9 +7448,9 @@ if (entry.endsWith("mcp_server.ts") || entry.endsWith("mcp-server.mjs")) {
7506
7448
  serveStdio();
7507
7449
  }
7508
7450
  export {
7509
- serveStdio,
7510
- listMcpTools,
7511
- handleJsonRpc,
7451
+ TOOLS,
7512
7452
  createMcpRuntime,
7513
- TOOLS
7453
+ handleJsonRpc,
7454
+ listMcpTools,
7455
+ serveStdio
7514
7456
  };
@@ -90,7 +90,7 @@ Pi `Agent` has no `readonly` parameter; the empty tool policy, child type, and p
90
90
  4. Kernel Assurance chat uses native Tool rendering only. It does not publish completion messages or wake a later parent turn; the direct Tool result is the continuation boundary. `awaiting_user` remains valid until the host-built authorization operation is confirmed and the immutable snapshot is revalidated.
91
91
  5. Child 不得再次派发 child,nested delegation 一律禁止。Parent 保留综合与最终判断责任。
92
92
  6. Kernel authority Review 对每个 immutable snapshot 恰好一个 primary reviewer,turn 预算按 workload 缩放(Quick 12 / Standard 16 / Heavy 24),并使用该 snapshot 对应的 Quick/Standard/Heavy 执行档位;不存在从 initial dispatch 起算的单一端到端总预算。Reviewer 必须先验证 immutable v5 manifest 的 `base_head`、`review_commit`、单一 parent、`review_tree` 与 `manifest_digest`,再用只读 Git 命令从 synthetic revision 获取源码;manifest 只有 metadata,不复制 source bytes,也不枚举 neighborhood files。审查只围绕 acceptance assertions 与 `changed_paths`,未变更路径只有在 acceptance、changed caller 或同一 state machine 直接需要时按需读取并注明理由;Reviewer 不探索无关 repository paths。对 settlement-class change,Reviewer 必须先枚举 immutable revision 内每条 terminal、cancellation、timeout 与 race path,再对全部路径给出判断;finding summary 必须以受影响路径开头。每个 acceptance 的执行结果已由 deterministic QA 在 review 前验证并内嵌于 manifest 的 `outcomes` 字段(acceptance_id -> {status, summary});Reviewer 不得重跑 descriptor,也不得把本地没有测试运行当作 finding——Review 只审 revision provenance、代码正确性、回归、安全与缺失测试。除该 reviewer 外,同一触发点最多两个相互独立的 advisory/discovery children;它们只能并行读,不能写 workflow state、关闭 QA 或产生 authority。
93
- 7. Foreground assurance never sleeps, polls, or schedules a completion callback. One Tool call owns QA; one Parent turn obtains and submits the reviewer verdict; one explicit authorization operation owns the critical-risk user decision.
93
+ 7. Foreground assurance never sleeps, polls, or schedules a completion callback. One Tool call owns QA; one Parent turn obtains and submits the reviewer verdict. User authorization is reserved for unresolved decisions, explicit stop, and breaking Intent revisions; no risk tier adds a generic final confirmation.
94
94
  8. `submit_review` validates the verdict contract, task identity, immutable snapshot digest, and fresh record/Intent/workspace/diff revisions before applying Review authority. Malformed verdicts are retryable without rebuilding evidence. Stale verdicts leave the TaskRecord unchanged and release the stale reservation. Local execution trusts the Parent to relay the reviewer verdict and does not require Agent lifecycle receipts.
95
95
  9. The assurance Tool checks the immutable snapshot before each phase and returns a terminal structured state directly: `cancelled`, `rework`, `review_ready`, `awaiting_user`, `blocked`, or `settlement_unknown`. The commit boundary is non-cancellable. No silent task, status timer, completion notification, or result retrieval path is permitted.
96
96
 
@@ -55,9 +55,12 @@ commit.
55
55
  routing owner or scope cannot be read reliably, fail closed for mutation.
56
56
 
57
57
  3. **Inventory instruction relationships.** Determine nesting, explicit
58
- precedence statements, inbound references, and existing authority pointers.
59
- Preserve each file's native organization. Do not normalize files to a shared
60
- template and do not infer unsupported cross-host inheritance semantics.
58
+ precedence statements, inbound references, existing authority pointers, and
59
+ semantic duplication within and across candidates. Preserve each file's
60
+ native organization. Do not normalize files to a shared template and do not
61
+ infer unsupported cross-host inheritance semantics. Resolve duplicate
62
+ ownership only from explicit precedence, authority, and inbound-reference
63
+ evidence; unresolved ownership remains `UNVERIFIED` or `BLOCKED`.
61
64
 
62
65
  4. **Build repository facts.** Resolve current truth in this order:
63
66
  executable/public registries, package exports, CLI/runtime entrypoints;
@@ -77,14 +80,25 @@ commit.
77
80
  6. **Classify exact actions.** Classify entries as `REMOVE`, `REWRITE`,
78
81
  `POINTER`, `KEEP`, `BLOCKED`, `BLOCKED_ACTIVE_SCOPE`, `UNVERIFIED`, or
79
82
  `MISSING_OWNER`. Every mutation entry identifies exact file/section bytes,
80
- preserved meaning, evidence, candidate hash, and resulting text. `POINTER`
81
- may target only an existing current authority and must include a concrete
82
- trigger condition. Missing reference ownership is `MISSING_OWNER`; this Skill
83
- does not create a reference document. Unknown or one-off-looking rules remain
84
- `UNVERIFIED` and are not deleted by default. Unresolved precedence or
85
- semantic conflicts that cannot be decided from explicit repository scope or
86
- declared precedence are `BLOCKED`. Filename convention, nesting, or guessed
87
- host behavior alone may not resolve a conflict.
83
+ preserved meaning, evidence, candidate hash, and resulting text. A retained
84
+ meaning has one instruction owner; duplicate copies are not all `KEEP` by
85
+ default. Rules already enforced deterministically by a formatter, linter,
86
+ schema, permission policy, Git hook, or runtime are `REMOVE` unless the agent
87
+ still needs a non-discoverable recovery action, in which case use the shortest
88
+ `REWRITE` or `POINTER`. Keep globally relevant retained rules inline. For a
89
+ branch-specific rule, prefer `POINTER` to an existing current authority with
90
+ a concrete trigger condition; when no owner exists, use `MISSING_OWNER`
91
+ rather than creating one. This Skill does not create a reference document.
92
+ Do not introduce host-specific conditional markup,
93
+ including `<important if>`, without an explicit current host contract and
94
+ repository evidence that the target file uses those semantics. Discoverable
95
+ code snippets, examples, and command tables are repository caches and are
96
+ removed or replaced with a stable pointer unless they encode a necessary
97
+ non-discoverable exception or recovery action. Unknown or one-off-looking
98
+ rules remain `UNVERIFIED` and are not deleted by default. Unresolved
99
+ precedence or semantic conflicts that cannot be decided from explicit
100
+ repository scope or declared precedence are `BLOCKED`. Filename convention,
101
+ nesting, or guessed host behavior alone may not resolve a conflict.
88
102
 
89
103
  7. **Produce one exact manifest.** `audit` mode stops after the manifest.
90
104
  Mutation mode also stops until the literal user approves exact manifest
@@ -103,8 +117,9 @@ commit.
103
117
  brevity.
104
118
 
105
119
  9. **Verify and report.** Re-scan modified instruction relationships, local
106
- pointer targets, unresolved conflicts, source/package public-surface parity,
107
- existing focused documentation contracts, and `git diff --check`. This Skill
120
+ pointer targets, duplicate retained meanings, unresolved conflicts,
121
+ source/package public-surface parity, existing focused documentation
122
+ contracts, and `git diff --check`. This Skill
108
123
  does not execute arbitrary documented commands, real-model success-rate
109
124
  benchmarks, or Token measurements unless the user literally requests a
110
125
  benchmark. This Skill does not commit. Report only `Removed`, `Rewritten`,