immune-brain 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/README.zh-CN.md +4 -2
- package/package.json +1 -1
- package/plugins/immune-brain/.claude-plugin/plugin.json +1 -1
- package/plugins/immune-brain/dist/docs/reference/immune-brain-config.md +8 -4
- package/plugins/immune-brain/dist/imm-agent-doc-maintain.md +137 -0
- package/plugins/immune-brain/dist/imm-loop.md +1 -1
- package/plugins/immune-brain/dist/imm-planner.md +56 -47
- package/plugins/immune-brain/dist/registry.yaml +9 -0
- package/plugins/immune-brain/runtime/github_issue_tracker.ts +341 -51
- package/plugins/immune-brain/runtime/v4_runtime.ts +2 -3
- package/plugins/immune-brain/skills/imm-agent-doc-maintain/SKILL.md +11 -0
- package/plugins/immune-brain/skills/imm-loop/SKILL.md +1 -1
- package/plugins/immune-brain/skills/imm-planner/SKILL.md +56 -47
- package/plugins/immune-brain/skills/registry.yaml +9 -0
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Pi and Claude Code are the supported hosts. Undeclared adapters remain unsupport
|
|
|
23
23
|
- [Installation](#installation)
|
|
24
24
|
- [Quick Start](#quick-start)
|
|
25
25
|
- [How to Use](#how-to-use)
|
|
26
|
-
- [The
|
|
26
|
+
- [The 6 Skills](#the-6-skills)
|
|
27
27
|
- [Lifecycle](#lifecycle)
|
|
28
28
|
- [Configuration](#configuration)
|
|
29
29
|
- [Project Layout](#project-layout)
|
|
@@ -87,12 +87,13 @@ You rarely need to remember skill names — **just describe your intent**:
|
|
|
87
87
|
| Plan is approved, ready to build | "Start building" / `imm-loop` | → Executor builds, QA verifies, Review checks |
|
|
88
88
|
| PR needs fixes after review | `imm-pr-fix` on that PR | → Standalone repair, no new managed task |
|
|
89
89
|
| Docs are stale after changes | `imm-doc-prune` with manifest | → Prunes only approved stale docs |
|
|
90
|
+
| Agent instruction files are bloated | `imm-agent-doc-maintain` with manifest | → Keeps only necessary non-discoverable rules |
|
|
90
91
|
|
|
91
92
|
> **Rule:** Managed work (brainstorm → plan → loop) starts only from explicit `imm-brainstorm`, `imm-planner`, or `imm-loop`. Ordinary Q&A or read-only requests stay host-native and never enroll a task.
|
|
92
93
|
|
|
93
94
|
---
|
|
94
95
|
|
|
95
|
-
## The
|
|
96
|
+
## The 6 Skills
|
|
96
97
|
|
|
97
98
|
| Skill | Type | When to use | What it does |
|
|
98
99
|
|---|---|---|---|
|
|
@@ -101,6 +102,7 @@ You rarely need to remember skill names — **just describe your intent**:
|
|
|
101
102
|
| `imm-loop` | Managed coordinator | Plan is validated | Drives execution → QA → Review → completion via foreground tools |
|
|
102
103
|
| `imm-pr-fix` | Standalone | CI failed / review comments on a PR | Repairs one PR in place, no managed authority |
|
|
103
104
|
| `imm-doc-prune` | Standalone | Stale current docs | Deletes only the hash-approved manifest entries |
|
|
105
|
+
| `imm-agent-doc-maintain` | Standalone | Bloated agent instructions | Minimizes tracked AGENTS/CLAUDE/GEMINI.md to necessary context |
|
|
104
106
|
|
|
105
107
|
Internal roles (Executor, QA, Review, Compounder) are dispatched by `imm-loop` — you never invoke them directly.
|
|
106
108
|
|
package/README.zh-CN.md
CHANGED
|
@@ -23,7 +23,7 @@ Pi 与 Claude Code 是支持的宿主。未声明的适配器仍不受支持。C
|
|
|
23
23
|
- [安装](#安装)
|
|
24
24
|
- [快速开始](#快速开始)
|
|
25
25
|
- [如何使用](#如何使用)
|
|
26
|
-
- [
|
|
26
|
+
- [6 个 Skills](#6-个-skills)
|
|
27
27
|
- [生命周期](#生命周期)
|
|
28
28
|
- [配置](#配置)
|
|
29
29
|
- [项目结构](#项目结构)
|
|
@@ -87,12 +87,13 @@ QA 与 Review 以 foreground Tool 形式运行并回传结果,返回 `phase=do
|
|
|
87
87
|
| 计划已确认,准备开干 | "开始构建" / `imm-loop` | → Executor 构建 → QA 验证 → Review 审查 |
|
|
88
88
|
| PR 被评论 / CI 挂了 | 对该 PR 使用 `imm-pr-fix` | → 独立修复,不创建新 managed 任务 |
|
|
89
89
|
| 文档过时需要清理 | `imm-doc-prune` + manifest | → 仅删除已审批的过时文档 |
|
|
90
|
+
| Agent instruction 文件膨胀 | `imm-agent-doc-maintain` + manifest | → 只保留不可直接推导的必要规则 |
|
|
90
91
|
|
|
91
92
|
> **规则:** Managed 工作流(brainstorm → plan → loop)仅由显式的 `imm-brainstorm`、`imm-planner`、`imm-loop` 启动。普通问答、只读解释不会 Enrollment。
|
|
92
93
|
|
|
93
94
|
---
|
|
94
95
|
|
|
95
|
-
##
|
|
96
|
+
## 6 个 Skills
|
|
96
97
|
|
|
97
98
|
| Skill | 类型 | 何时使用 | 职责 |
|
|
98
99
|
|---|---|---|---|
|
|
@@ -101,6 +102,7 @@ QA 与 Review 以 foreground Tool 形式运行并回传结果,返回 `phase=do
|
|
|
101
102
|
| `imm-loop` | Managed 协调器 | 计划已验证 | 通过 foreground Tools 协调 执行 → QA → Review → 收尾 |
|
|
102
103
|
| `imm-pr-fix` | 独立 | PR 需修复 | 原地修复单个 PR,不触及 managed authority |
|
|
103
104
|
| `imm-doc-prune` | 独立 | 清理过时文档 | 仅删除哈希绑定的 manifest 条目 |
|
|
105
|
+
| `imm-agent-doc-maintain` | 独立 | Agent instruction 膨胀 | 将 tracked AGENTS/CLAUDE/GEMINI.md 压到最小必要上下文 |
|
|
104
106
|
|
|
105
107
|
Executor、QA、Review、Compounder 等为 `imm-loop` 内部调度的角色,无需手动调用。
|
|
106
108
|
|
package/package.json
CHANGED
|
@@ -35,10 +35,14 @@ TaskRecords. Set one of these fixed directives in `AGENTS.md`:
|
|
|
35
35
|
|
|
36
36
|
A repository directive overrides the global directive. A configured `github`
|
|
37
37
|
default is standing opt-in for GitHub projection, but the literal user still
|
|
38
|
-
confirms the Initiative name
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
confirms the Initiative name, immutable slug, complete Parent/Child decomposition,
|
|
39
|
+
granularity, and dependencies before the first remote mutation. Planner reports
|
|
40
|
+
the selected carrier and its source. After confirmation it publishes the complete
|
|
41
|
+
Issue graph in one idempotent batch and reports the recommended first unblocked
|
|
42
|
+
Task, stable dependency order, and parallel groups. Projection failure is reported
|
|
43
|
+
with a batch retry action and does not invalidate the authored planning files,
|
|
44
|
+
but it blocks Enrollment and execution handoff for that Initiative until the same
|
|
45
|
+
complete batch succeeds. It never silently switches carrier.
|
|
42
46
|
|
|
43
47
|
## Other Preferences
|
|
44
48
|
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
Minimize tracked agent instruction files in one Git repository to the smallest
|
|
9
|
+
set of persistent rules that are non-obvious, repeatable, stable, and costly to
|
|
10
|
+
violate. This is a standalone host-native maintenance entry, not a Managed Path
|
|
11
|
+
continuation and not an `imm-loop` internal-role dispatch.
|
|
12
|
+
|
|
13
|
+
## Authority Boundary
|
|
14
|
+
|
|
15
|
+
This Skill maintains agent instruction files without creating or mutating
|
|
16
|
+
TaskIntent, TaskRecord, Kernel, Spec, or Plan authority. An already active
|
|
17
|
+
Managed task remains owned by `imm-loop`. Read-only inventory and manifest
|
|
18
|
+
production remain available. Classify each candidate overlapping the active
|
|
19
|
+
TaskIntent `scope_hint` as `BLOCKED_ACTIVE_SCOPE` and continue auditing
|
|
20
|
+
unaffected candidates. If the routing owner or scope cannot be read reliably,
|
|
21
|
+
fail closed for mutation. This Skill does not invoke `imm-doc-prune`; stale
|
|
22
|
+
deletion of evidence-proven content remains that Skill's separate job.
|
|
23
|
+
|
|
24
|
+
## Invocation
|
|
25
|
+
|
|
26
|
+
Requires explicit invocation: `imm-agent-doc-maintain` or
|
|
27
|
+
`/imm-agent-doc-maintain`. Ordinary "is this AGENTS.md too long?" questions stay
|
|
28
|
+
host-native and do not enter this Skill.
|
|
29
|
+
|
|
30
|
+
- `imm-agent-doc-maintain audit`: read-only. Produce the manifest and stop.
|
|
31
|
+
- `imm-agent-doc-maintain`: produce the manifest, then wait for exact manifest
|
|
32
|
+
approval before any mutation.
|
|
33
|
+
|
|
34
|
+
No automatic invocation. No daemon, no telemetry, no automatic learning, no
|
|
35
|
+
cron, no CI, no allowlist, no runtime, no persistent report, and no automatic
|
|
36
|
+
commit.
|
|
37
|
+
|
|
38
|
+
## Ordered Maintenance Protocol
|
|
39
|
+
|
|
40
|
+
1. **Establish repository safety.** Mutation requires a Git worktree. Only
|
|
41
|
+
tracked regular files named exactly `AGENTS.md`, `CLAUDE.md`, or `GEMINI.md`,
|
|
42
|
+
at the repository root or in nested tracked directories, are candidates.
|
|
43
|
+
Git-tracked symlinks (`120000`) and other non-regular modes are `BLOCKED`
|
|
44
|
+
before inventory, reading, or mutation, even when the basename matches.
|
|
45
|
+
User-level and external files, including `~/.pi/agent/AGENTS.md` and
|
|
46
|
+
`~/.agents/`, are never inventoried or modified. Record the candidate path,
|
|
47
|
+
blob/content hash, tracked status, candidate-local worktree status, Git file
|
|
48
|
+
mode, line count, and byte count. Unrelated dirty files do not block the run;
|
|
49
|
+
a dirty or untracked candidate is `BLOCKED`.
|
|
50
|
+
|
|
51
|
+
2. **Resolve Managed ownership.** Read the existing routing projection without
|
|
52
|
+
creating authority. An already active Managed task remains owned by
|
|
53
|
+
`imm-loop`. A candidate overlapping its `scope_hint` is
|
|
54
|
+
`BLOCKED_ACTIVE_SCOPE`; continue auditing unaffected candidates. If the
|
|
55
|
+
routing owner or scope cannot be read reliably, fail closed for mutation.
|
|
56
|
+
|
|
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.
|
|
61
|
+
|
|
62
|
+
4. **Build repository facts.** Resolve current truth in this order:
|
|
63
|
+
executable/public registries, package exports, CLI/runtime entrypoints;
|
|
64
|
+
behavior tests; active Spec/TaskIntent; current `CONTEXT.md`/ADR/
|
|
65
|
+
reference/README; Solution/Brainstorm/archive. Repository facts are evidence
|
|
66
|
+
for classification, not a reason to copy discoverable context into
|
|
67
|
+
instructions.
|
|
68
|
+
|
|
69
|
+
5. **Apply the four-part persistent-rule value gate.** A retained or newly
|
|
70
|
+
proposed persistent rule must be all of: non-obvious from ordinary
|
|
71
|
+
repository inspection; plausibly repeatable across later tasks; stable beyond
|
|
72
|
+
the current task; and costly to violate. Repository overviews, directory
|
|
73
|
+
listings, technology summaries, discoverable command inventories, vague
|
|
74
|
+
exhortations, and unconditional broad exploration or testing fail this gate
|
|
75
|
+
unless the manifest proves they encode a hidden behavioral constraint.
|
|
76
|
+
|
|
77
|
+
6. **Classify exact actions.** Classify entries as `REMOVE`, `REWRITE`,
|
|
78
|
+
`POINTER`, `KEEP`, `BLOCKED`, `BLOCKED_ACTIVE_SCOPE`, `UNVERIFIED`, or
|
|
79
|
+
`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.
|
|
88
|
+
|
|
89
|
+
7. **Produce one exact manifest.** `audit` mode stops after the manifest.
|
|
90
|
+
Mutation mode also stops until the literal user approves exact manifest
|
|
91
|
+
entries (for example, "all recommendations except 4 and 7"). Broad approval
|
|
92
|
+
such as "clean AGENTS.md" is insufficient. Interruption starts a fresh scan;
|
|
93
|
+
no manifest is persisted. No fixed line, byte, percentage, or Token target
|
|
94
|
+
authorizes removal.
|
|
95
|
+
|
|
96
|
+
8. **Revalidate and mutate minimally.** Re-read candidate bytes, Git status,
|
|
97
|
+
content hash, references, precedence evidence, and active scope immediately
|
|
98
|
+
before each approved change. Drift blocks that item. Never execute commands
|
|
99
|
+
copied from instruction files. Never alter ordinary documentation, install
|
|
100
|
+
project contracts, commit, or mutate Managed authority. Necessary hard
|
|
101
|
+
guardrails, security constraints, data-loss prevention, accessibility
|
|
102
|
+
basics, and explicit user requirements are never simplified away for
|
|
103
|
+
brevity.
|
|
104
|
+
|
|
105
|
+
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
|
|
108
|
+
does not execute arbitrary documented commands, real-model success-rate
|
|
109
|
+
benchmarks, or Token measurements unless the user literally requests a
|
|
110
|
+
benchmark. This Skill does not commit. Report only `Removed`, `Rewritten`,
|
|
111
|
+
`Moved to pointer`, `Kept`, `Blocked`, `Unverified`, `Verification`, and
|
|
112
|
+
before/after line and byte counts. Do not translate byte changes into Token
|
|
113
|
+
or task-success claims.
|
|
114
|
+
|
|
115
|
+
## Mutation Envelope
|
|
116
|
+
|
|
117
|
+
This Skill may modify only approved agent instruction files. It must stop when
|
|
118
|
+
completion requires creating a new reference document, changing ordinary docs,
|
|
119
|
+
runtime behavior, business-test semantics, package exports, public API,
|
|
120
|
+
credentials, network writes, Git history rewriting, project-contract
|
|
121
|
+
installation or continuous validation, automatic rule learning, or any Managed
|
|
122
|
+
authority mutation.
|
|
123
|
+
|
|
124
|
+
A failed focused check leaves an inspectable diff and stops; this Skill does
|
|
125
|
+
not stash, reset, checkout, or revert user work.
|
|
126
|
+
|
|
127
|
+
## Non-goals
|
|
128
|
+
|
|
129
|
+
- User-level and external instruction files are never inventoried or modified.
|
|
130
|
+
- Runtime still does not install or continuously validate project-level
|
|
131
|
+
`AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `IMMUNE.md`, or `CONTEXT.md`
|
|
132
|
+
contracts.
|
|
133
|
+
- No daemon, telemetry, automatic learning, persistent report, or automatic
|
|
134
|
+
commit.
|
|
135
|
+
- No 200-line, Token, or success-rate target.
|
|
136
|
+
- `imm-doc-prune` remains able to remove evidence-proven stale
|
|
137
|
+
agent-instruction content; the two Skills never invoke each other.
|
|
@@ -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-
|
|
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
|
Dispatch authorization follows the [shared Subagent Dispatch
|
|
19
19
|
Protocol](docs/reference/subagent-dispatch-protocol.md#authorization-authority).
|
|
20
20
|
Same-boundary `follow_up` is not a Plan mutation; it repeats the current
|
|
@@ -140,56 +140,65 @@ A repository directive overrides the global directive. Report an invalid value
|
|
|
140
140
|
and ask instead of guessing. After resolving it, display one non-blocking line
|
|
141
141
|
with the selected carrier and its source. A configured `github` default is
|
|
142
142
|
standing opt-in for GitHub projection, but the literal user must still confirm
|
|
143
|
-
the named Initiative
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
`
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
`
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
`
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
143
|
+
the named Initiative, its immutable slug, and the complete Parent/Child
|
|
144
|
+
decomposition before the first remote mutation. A prior bulk approval cannot
|
|
145
|
+
confirm a name, slug, Child, or dependency that had not yet been shown.
|
|
146
|
+
|
|
147
|
+
Once decomposition is complete, present one review table containing the Parent
|
|
148
|
+
result and every Child's stable Slice ID, result, scope boundary, risk, blockers,
|
|
149
|
+
and proposed execution order. Ask one focused question: whether the coverage,
|
|
150
|
+
granularity, and dependencies are correct. Recommend the complete current
|
|
151
|
+
frontier so the user can approve it in one response. Before that approval,
|
|
152
|
+
perform zero GitHub mutations. A partial or progressively disclosed issue set is
|
|
153
|
+
not eligible for publication.
|
|
154
|
+
|
|
155
|
+
After approval, author, stage, and validate every TaskIntent in the decomposition
|
|
156
|
+
with `valid: true` and `enrollment_ready: true`. Resolve `../bin/imm-tracker` from this packaged contract; do not assume a bare command is on `PATH`. Submit the entire approved set once through
|
|
157
|
+
`imm-tracker publish-initiative --stdin --json`. Its input contains the confirmed
|
|
158
|
+
Initiative slug and goal, Parent projection, and every Child's `slice_id`,
|
|
159
|
+
canonical TaskIntent path, and public projection. The Parent projection requires
|
|
160
|
+
`problem`, `result`, and `design`, and may include `decisions`,
|
|
161
|
+
`testing_strategy`, and `out_of_scope`. `design` records Initiative-level
|
|
162
|
+
invariants, Slice boundaries and ordering, shared interfaces or state flow, and
|
|
163
|
+
material compatibility decisions. Every Parent Slice must correspond to one
|
|
164
|
+
published Child; future checklist-only Slices are not allowed in the batch.
|
|
165
|
+
|
|
166
|
+
Each Child projection may contain `result`, `current_behavior`,
|
|
167
|
+
`desired_behavior`, `key_interfaces`, `verification`, `blocked_by` Task IDs,
|
|
168
|
+
`out_of_scope`, and `agent_handoff`. The tracker rereads every canonical
|
|
169
|
+
TaskIntent for identity, risk, and acceptance; projection fields never widen
|
|
170
|
+
TaskIntent scope or authority. It validates the complete dependency graph before
|
|
171
|
+
remote writes, creates the Parent once, creates all Children, attaches every
|
|
172
|
+
Child as a native Sub-issue, creates native `blocked_by` relations, and rereads
|
|
173
|
+
the complete topology. The Child Agent Brief includes a direct Parent Issue link.
|
|
173
174
|
Internal role prompts, tool policies, review gates, model reservations, and
|
|
174
|
-
prompt digests never belong in this external handoff.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
prompt digests never belong in this external handoff. If
|
|
176
|
+
`docs/initiatives/<slug>.md` exists, publication fails with a carrier conflict;
|
|
177
|
+
Local mode performs zero GitHub operations.
|
|
178
|
+
|
|
179
|
+
The batch result includes an execution recommendation: the first unblocked Task,
|
|
180
|
+
a stable dependency order, and parallel groups. For a plan-only request, report
|
|
181
|
+
that recommendation and stop. For a request that includes execution, invoke the
|
|
182
|
+
native Enrollment gate for the recommended first TaskIntent after successful
|
|
183
|
+
publication; do not ask for another chat confirmation. GitHub selection never
|
|
184
|
+
bypasses Enrollment.
|
|
178
185
|
|
|
179
186
|
Tracker output is observation, never authority. Before the Planner returns, its
|
|
180
|
-
GitHub carrier outcome must be exactly one of: `tracker_associated` after
|
|
181
|
-
|
|
182
|
-
`awaiting_user_initiative_confirmation` with the single pending
|
|
183
|
-
|
|
184
|
-
and exact retry action.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
187
|
+
GitHub carrier outcome must be exactly one of: `tracker_associated` after the
|
|
188
|
+
complete batch returns `created`, `updated`, or `already_current`;
|
|
189
|
+
`awaiting_user_initiative_confirmation` with the single pending name, slug, and
|
|
190
|
+
complete-decomposition decision; or `tracker_projection_failed` with the returned
|
|
191
|
+
failure and exact retry action. A candidate Initiative or partial Issue set
|
|
192
|
+
recorded only in the Spec or final summary is neither user confirmation nor a
|
|
193
|
+
completed carrier outcome. Report `retryable_failure`, `permanent_failure`, or
|
|
194
|
+
`ambiguous_remote_state` and the exact batch retry action. This does not invalidate
|
|
195
|
+
already-authored planning files, but it blocks `tracker_associated` and every
|
|
196
|
+
Enrollment or execution handoff for that Initiative until the same complete
|
|
197
|
+
batch succeeds. Do not infer opt-in from tracker output or Issue state, auto-close the Parent,
|
|
198
|
+
import Issue state, create a TaskIntent from an Issue, or store Issue identity in
|
|
199
|
+
TaskIntent or TaskRecord. Existing Issue markers grant permission only for
|
|
200
|
+
idempotent retry of that same approved Initiative; they never grant execution
|
|
201
|
+
authority.
|
|
193
202
|
|
|
194
203
|
### Verification Descriptor Discipline
|
|
195
204
|
|
|
@@ -47,3 +47,12 @@ skills:
|
|
|
47
47
|
output_artifacts: [prune_report]
|
|
48
48
|
next_actions: []
|
|
49
49
|
boundary: Prune stale current documentation after explicit manifest approval; no Managed authority mutation or authority-artifact deletion.
|
|
50
|
+
- name: imm-agent-doc-maintain
|
|
51
|
+
path: skills/imm-agent-doc-maintain/SKILL.md
|
|
52
|
+
role: execute
|
|
53
|
+
title: Agent Doc Maintain
|
|
54
|
+
role_class: repair
|
|
55
|
+
canonical: true
|
|
56
|
+
output_artifacts: [maintain_report]
|
|
57
|
+
next_actions: []
|
|
58
|
+
boundary: Minimize tracked agent-instruction context after explicit manifest approval; no Managed authority mutation, contract installation, or reference-document creation.
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
953
|
-
|
|
954
|
-
|
|
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
|
|
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
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
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
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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.
|
|
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
|
|
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-
|
|
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.
|
|
@@ -146,56 +146,65 @@ A repository directive overrides the global directive. Report an invalid value
|
|
|
146
146
|
and ask instead of guessing. After resolving it, display one non-blocking line
|
|
147
147
|
with the selected carrier and its source. A configured `github` default is
|
|
148
148
|
standing opt-in for GitHub projection, but the literal user must still confirm
|
|
149
|
-
the named Initiative
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
`
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
`
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
`
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
149
|
+
the named Initiative, its immutable slug, and the complete Parent/Child
|
|
150
|
+
decomposition before the first remote mutation. A prior bulk approval cannot
|
|
151
|
+
confirm a name, slug, Child, or dependency that had not yet been shown.
|
|
152
|
+
|
|
153
|
+
Once decomposition is complete, present one review table containing the Parent
|
|
154
|
+
result and every Child's stable Slice ID, result, scope boundary, risk, blockers,
|
|
155
|
+
and proposed execution order. Ask one focused question: whether the coverage,
|
|
156
|
+
granularity, and dependencies are correct. Recommend the complete current
|
|
157
|
+
frontier so the user can approve it in one response. Before that approval,
|
|
158
|
+
perform zero GitHub mutations. A partial or progressively disclosed issue set is
|
|
159
|
+
not eligible for publication.
|
|
160
|
+
|
|
161
|
+
After approval, author, stage, and validate every TaskIntent in the decomposition
|
|
162
|
+
with `valid: true` and `enrollment_ready: true`. Resolve `../../bin/imm-tracker` from this Skill location; do not assume a bare command is on `PATH`. Submit the entire approved set once through
|
|
163
|
+
`imm-tracker publish-initiative --stdin --json`. Its input contains the confirmed
|
|
164
|
+
Initiative slug and goal, Parent projection, and every Child's `slice_id`,
|
|
165
|
+
canonical TaskIntent path, and public projection. The Parent projection requires
|
|
166
|
+
`problem`, `result`, and `design`, and may include `decisions`,
|
|
167
|
+
`testing_strategy`, and `out_of_scope`. `design` records Initiative-level
|
|
168
|
+
invariants, Slice boundaries and ordering, shared interfaces or state flow, and
|
|
169
|
+
material compatibility decisions. Every Parent Slice must correspond to one
|
|
170
|
+
published Child; future checklist-only Slices are not allowed in the batch.
|
|
171
|
+
|
|
172
|
+
Each Child projection may contain `result`, `current_behavior`,
|
|
173
|
+
`desired_behavior`, `key_interfaces`, `verification`, `blocked_by` Task IDs,
|
|
174
|
+
`out_of_scope`, and `agent_handoff`. The tracker rereads every canonical
|
|
175
|
+
TaskIntent for identity, risk, and acceptance; projection fields never widen
|
|
176
|
+
TaskIntent scope or authority. It validates the complete dependency graph before
|
|
177
|
+
remote writes, creates the Parent once, creates all Children, attaches every
|
|
178
|
+
Child as a native Sub-issue, creates native `blocked_by` relations, and rereads
|
|
179
|
+
the complete topology. The Child Agent Brief includes a direct Parent Issue link.
|
|
179
180
|
Internal role prompts, tool policies, review gates, model reservations, and
|
|
180
|
-
prompt digests never belong in this external handoff.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
181
|
+
prompt digests never belong in this external handoff. If
|
|
182
|
+
`docs/initiatives/<slug>.md` exists, publication fails with a carrier conflict;
|
|
183
|
+
Local mode performs zero GitHub operations.
|
|
184
|
+
|
|
185
|
+
The batch result includes an execution recommendation: the first unblocked Task,
|
|
186
|
+
a stable dependency order, and parallel groups. For a plan-only request, report
|
|
187
|
+
that recommendation and stop. For a request that includes execution, invoke the
|
|
188
|
+
native Enrollment gate for the recommended first TaskIntent after successful
|
|
189
|
+
publication; do not ask for another chat confirmation. GitHub selection never
|
|
190
|
+
bypasses Enrollment.
|
|
184
191
|
|
|
185
192
|
Tracker output is observation, never authority. Before the Planner returns, its
|
|
186
|
-
GitHub carrier outcome must be exactly one of: `tracker_associated` after
|
|
187
|
-
|
|
188
|
-
`awaiting_user_initiative_confirmation` with the single pending
|
|
189
|
-
|
|
190
|
-
and exact retry action.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
`
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
193
|
+
GitHub carrier outcome must be exactly one of: `tracker_associated` after the
|
|
194
|
+
complete batch returns `created`, `updated`, or `already_current`;
|
|
195
|
+
`awaiting_user_initiative_confirmation` with the single pending name, slug, and
|
|
196
|
+
complete-decomposition decision; or `tracker_projection_failed` with the returned
|
|
197
|
+
failure and exact retry action. A candidate Initiative or partial Issue set
|
|
198
|
+
recorded only in the Spec or final summary is neither user confirmation nor a
|
|
199
|
+
completed carrier outcome. Report `retryable_failure`, `permanent_failure`, or
|
|
200
|
+
`ambiguous_remote_state` and the exact batch retry action. This does not invalidate
|
|
201
|
+
already-authored planning files, but it blocks `tracker_associated` and every
|
|
202
|
+
Enrollment or execution handoff for that Initiative until the same complete
|
|
203
|
+
batch succeeds. Do not infer opt-in from tracker output or Issue state, auto-close the Parent,
|
|
204
|
+
import Issue state, create a TaskIntent from an Issue, or store Issue identity in
|
|
205
|
+
TaskIntent or TaskRecord. Existing Issue markers grant permission only for
|
|
206
|
+
idempotent retry of that same approved Initiative; they never grant execution
|
|
207
|
+
authority.
|
|
199
208
|
|
|
200
209
|
### Verification Descriptor Discipline
|
|
201
210
|
|
|
@@ -47,3 +47,12 @@ skills:
|
|
|
47
47
|
output_artifacts: [prune_report]
|
|
48
48
|
next_actions: []
|
|
49
49
|
boundary: Prune stale current documentation after explicit manifest approval; no Managed authority mutation or authority-artifact deletion.
|
|
50
|
+
- name: imm-agent-doc-maintain
|
|
51
|
+
path: skills/imm-agent-doc-maintain/SKILL.md
|
|
52
|
+
role: execute
|
|
53
|
+
title: Agent Doc Maintain
|
|
54
|
+
role_class: repair
|
|
55
|
+
canonical: true
|
|
56
|
+
output_artifacts: [maintain_report]
|
|
57
|
+
next_actions: []
|
|
58
|
+
boundary: Minimize tracked agent-instruction context after explicit manifest approval; no Managed authority mutation, contract installation, or reference-document creation.
|