immune-brain 3.0.2 → 3.2.1

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.
@@ -30,6 +30,7 @@ export interface GithubTrackerResult {
30
30
  association_found: boolean;
31
31
  issue_number?: number;
32
32
  issue_url?: string;
33
+ node_id?: string;
33
34
  message: string;
34
35
  }
35
36
 
@@ -43,11 +44,47 @@ export interface InitiativeSlice {
43
44
  export interface InitiativeProjection {
44
45
  problem?: string;
45
46
  result?: string;
47
+ design?: string;
46
48
  decisions?: string[];
47
49
  testing_strategy?: string;
48
50
  out_of_scope?: string[];
49
51
  }
50
52
 
53
+ export interface InitiativePublicationInput {
54
+ initiative_id: string;
55
+ goal: string;
56
+ projection: InitiativeProjection;
57
+ tasks: Array<{
58
+ slice_id: string;
59
+ intent: string;
60
+ projection?: TaskProjection;
61
+ }>;
62
+ }
63
+
64
+ export interface GithubInitiativePublicationResult {
65
+ contract: "immune_brain/github_initiative_publication/v1";
66
+ operation: "publish-initiative";
67
+ status: TrackerStatus;
68
+ initiative?: GithubTrackerResult;
69
+ tasks: Array<{
70
+ task_id: string;
71
+ slice_id: string;
72
+ status: TrackerStatus;
73
+ issue_number?: number;
74
+ issue_url?: string;
75
+ node_id?: string;
76
+ }>;
77
+ execution?: {
78
+ recommended_first_task_id: string;
79
+ recommended_first_issue_number: number;
80
+ order: string[];
81
+ issue_order: number[];
82
+ parallel_groups: string[][];
83
+ parallel_issue_groups: number[][];
84
+ };
85
+ message: string;
86
+ }
87
+
51
88
  export interface TaskProjection {
52
89
  result?: string;
53
90
  current_behavior?: string;
@@ -215,7 +252,7 @@ function result(
215
252
  operation,
216
253
  status,
217
254
  association_found: issue !== undefined,
218
- ...(issue ? { issue_number: issue.number, issue_url: issue.url } : {}),
255
+ ...(issue ? { issue_number: issue.number, issue_url: issue.url, node_id: String(issue.id) } : {}),
219
256
  message: redactGithubDiagnostic(message),
220
257
  };
221
258
  }
@@ -431,24 +468,34 @@ function sliceCount(parentBody: string, sliceId: string): number {
431
468
  return countLiteral(parentBody, marker("slice-id", sliceId));
432
469
  }
433
470
 
434
- async function confirmAttachment(
471
+ async function readSubIssueNumbers(
435
472
  root: string,
436
473
  gh: GhTransport,
437
474
  operation: TrackerOperation["op"],
438
475
  repository: RepositoryInfo,
439
476
  parentNumber: number,
440
- childNumber: number,
441
- ): Promise<GithubTrackerResult | { attached: boolean }> {
477
+ ): Promise<GithubTrackerResult | number[]> {
442
478
  const listed = await gh.run(["api", "--paginate", "--slurp", `repos/${repository.name_with_owner}/issues/${parentNumber}/sub_issues?per_page=100`], { cwd: root });
443
479
  if (listed.exit_code !== 0 || listed.output_exceeded)
444
480
  return ghFailure(operation, listed, "cannot read native Sub-issue relations");
445
- let numbers: number[];
446
481
  try {
447
- numbers = parseSubIssueNumbers(listed.stdout);
482
+ return parseSubIssueNumbers(listed.stdout);
448
483
  } catch (error) {
449
484
  return result(operation, "permanent_failure", error instanceof Error ? error.message : String(error));
450
485
  }
451
- const matches = numbers.filter((candidate) => candidate === childNumber).length;
486
+ }
487
+
488
+ async function confirmAttachment(
489
+ root: string,
490
+ gh: GhTransport,
491
+ operation: TrackerOperation["op"],
492
+ repository: RepositoryInfo,
493
+ parentNumber: number,
494
+ childNumber: number,
495
+ ): Promise<GithubTrackerResult | { attached: boolean }> {
496
+ const read = await readSubIssueNumbers(root, gh, operation, repository, parentNumber);
497
+ if (!Array.isArray(read)) return read;
498
+ const matches = read.filter((candidate) => candidate === childNumber).length;
452
499
  if (matches > 1) return result(operation, "ambiguous_remote_state", `Issue #${parentNumber} lists the Task Issue more than once`);
453
500
  return { attached: matches === 1 };
454
501
  }
@@ -565,7 +612,7 @@ function createInitiativeBody(repository: RepositoryInfo, operation: Extract<Tra
565
612
  KIND_INITIATIVE_MARKER,
566
613
  marker("repo-id", repository.id),
567
614
  marker("initiative-id", operation.initiative_id),
568
- ].join("\n")}\n\n# ${titleText(projection.result ?? operation.goal)}\n\nOpt-in, non-authoritative Immune-Brain Initiative planning carrier. Kernel TaskIntent, TaskRecord, and Assurance remain the execution authority.\n\n## How to use this Issue\n\n- Edit planning prose and Slice ordering directly after creation.\n- Keep each Slice marker attached to exactly one stable Slice entry.\n- The tracker never rewrites or closes this Parent after creation; the tracker never changes or closes it automatically.\n\n## Problem\n\n${publicText(projection.problem ?? "The Initiative addresses the bounded delivery described below.", "projection.problem")}\n\n## Result\n\n${publicText(projection.result ?? operation.goal, "projection.result")}\n\n## Decisions\n\n${listText(projection.decisions, "- No additional Initiative decisions recorded.")}\n\n## Testing strategy\n\n${publicText(projection.testing_strategy ?? "Each Child closes from its focused acceptance verification.", "projection.testing_strategy")}\n\n## Out of scope\n\n${listText(projection.out_of_scope, "- Unrelated work outside this Initiative.")}\n\n## Slices\n\n${operation.slices.length === 0 ? "No Slices recorded yet." : operation.slices.map((slice) => `- [ ] ${marker("slice-id", slice.id)} **${slice.id}**: ${slice.result ?? slice.goal}${slice.blocked_by?.length ? ` (blocked by: ${slice.blocked_by.join(", ")})` : ""}`).join("\n")}\n\n## Authority boundary\n\nThis Issue is outbound visibility only. GitHub state never starts, authorizes, reprioritizes, or settles work. Native Sub-issues identify published Tasks; their state is only an observation.\n`;
615
+ ].join("\n")}\n\n# ${titleText(projection.result ?? operation.goal)}\n\nOpt-in, non-authoritative Immune-Brain Initiative planning carrier. Kernel TaskIntent, TaskRecord, and Assurance remain the execution authority.\n\n## How to use this Issue\n\n- Edit planning prose and Slice ordering directly after creation.\n- Keep each Slice marker attached to exactly one stable Slice entry.\n- The tracker never rewrites or closes this Parent after creation; the tracker never changes or closes it automatically.\n\n## Problem\n\n${publicText(projection.problem ?? "The Initiative addresses the bounded delivery described below.", "projection.problem")}\n\n## Result\n\n${publicText(projection.result ?? operation.goal, "projection.result")}\n\n## Initiative design\n\n${publicText(projection.design ?? "Each Child preserves the shared Initiative decisions and boundaries recorded here.", "projection.design")}\n\n## Decisions\n\n${listText(projection.decisions, "- No additional Initiative decisions recorded.")}\n\n## Testing strategy\n\n${publicText(projection.testing_strategy ?? "Each Child closes from its focused acceptance verification.", "projection.testing_strategy")}\n\n## Out of scope\n\n${listText(projection.out_of_scope, "- Unrelated work outside this Initiative.")}\n\n## Slices\n\n${operation.slices.length === 0 ? "No Slices recorded yet." : operation.slices.map((slice) => `- [ ] ${marker("slice-id", slice.id)} **${slice.id}**: ${slice.result ?? slice.goal}${slice.blocked_by?.length ? ` (blocked by: ${slice.blocked_by.join(", ")})` : ""}`).join("\n")}\n\n## Authority boundary\n\nThis Issue is outbound visibility only. GitHub state never starts, authorizes, reprioritizes, or settles work. Native Sub-issues identify published Tasks; their state is only an observation.\n`;
569
616
  }
570
617
 
571
618
  async function createInitiative(
@@ -610,7 +657,11 @@ async function createInitiative(
610
657
  );
611
658
  }
612
659
 
613
- function childBody(repository: RepositoryInfo, operation: Extract<TrackerOperation, { op: "upsert-task" }>): string {
660
+ function childBody(
661
+ repository: RepositoryInfo,
662
+ operation: Extract<TrackerOperation, { op: "upsert-task" }>,
663
+ parent: GithubIssue,
664
+ ): string {
614
665
  const projection = operation.projection ?? {};
615
666
  const acceptance = operation.acceptance.map((item) => `- \`${item.id}\`: ${item.summary}`).join("\n");
616
667
  return `${[
@@ -620,7 +671,7 @@ function childBody(repository: RepositoryInfo, operation: Extract<TrackerOperati
620
671
  marker("initiative-id", operation.initiative_id),
621
672
  marker("slice-id", operation.slice_id),
622
673
  marker("task-id", operation.task_id),
623
- ].join("\n")}\n\n# ${titleText(projection.result ?? operation.goal)}\n\nOpt-in, non-authoritative Immune-Brain Task Issue. Kernel TaskIntent, TaskRecord, and Assurance remain the execution authority.\n\n## Parent\n\n| Initiative | \`${operation.initiative_id}\` |\n| Slice | \`${operation.slice_id}\` |\n| Risk | \`${operation.risk}\` |\n\n## What to build\n\n${publicText(projection.result ?? operation.goal, "projection.result")}\n\n## Current behavior\n\n${publicText(projection.current_behavior ?? "The current behavior is defined by the repository's existing contract.", "projection.current_behavior")}\n\n## Desired behavior\n\n${publicText(projection.desired_behavior ?? operation.goal, "projection.desired_behavior")}\n\n## Key interfaces\n\n${listText(projection.key_interfaces, "- Canonical TaskIntent acceptance and Kernel lifecycle remain authoritative.")}\n\n## Acceptance criteria\n\n${acceptance}\n\n## Verification\n\n${publicText(projection.verification ?? "Run the focused acceptance verification declared by the TaskIntent.", "projection.verification")}\n\n## Blocked by\n\n${projection.blocked_by?.length ? projection.blocked_by.map((id) => `- \`${identifier(id, "blocked_by task_id")}\``).join("\n") : "None"}\n\n## Out of scope\n\n${listText(projection.out_of_scope, "- Scope not declared by the validated TaskIntent.")}\n\n## Agent handoff\n\n${publicText(projection.agent_handoff ?? "Implement only the bounded TaskIntent result and run the focused checks. Do not widen scope or treat GitHub as authorization.", "projection.agent_handoff")}\n\n## Lifecycle\n\n- **Open** means this Task still needs attention; it does not mean the Task is authorized or executing.\n- Only a fresh claimless terminal projection can close this Issue: \`done\` becomes **Completed**, and \`stopped\` becomes **Not planned**.\n\n## Authority boundary\n\nThis Issue is outbound visibility only. GitHub state never changes TaskIntent, TaskRecord, QA, Review, authorization, or Kernel settlement. Internal role prompts, tool policies, review gates, model reservations, and prompt digests are not part of this external handoff.\n`;
674
+ ].join("\n")}\n\n# ${titleText(projection.result ?? operation.goal)}\n\nOpt-in, non-authoritative Immune-Brain Task Issue. Kernel TaskIntent, TaskRecord, and Assurance remain the execution authority.\n\n## Parent\n\n| Initiative | \`${operation.initiative_id}\` |\n| Parent Issue | [#${parent.number}](${parent.url}) |\n| Slice | \`${operation.slice_id}\` |\n| Risk | \`${operation.risk}\` |\n\n## What to build\n\n${publicText(projection.result ?? operation.goal, "projection.result")}\n\n## Current behavior\n\n${publicText(projection.current_behavior ?? "The current behavior is defined by the repository's existing contract.", "projection.current_behavior")}\n\n## Desired behavior\n\n${publicText(projection.desired_behavior ?? operation.goal, "projection.desired_behavior")}\n\n## Key interfaces\n\n${listText(projection.key_interfaces, "- Canonical TaskIntent acceptance and Kernel lifecycle remain authoritative.")}\n\n## Acceptance criteria\n\n${acceptance}\n\n## Verification\n\n${publicText(projection.verification ?? "Run the focused acceptance verification declared by the TaskIntent.", "projection.verification")}\n\n## Blocked by\n\n${projection.blocked_by?.length ? projection.blocked_by.map((id) => `- \`${identifier(id, "blocked_by task_id")}\``).join("\n") : "None"}\n\n## Out of scope\n\n${listText(projection.out_of_scope, "- Scope not declared by the validated TaskIntent.")}\n\n## Agent handoff\n\n${publicText(projection.agent_handoff ?? "Implement only the bounded TaskIntent result and run the focused checks. Do not widen scope or treat GitHub as authorization.", "projection.agent_handoff")}\n\n## Lifecycle\n\n- **Open** means this Task still needs attention; it does not mean the Task is authorized or executing.\n- Only a fresh claimless terminal projection can close this Issue: \`done\` becomes **Completed**, and \`stopped\` becomes **Not planned**.\n\n## Authority boundary\n\nThis Issue is outbound visibility only. GitHub state never changes TaskIntent, TaskRecord, QA, Review, authorization, or Kernel settlement. Internal role prompts, tool policies, review gates, model reservations, and prompt digests are not part of this external handoff.\n`;
624
675
  }
625
676
 
626
677
  async function upsertTask(
@@ -655,7 +706,7 @@ async function upsertTask(
655
706
  if (!("owned" in ownership)) return ownership;
656
707
  blockers.push(blocker.issue);
657
708
  }
658
- const body = childBody(source.repository, operation);
709
+ const body = childBody(source.repository, operation, parent.issue);
659
710
  const oversized = bodyLimitFailure(operation.op, body, MAX_TERMINAL_SUFFIX_BYTES);
660
711
  if (oversized) return oversized;
661
712
  const title = issueTitle(`${operation.initiative_id}/${operation.slice_id}`, operation.projection?.result ?? operation.goal);
@@ -870,6 +921,7 @@ function normalizeInitiativeProjection(value: InitiativeProjection | undefined):
870
921
  return {
871
922
  problem: value.problem === undefined ? undefined : projectionText(value.problem, "projection.problem"),
872
923
  result: value.result === undefined ? undefined : projectionText(value.result, "projection.result"),
924
+ design: value.design === undefined ? undefined : projectionText(value.design, "projection.design"),
873
925
  decisions: normalizedProjectionList(value.decisions, "projection.decisions", 500),
874
926
  testing_strategy: value.testing_strategy === undefined ? undefined : projectionText(value.testing_strategy, "projection.testing_strategy"),
875
927
  out_of_scope: normalizedProjectionList(value.out_of_scope, "projection.out_of_scope", 500),
@@ -949,12 +1001,265 @@ export async function runGithubTrackerOperation(
949
1001
  }
950
1002
  }
951
1003
 
952
- function valueAfter(args: string[], name: string): string | undefined {
953
- const index = args.indexOf(name);
954
- return index === -1 ? undefined : args[index + 1];
1004
+ function publicationResult(
1005
+ status: TrackerStatus,
1006
+ message: string,
1007
+ initiative?: GithubTrackerResult,
1008
+ tasks: GithubInitiativePublicationResult["tasks"] = [],
1009
+ execution?: GithubInitiativePublicationResult["execution"],
1010
+ ): GithubInitiativePublicationResult {
1011
+ return {
1012
+ contract: "immune_brain/github_initiative_publication/v1",
1013
+ operation: "publish-initiative",
1014
+ status,
1015
+ ...(initiative ? { initiative } : {}),
1016
+ tasks,
1017
+ ...(execution ? { execution } : {}),
1018
+ message: redactGithubDiagnostic(message),
1019
+ };
1020
+ }
1021
+
1022
+ interface PreparedPublicationTask {
1023
+ operation: Extract<TrackerOperation, { op: "upsert-task" }>;
1024
+ intent_path: string;
1025
+ intent_content_hash: string;
1026
+ }
1027
+
1028
+ function publicationPlan(operations: Array<Extract<TrackerOperation, { op: "upsert-task" }>>): {
1029
+ order: Array<Extract<TrackerOperation, { op: "upsert-task" }>>;
1030
+ parallel_groups: string[][];
1031
+ } {
1032
+ const remaining = new Set(operations.map((operation) => operation.task_id));
1033
+ const done = new Set<string>();
1034
+ const order: Array<Extract<TrackerOperation, { op: "upsert-task" }>> = [];
1035
+ const parallelGroups: string[][] = [];
1036
+ while (remaining.size) {
1037
+ const ready = operations.filter((operation) => remaining.has(operation.task_id)
1038
+ && (operation.projection?.blocked_by ?? []).every((taskId) => done.has(taskId)));
1039
+ if (!ready.length) throw new Error("Initiative Task dependencies must form an acyclic graph");
1040
+ parallelGroups.push(ready.map((operation) => operation.task_id));
1041
+ for (const operation of ready) {
1042
+ remaining.delete(operation.task_id);
1043
+ done.add(operation.task_id);
1044
+ order.push(operation);
1045
+ }
1046
+ }
1047
+ return { order, parallel_groups: parallelGroups };
1048
+ }
1049
+
1050
+ function preflightPublication(root: string, input: InitiativePublicationInput): {
1051
+ initiative: Extract<TrackerOperation, { op: "create-initiative" }>;
1052
+ order: Array<Extract<TrackerOperation, { op: "upsert-task" }>>;
1053
+ parallel_groups: string[][];
1054
+ intent_bindings: Map<string, Pick<PreparedPublicationTask, "intent_path" | "intent_content_hash">>;
1055
+ } {
1056
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("publication must be an object");
1057
+ if (!Array.isArray(input.tasks) || input.tasks.length < 2)
1058
+ throw new Error("a complete Initiative publication requires at least two Tasks");
1059
+ if (!input.projection || typeof input.projection !== "object" || Array.isArray(input.projection))
1060
+ throw new Error("publication projection must be an object");
1061
+ for (const field of ["problem", "result", "design"] as const) {
1062
+ if (input.projection[field] === undefined)
1063
+ throw new Error(`a complete Initiative publication requires projection.${field}`);
1064
+ }
1065
+ const publications = input.tasks.map((task, index) => {
1066
+ if (!task || typeof task !== "object" || Array.isArray(task)) throw new Error(`tasks[${index}] must be an object`);
1067
+ if (typeof task.intent !== "string") throw new Error(`tasks[${index}].intent must be a string`);
1068
+ return taskPublication(root, input.initiative_id, task.slice_id, task.intent, task.projection);
1069
+ });
1070
+ const operations = publications.map((publication) => publication.operation);
1071
+ const taskIds = new Set<string>();
1072
+ const sliceIds = new Set<string>();
1073
+ for (const operation of operations) {
1074
+ if (taskIds.has(operation.task_id)) throw new Error(`duplicate Task id: ${operation.task_id}`);
1075
+ if (sliceIds.has(operation.slice_id)) throw new Error(`duplicate Slice id: ${operation.slice_id}`);
1076
+ taskIds.add(operation.task_id);
1077
+ sliceIds.add(operation.slice_id);
1078
+ }
1079
+ for (const operation of operations) {
1080
+ for (const blocker of operation.projection?.blocked_by ?? []) {
1081
+ if (!taskIds.has(blocker)) throw new Error(`Task ${operation.task_id} depends on ${blocker}, which is outside the complete Initiative batch`);
1082
+ }
1083
+ }
1084
+ const plan = publicationPlan(operations);
1085
+ const initiative = validateOperation({
1086
+ op: "create-initiative",
1087
+ initiative_id: input.initiative_id,
1088
+ goal: input.goal,
1089
+ projection: input.projection,
1090
+ slices: operations.map((operation) => ({
1091
+ id: operation.slice_id,
1092
+ goal: operation.goal,
1093
+ result: operation.projection?.result,
1094
+ blocked_by: operation.projection?.blocked_by,
1095
+ })),
1096
+ }) as Extract<TrackerOperation, { op: "create-initiative" }>;
1097
+ const intentBindings = new Map(publications.map((publication) => [publication.operation.task_id, {
1098
+ intent_path: publication.intent_path,
1099
+ intent_content_hash: publication.intent_content_hash,
1100
+ }]));
1101
+ return { initiative, ...plan, intent_bindings: intentBindings };
1102
+ }
1103
+
1104
+ function publicationIntentDrift(
1105
+ root: string,
1106
+ bindings: Map<string, Pick<PreparedPublicationTask, "intent_path" | "intent_content_hash">>,
1107
+ taskIds: Iterable<string> = bindings.keys(),
1108
+ ): string | null {
1109
+ for (const taskId of taskIds) {
1110
+ const expected = bindings.get(taskId);
1111
+ if (!expected) return `TaskIntent ${taskId} is missing its publication binding`;
1112
+ try {
1113
+ const current = readTaskIntent(root, taskId);
1114
+ if (current.intent_ref.path !== expected.intent_path || current.content_hash !== expected.intent_content_hash)
1115
+ return `TaskIntent ${taskId} changed during Initiative publication`;
1116
+ } catch (error) {
1117
+ return `TaskIntent ${taskId} became unreadable during Initiative publication: ${error instanceof Error ? error.message : String(error)}`;
1118
+ }
1119
+ }
1120
+ return null;
1121
+ }
1122
+
1123
+ function publicationIssueDrift(expected: {
1124
+ issue_number?: number;
1125
+ issue_url?: string;
1126
+ node_id?: string;
1127
+ }, actual: GithubIssue, title: string, body: string, label: string): string | null {
1128
+ if (expected.issue_number !== actual.number || expected.issue_url !== actual.url || expected.node_id !== String(actual.id))
1129
+ return `${label} identity changed during Initiative publication`;
1130
+ if (actual.state !== "open") return `${label} is no longer open`;
1131
+ if (actual.title !== title || actual.body !== body) return `${label} content changed during Initiative publication`;
1132
+ return null;
955
1133
  }
956
1134
 
957
- function taskPublication(root: string, initiativeId: string, sliceId: string, intentPath: string, projection?: TaskProjection): Extract<TrackerOperation, { op: "upsert-task" }> {
1135
+ export async function runGithubInitiativePublication(
1136
+ root: string,
1137
+ input: InitiativePublicationInput,
1138
+ gh: GhTransport = createGhTransport(),
1139
+ ): Promise<GithubInitiativePublicationResult> {
1140
+ const absoluteRoot = resolve(root);
1141
+ let prepared: ReturnType<typeof preflightPublication>;
1142
+ try {
1143
+ prepared = preflightPublication(absoluteRoot, input);
1144
+ } catch (error) {
1145
+ return publicationResult("permanent_failure", error instanceof Error ? error.message : String(error));
1146
+ }
1147
+ const conflict = carrierConflict(absoluteRoot, "create-initiative", prepared.initiative.initiative_id);
1148
+ if (conflict) return publicationResult(conflict.status, conflict.message, conflict);
1149
+ const initial = await snapshot(absoluteRoot, gh, "create-initiative");
1150
+ if ("contract" in initial) return publicationResult(initial.status, initial.message, initial);
1151
+ const initialParent = initiativeLookup(initial.issues, initial.repository.id, prepared.initiative.initiative_id);
1152
+ if (initialParent.kind === "ambiguous") return publicationResult("ambiguous_remote_state", initialParent.message);
1153
+ const parentForPreflight = initialParent.kind === "found" ? initialParent.issue : {
1154
+ id: Number.MAX_SAFE_INTEGER,
1155
+ number: Number.MAX_SAFE_INTEGER,
1156
+ url: `https://github.com/${initial.repository.name_with_owner}/issues/${Number.MAX_SAFE_INTEGER}`,
1157
+ title: "",
1158
+ body: "",
1159
+ state: "open" as const,
1160
+ state_reason: null,
1161
+ };
1162
+ const parentBodyFailure = bodyLimitFailure("create-initiative", createInitiativeBody(initial.repository, prepared.initiative));
1163
+ if (parentBodyFailure) return publicationResult(parentBodyFailure.status, parentBodyFailure.message, parentBodyFailure);
1164
+ for (const operation of prepared.order) {
1165
+ const childFailure = bodyLimitFailure("upsert-task", childBody(initial.repository, operation, parentForPreflight), MAX_TERMINAL_SUFFIX_BYTES);
1166
+ if (childFailure) return publicationResult(childFailure.status, childFailure.message, childFailure);
1167
+ }
1168
+
1169
+ const beforeParentWrite = publicationIntentDrift(absoluteRoot, prepared.intent_bindings);
1170
+ if (beforeParentWrite) return publicationResult("ambiguous_remote_state", beforeParentWrite);
1171
+ const parentResult = await runGithubTrackerOperation(absoluteRoot, prepared.initiative, gh);
1172
+ if (!isSuccessfulTrackerStatus(parentResult.status))
1173
+ return publicationResult(parentResult.status, parentResult.message, parentResult);
1174
+ const taskResults: GithubInitiativePublicationResult["tasks"] = [];
1175
+ for (const operation of prepared.order) {
1176
+ const intentDrift = publicationIntentDrift(absoluteRoot, prepared.intent_bindings, [operation.task_id]);
1177
+ if (intentDrift) return publicationResult("ambiguous_remote_state", intentDrift, parentResult, taskResults);
1178
+ const taskResult = await runGithubTrackerOperation(absoluteRoot, operation, gh);
1179
+ taskResults.push({
1180
+ task_id: operation.task_id,
1181
+ slice_id: operation.slice_id,
1182
+ status: taskResult.status,
1183
+ ...(taskResult.issue_number === undefined ? {} : { issue_number: taskResult.issue_number }),
1184
+ ...(taskResult.issue_url === undefined ? {} : { issue_url: taskResult.issue_url }),
1185
+ ...(taskResult.node_id === undefined ? {} : { node_id: taskResult.node_id }),
1186
+ });
1187
+ if (!isSuccessfulTrackerStatus(taskResult.status))
1188
+ return publicationResult(taskResult.status, taskResult.message, parentResult, taskResults);
1189
+ }
1190
+
1191
+ const finalIntentDrift = publicationIntentDrift(absoluteRoot, prepared.intent_bindings);
1192
+ if (finalIntentDrift) return publicationResult("ambiguous_remote_state", finalIntentDrift, parentResult, taskResults);
1193
+ const finalSource = await snapshot(absoluteRoot, gh, "upsert-task");
1194
+ if ("contract" in finalSource) return publicationResult(finalSource.status, finalSource.message, parentResult, taskResults);
1195
+ const parent = initiativeLookup(finalSource.issues, finalSource.repository.id, prepared.initiative.initiative_id);
1196
+ if (parent.kind !== "found") return publicationResult("ambiguous_remote_state", parent.kind === "ambiguous" ? parent.message : "published Initiative Parent disappeared", parentResult, taskResults);
1197
+ const parentDrift = publicationIssueDrift(
1198
+ parentResult,
1199
+ parent.issue,
1200
+ issueTitle(prepared.initiative.initiative_id, prepared.initiative.projection?.result ?? prepared.initiative.goal),
1201
+ createInitiativeBody(finalSource.repository, prepared.initiative),
1202
+ "Initiative Parent",
1203
+ );
1204
+ if (parentDrift) return publicationResult("ambiguous_remote_state", parentDrift, parentResult, taskResults);
1205
+ const resultByTask = new Map(taskResults.map((task) => [task.task_id, task]));
1206
+ const expectedNumbers: number[] = [];
1207
+ for (const operation of prepared.order) {
1208
+ const child = ownedTaskLookup(finalSource.issues, finalSource.repository.id, operation.task_id, operation.initiative_id, operation.slice_id);
1209
+ if (child.kind !== "found") return publicationResult("ambiguous_remote_state", child.kind === "ambiguous" ? child.message : `published Task ${operation.task_id} disappeared`, parentResult, taskResults);
1210
+ const childResult = resultByTask.get(operation.task_id);
1211
+ if (!childResult) return publicationResult("ambiguous_remote_state", `published Task ${operation.task_id} has no batch result`, parentResult, taskResults);
1212
+ const childDrift = publicationIssueDrift(
1213
+ childResult,
1214
+ child.issue,
1215
+ issueTitle(`${operation.initiative_id}/${operation.slice_id}`, operation.projection?.result ?? operation.goal),
1216
+ childBody(finalSource.repository, operation, parent.issue),
1217
+ `Task ${operation.task_id}`,
1218
+ );
1219
+ if (childDrift) return publicationResult("ambiguous_remote_state", childDrift, parentResult, taskResults);
1220
+ expectedNumbers.push(child.issue.number);
1221
+ const ownership = await confirmTerminalOwnership(absoluteRoot, gh, "upsert-task", finalSource, child.issue);
1222
+ if (!("owned" in ownership)) return publicationResult(ownership.status, ownership.message, parentResult, taskResults);
1223
+ const blockers: GithubIssue[] = [];
1224
+ for (const blockerId of operation.projection?.blocked_by ?? []) {
1225
+ const blocker = taskLookup(finalSource.issues, finalSource.repository.id, blockerId);
1226
+ if (blocker.kind !== "found") return publicationResult("ambiguous_remote_state", `published blocker ${blockerId} disappeared`, parentResult, taskResults);
1227
+ blockers.push(blocker.issue);
1228
+ }
1229
+ const dependencies = await confirmBlockedBy(absoluteRoot, gh, "upsert-task", finalSource.repository, child.issue.number, blockers);
1230
+ if (!("complete" in dependencies)) return publicationResult(dependencies.status, dependencies.message, parentResult, taskResults);
1231
+ if (!dependencies.complete) return publicationResult("ambiguous_remote_state", `Task ${operation.task_id} has incomplete blocking relations`, parentResult, taskResults);
1232
+ }
1233
+ const attached = await readSubIssueNumbers(absoluteRoot, gh, "upsert-task", finalSource.repository, parent.issue.number);
1234
+ if (!Array.isArray(attached)) return publicationResult(attached.status, attached.message, parentResult, taskResults);
1235
+ const sortedAttached = [...attached].sort((left, right) => left - right);
1236
+ const sortedExpected = [...expectedNumbers].sort((left, right) => left - right);
1237
+ if (sortedAttached.length !== sortedExpected.length || sortedAttached.some((number, index) => number !== sortedExpected[index]))
1238
+ return publicationResult("ambiguous_remote_state", "Initiative Parent Sub-issues do not match the complete publication batch", parentResult, taskResults);
1239
+ const statuses = [parentResult.status, ...taskResults.map((task) => task.status)];
1240
+ const status: TrackerStatus = statuses.every((item) => item === "created")
1241
+ ? "created"
1242
+ : statuses.every((item) => item === "already_current") ? "already_current" : "updated";
1243
+ const issueByTask = new Map(taskResults.map((task) => [task.task_id, task.issue_number]));
1244
+ if ([...issueByTask.values()].some((number) => number === undefined))
1245
+ return publicationResult("ambiguous_remote_state", "published Task result is missing an Issue number", parentResult, taskResults);
1246
+ const issueNumber = (taskId: string): number => issueByTask.get(taskId)!;
1247
+ const firstTaskId = prepared.order[0].task_id;
1248
+ return publicationResult(status, "complete Initiative Parent, Children, and dependency graph published", parentResult, taskResults, {
1249
+ recommended_first_task_id: firstTaskId,
1250
+ recommended_first_issue_number: issueNumber(firstTaskId),
1251
+ order: prepared.order.map((operation) => operation.task_id),
1252
+ issue_order: prepared.order.map((operation) => issueNumber(operation.task_id)),
1253
+ parallel_groups: prepared.parallel_groups,
1254
+ parallel_issue_groups: prepared.parallel_groups.map((group) => group.map(issueNumber)),
1255
+ });
1256
+ }
1257
+
1258
+ function isSuccessfulTrackerStatus(status: TrackerStatus): boolean {
1259
+ return status === "created" || status === "updated" || status === "already_current";
1260
+ }
1261
+
1262
+ function taskPublication(root: string, initiativeId: string, sliceId: string, intentPath: string, projection?: TaskProjection): PreparedPublicationTask {
958
1263
  const absoluteRoot = resolve(root);
959
1264
  const absolutePath = resolve(absoluteRoot, intentPath);
960
1265
  const rel = relative(absoluteRoot, absolutePath);
@@ -966,14 +1271,18 @@ function taskPublication(root: string, initiativeId: string, sliceId: string, in
966
1271
  if (read.intent_ref.path !== rel) throw new Error("TaskIntent path must match its canonical sidecar path");
967
1272
  const intent = read.intent;
968
1273
  return {
969
- op: "upsert-task",
970
- initiative_id: initiativeId,
971
- task_id: intent.task_id,
972
- slice_id: sliceId,
973
- goal: intent.goal,
974
- risk: intent.risk,
975
- acceptance: intent.acceptance.map((item) => ({ id: item.id, summary: item.assertion })),
976
- projection,
1274
+ operation: validateOperation({
1275
+ op: "upsert-task",
1276
+ initiative_id: initiativeId,
1277
+ task_id: intent.task_id,
1278
+ slice_id: sliceId,
1279
+ goal: intent.goal,
1280
+ risk: intent.risk,
1281
+ acceptance: intent.acceptance.map((item) => ({ id: item.id, summary: item.assertion })),
1282
+ projection,
1283
+ }) as Extract<TrackerOperation, { op: "upsert-task" }>,
1284
+ intent_path: read.intent_ref.path,
1285
+ intent_content_hash: read.content_hash,
977
1286
  };
978
1287
  }
979
1288
 
@@ -985,29 +1294,16 @@ export async function runGithubTrackerCli(
985
1294
  const op = args[0];
986
1295
  if (!args.includes("--json"))
987
1296
  return { stdout: "", stderr: "invalid_tracker_command: --json is required\n", returncode: 2 };
988
- let operation: TrackerOperation;
989
1297
  try {
990
- if (op === "create-initiative" && args.length === 3 && args[1] === "--stdin") {
991
- const raw = JSON.parse((options.stdin ?? (() => readFileSync(0, "utf8")))()) as Record<string, unknown>;
992
- operation = {
993
- op: "create-initiative",
994
- initiative_id: raw.initiative_id as string,
995
- goal: raw.goal as string,
996
- slices: raw.slices as InitiativeSlice[],
997
- projection: raw.projection as InitiativeProjection | undefined,
998
- };
999
- } else if (op === "upsert-task" && (args.length === 8 || args.length === 10)) {
1000
- const initiative = valueAfter(args, "--initiative-id");
1001
- const slice = valueAfter(args, "--slice-id");
1002
- const intent = valueAfter(args, "--intent");
1003
- if (!initiative || !slice || !intent)
1004
- throw new Error("upsert-task requires --initiative-id, --slice-id, and --intent");
1005
- const rawProjection = valueAfter(args, "--projection-json");
1006
- const projection = rawProjection ? JSON.parse(rawProjection) as TaskProjection : undefined;
1007
- operation = taskPublication(root, initiative, slice, intent, projection);
1008
- } else {
1009
- throw new Error("use create-initiative --stdin --json or upsert-task --initiative-id <id> --slice-id <id> --intent <path> [--projection-json <json>] --json");
1010
- }
1298
+ if (op !== "publish-initiative" || args.length !== 3 || args[1] !== "--stdin")
1299
+ throw new Error("use publish-initiative --stdin --json");
1300
+ const raw = JSON.parse((options.stdin ?? (() => readFileSync(0, "utf8")))()) as InitiativePublicationInput;
1301
+ const published = await runGithubInitiativePublication(root, raw, options.gh);
1302
+ return {
1303
+ stdout: `${JSON.stringify(published, null, 2)}\n`,
1304
+ stderr: "",
1305
+ returncode: isSuccessfulTrackerStatus(published.status) ? 0 : 1,
1306
+ };
1011
1307
  } catch (error) {
1012
1308
  return {
1013
1309
  stdout: "",
@@ -1015,10 +1311,4 @@ export async function runGithubTrackerCli(
1015
1311
  returncode: 2,
1016
1312
  };
1017
1313
  }
1018
- const projected = await runGithubTrackerOperation(root, operation, options.gh);
1019
- return {
1020
- stdout: `${JSON.stringify(projected, null, 2)}\n`,
1021
- stderr: "",
1022
- returncode: projected.status === "permanent_failure" || projected.status === "ambiguous_remote_state" ? 1 : 0,
1023
- };
1024
1314
  }
@@ -261,11 +261,10 @@ async function main(argv: string[]): Promise<number> {
261
261
  {
262
262
  name: "imm-tracker",
263
263
  description:
264
- "Opt-in, one-way GitHub Issue projection. Creates a Parent once, never rewrites or closes it. Never grants or consumes Kernel authority.",
264
+ "Opt-in, one-way GitHub Issue projection. Publishes one complete Parent/Child graph and execution recommendation. Never grants or consumes Kernel authority.",
265
265
  json_output: true,
266
266
  examples: [
267
- "imm-tracker create-initiative --stdin --json",
268
- "imm-tracker upsert-task --initiative-id <id> --slice-id <id> --intent docs/plans/<task-id>.intent.json --json",
267
+ "imm-tracker publish-initiative --stdin --json",
269
268
  ],
270
269
  },
271
270
  ],
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: imm-agent-doc-maintain
3
+ description: Use to minimize tracked AGENTS.md, CLAUDE.md, and GEMINI.md files to necessary non-discoverable context after an explicit, hash-bound, user-approved manifest; never installs or validates project contracts.
4
+ ---
5
+
6
+ # Immune-Brain: Agent Doc Maintain
7
+
8
+ Load [`../../dist/imm-agent-doc-maintain.md`](../../dist/imm-agent-doc-maintain.md),
9
+ then minimize tracked agent instruction files to necessary non-discoverable
10
+ context. This is a standalone host-native maintenance entry, not a Managed Path
11
+ continuation and not an `imm-loop` internal-role dispatch.
@@ -14,7 +14,7 @@ Agent envelope exactly. Brainstorm and Planner use the same Tool for bounded
14
14
  `arch-explorer` and explicit-lens `advisory-reviewer` dispatches. Loop may
15
15
  dispatch `compounder` only when a closed Step supplies structured evidence for
16
16
  a reusable Learning; routine work without that evidence returns `next: none`
17
- and creates no Learning. Do not discover or load a Pi Skill for these roles. The Managed Path public entries remain `imm-brainstorm`, `imm-planner`, and `imm-loop`; standalone `imm-pr-fix` and `imm-doc-prune` are host-native and are never dispatched as the Loop role.
17
+ and creates no Learning. Do not discover or load a Pi Skill for these roles. The Managed Path public entries remain `imm-brainstorm`, `imm-planner`, and `imm-loop`; standalone `imm-pr-fix`, `imm-doc-prune`, and `imm-agent-doc-maintain` are host-native and are never dispatched as the Loop role.
18
18
  Subagent Dispatch Protocol](../../dist/docs/reference/subagent-dispatch-protocol.md#authorization-authority).
19
19
  All internal Agent dispatch envelopes use `run_in_background: false` and
20
20
  return a direct result to the Parent before any workflow mutation.