instar 0.28.95 → 0.28.97
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/dashboard/index.html +249 -2
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +90 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/MachineHeartbeat.d.ts +94 -0
- package/dist/core/MachineHeartbeat.d.ts.map +1 -0
- package/dist/core/MachineHeartbeat.js +155 -0
- package/dist/core/MachineHeartbeat.js.map +1 -0
- package/dist/core/ProjectAutoAdvancePoller.d.ts +107 -0
- package/dist/core/ProjectAutoAdvancePoller.d.ts.map +1 -0
- package/dist/core/ProjectAutoAdvancePoller.js +176 -0
- package/dist/core/ProjectAutoAdvancePoller.js.map +1 -0
- package/dist/core/ProjectRoundCompleteMessage.d.ts +158 -0
- package/dist/core/ProjectRoundCompleteMessage.d.ts.map +1 -0
- package/dist/core/ProjectRoundCompleteMessage.js +253 -0
- package/dist/core/ProjectRoundCompleteMessage.js.map +1 -0
- package/dist/core/ProjectRoundExecution.d.ts +111 -0
- package/dist/core/ProjectRoundExecution.d.ts.map +1 -0
- package/dist/core/ProjectRoundExecution.js +359 -0
- package/dist/core/ProjectRoundExecution.js.map +1 -0
- package/dist/core/ProjectRoundLock.d.ts +67 -0
- package/dist/core/ProjectRoundLock.d.ts.map +1 -0
- package/dist/core/ProjectRoundLock.js +161 -0
- package/dist/core/ProjectRoundLock.js.map +1 -0
- package/dist/core/ProjectRoundRunner.d.ts +146 -0
- package/dist/core/ProjectRoundRunner.d.ts.map +1 -0
- package/dist/core/ProjectRoundRunner.js +371 -0
- package/dist/core/ProjectRoundRunner.js.map +1 -0
- package/dist/core/ProjectRoundWorktrees.d.ts +57 -0
- package/dist/core/ProjectRoundWorktrees.d.ts.map +1 -0
- package/dist/core/ProjectRoundWorktrees.js +111 -0
- package/dist/core/ProjectRoundWorktrees.js.map +1 -0
- package/dist/server/AgentServer.d.ts +12 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +3 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +19 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +782 -7
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +48 -48
- package/upgrades/0.28.96.md +347 -0
- package/upgrades/0.28.97.md +146 -0
- package/upgrades/side-effects/project-scope-phase1-7-skill-surface.md +175 -0
- package/upgrades/side-effects/project-scope-phase1b-connect-the-dots.md +184 -0
- package/upgrades/side-effects/project-scope-phase1b-pr3.md +228 -0
- package/upgrades/side-effects/project-scope-phase1b-pr4.md +198 -0
- package/upgrades/side-effects/project-scope-phase1b-pr5.md +150 -0
- package/upgrades/side-effects/project-scope-phase1b-pr6.md +136 -0
- package/upgrades/side-effects/project-scope-phase1b-pr7.md +175 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: minor -->
|
|
4
|
+
<!-- Valid values: patch, minor, major -->
|
|
5
|
+
<!-- patch = bug fixes, refactors, test additions, doc updates -->
|
|
6
|
+
<!-- minor = new features, new APIs, new capabilities (backwards-compatible) -->
|
|
7
|
+
<!-- major = breaking changes to existing APIs or behavior -->
|
|
8
|
+
|
|
9
|
+
## What Changed
|
|
10
|
+
|
|
11
|
+
### Project-scope Phase 1b connect-the-dots — wire the primitives
|
|
12
|
+
|
|
13
|
+
Wires the four primitives shipped during the Phase 1b autonomous burst
|
|
14
|
+
to their consumers, closing the deferrals named in side-effects reviews
|
|
15
|
+
of PRs #164, #166, #167, and #168.
|
|
16
|
+
|
|
17
|
+
- **Auto-advance poller now actually launches rounds.** On a successful
|
|
18
|
+
preflight + bookkeeping move, the poller fires-and-forgets a
|
|
19
|
+
`ProjectRoundExecution.runRound()` call. A per-project `inFlight`
|
|
20
|
+
guard prevents a slow run from being relaunched on the next 60s
|
|
21
|
+
tick. Errors are captured in `result.executorErrors[]` — they never
|
|
22
|
+
throw out of `tick()`.
|
|
23
|
+
- **`GET /projects/:id/next` returns a structured action.** Replaces
|
|
24
|
+
the 501 placeholder with the spec § 1.5 line 268 contract:
|
|
25
|
+
`{ action, params, skillCommand }`. Verbs include
|
|
26
|
+
`'await-user-approval'`, `'ack-required'`, `'resolve-conflict'`,
|
|
27
|
+
`'accept-partial'`, `'run-spec-converge'`, `'run-drift-check'`,
|
|
28
|
+
`'start-round'`. Each maps to a `/project ...` invocation so the
|
|
29
|
+
dashboard + skill UI can act on a single GET. 204 when all rounds
|
|
30
|
+
complete, 404 on non-project initiative.
|
|
31
|
+
- **`GET /projects/:id` lazy reconciler.** Per spec § 1.4 lines 256-258.
|
|
32
|
+
For 'building' children with `mergeCommitOid` set: 6h debounce via
|
|
33
|
+
`ciCheckedAt`, selection order oldest-`ciCheckedAt`-first
|
|
34
|
+
(ties → `roundIndex` ASC → `itemId` ASC, no starvation), cap of 3
|
|
35
|
+
per GET. Verified ancestor of `origin/main` → `'merged'`. NOT an
|
|
36
|
+
ancestor → `'regressed'` AND clear future `autoAdvanceAt` so the
|
|
37
|
+
chain doesn't auto-fire while a child is broken. `ciCheckedAt` is
|
|
38
|
+
written on every revalidation attempt (success or git failure) so
|
|
39
|
+
the debounce always backs off. Opt-out: `?reconcile=false`.
|
|
40
|
+
- **`POST /projects/:id/drift-check` route.** New HTTP wrapper around
|
|
41
|
+
`ProjectDriftChecker.run()`. **Mutex-guarded per project (spec line
|
|
42
|
+
279)** — concurrent calls against the same project return 409,
|
|
43
|
+
protecting the drift-spend ledger from double-spend. Body:
|
|
44
|
+
`{ roundIndex, specPath, referencedFiles[], timeoutMs?, modelId? }`.
|
|
45
|
+
Returns 503 when no `IntelligenceProvider` is configured.
|
|
46
|
+
|
|
47
|
+
Drift cache + spend-ledger wiring are intentionally deferred until the
|
|
48
|
+
dashboard surfaces live verdicts and cost telemetry needs the cap. The
|
|
49
|
+
mutex is in-process per-server; multi-process protection comes from
|
|
50
|
+
the existing `proper-lockfile` on the drift-spend ledger itself.
|
|
51
|
+
|
|
52
|
+
### Project-scope Phase 1b PR 7 — autonomous run loop
|
|
53
|
+
|
|
54
|
+
Final PR of project-scope Phase 1b. Ships the autonomous run loop the
|
|
55
|
+
spec § Phase 1.5 names (steps 1-11), wired against the lock, preflight,
|
|
56
|
+
and runner primitives that landed in PRs 3-6:
|
|
57
|
+
|
|
58
|
+
- **`ProjectRoundWorktrees`** — lazy worktree allocator under
|
|
59
|
+
`<targetRepoPath>/.worktrees/<projectId>/<roundIndex>/<itemId>`.
|
|
60
|
+
Appends `.worktrees/` to `.git/info/exclude` on first allocation so
|
|
61
|
+
the namespace doesn't pollute `git status`. Helpers for prune +
|
|
62
|
+
remove + ensure-exclude-entry.
|
|
63
|
+
- **`runRound(input, deps)`** — the run loop itself. Acquires the
|
|
64
|
+
round-runner lock, lazily allocates the first worktree, spawns the
|
|
65
|
+
autonomous child in a detached process group, polls the project
|
|
66
|
+
record every 60 seconds. On mid-round mutation to `round.itemIds`,
|
|
67
|
+
SIGTERMs the child's process group (5s grace, then SIGKILL) and
|
|
68
|
+
relaunches with the new stop condition. On the child's natural
|
|
69
|
+
exit, verifies per-item artifacts via the caller-injected
|
|
70
|
+
`verifyMergedItems` and sets `round.status` to `complete` or
|
|
71
|
+
`partially-complete`. On `haltedAt` set mid-run, kills the child
|
|
72
|
+
and returns `halted`. Three-attempt resume cap on transient
|
|
73
|
+
non-zero exits before `failed`.
|
|
74
|
+
- **`verifyMergedItemsViaGit`** — exported helper for production
|
|
75
|
+
callers; runs `git merge-base --is-ancestor <child.mergeCommitOid>
|
|
76
|
+
origin/main` per item.
|
|
77
|
+
|
|
78
|
+
The run loop is a module function, not a method on `ProjectRoundRunner`,
|
|
79
|
+
because it has different lifecycle semantics (long-running, subprocess
|
|
80
|
+
manager) than the synchronous verbs in PR 3's runner. Both modules
|
|
81
|
+
work in concert: callers call `runner.preflight(...)` first, then
|
|
82
|
+
`runRound(...)` if preflight passes.
|
|
83
|
+
|
|
84
|
+
Phase 1b is now fully decomposed: 7 PRs across drift + cache + ledger
|
|
85
|
+
+ runner + halt/advance/ack + auto-advance + claim-ownership +
|
|
86
|
+
dashboard + filter + round-complete-message + run-loop. The remaining
|
|
87
|
+
spec items (lazy merged-state reconciler on GET, `GET /next` real
|
|
88
|
+
implementation) are wiring tasks rather than new infrastructure.
|
|
89
|
+
|
|
90
|
+
### Project-scope Phase 1b PR 6 — tone-gated round-complete message + delivery
|
|
91
|
+
|
|
92
|
+
Sixth PR of project-scope Phase 1b. Ships the two final primitives the
|
|
93
|
+
autonomous run loop will need to emit round-complete digests safely:
|
|
94
|
+
|
|
95
|
+
- **`formatRoundCompleteMessage(input)`** — pure template function.
|
|
96
|
+
Enforces required-field PRESENCE (not non-emptiness, per spec —
|
|
97
|
+
"the gate never silently rejects on a legitimate halt"). Returns
|
|
98
|
+
`{ok:true, message, idempotencyKey}` or `{ok:false, missingFields}`.
|
|
99
|
+
Halt-flavor events additionally require `whatHalted`.
|
|
100
|
+
|
|
101
|
+
- **`RoundCompleteDeliveryHelper`** — retry + idempotency wrapper.
|
|
102
|
+
3-attempt exponential backoff (1s, 2s, 4s by default). Records
|
|
103
|
+
the `(projectId, roundIndex, eventKind, projectVersion)` key in
|
|
104
|
+
`.instar/local/round-complete-sent.json` after the first successful
|
|
105
|
+
send so tone-gate retries don't produce duplicates. On permanent
|
|
106
|
+
failure, fires the caller's `onPermanentFail` callback (the run
|
|
107
|
+
loop will wire it to attention-queue + audit-log + awaitingUser
|
|
108
|
+
population when it ships).
|
|
109
|
+
|
|
110
|
+
Nothing wires these up yet — the autonomous run loop is the consumer.
|
|
111
|
+
PR 6 ships the primitives so the run loop in a follow-up PR doesn't
|
|
112
|
+
fork either of them.
|
|
113
|
+
|
|
114
|
+
### Project-scope Phase 1b PR 5 — dashboard Projects tab + Initiatives filter
|
|
115
|
+
|
|
116
|
+
Fifth PR of project-scope Phase 1b. Ships the dashboard view of the
|
|
117
|
+
project surface and the small server-side filter the dashboard depends
|
|
118
|
+
on:
|
|
119
|
+
|
|
120
|
+
- **Projects tab** in the dashboard. Read-only card per active project
|
|
121
|
+
with title + id + version + drift status badge + per-round progress
|
|
122
|
+
bar + pipelineStage histogram. Halt + Ack buttons route through the
|
|
123
|
+
PR 3 endpoints; 409 responses silently re-render (no error toast).
|
|
124
|
+
15-second poll while the tab is active; cleared on deactivation.
|
|
125
|
+
XSS-safe — every rendered string goes through `textContent`, no
|
|
126
|
+
`innerHTML`.
|
|
127
|
+
- **Initiatives tab filter** — by default the tab now hides
|
|
128
|
+
project-kind records (shown in their own Projects tab) and child
|
|
129
|
+
initiatives of any project (shown inside the parent's card). A
|
|
130
|
+
"Show projects + children" checkbox bypasses the filter for users
|
|
131
|
+
who want the full list. The filter is server-side: the records
|
|
132
|
+
never cross the wire when excluded.
|
|
133
|
+
- **`GET /initiatives` filter params** — accepts `excludeKind=<kind>`
|
|
134
|
+
(drops records of that kind) and `excludeParented=true` (drops
|
|
135
|
+
records that are children of any project). Additive — existing
|
|
136
|
+
callers see no behavior change.
|
|
137
|
+
|
|
138
|
+
The compaction-recovery hook's active-projects digest was already in
|
|
139
|
+
the template (it shipped alongside session-start.sh in PR 1's Phase
|
|
140
|
+
1.9 work), so no template change is needed here.
|
|
141
|
+
|
|
142
|
+
### Project-scope Phase 1b PR 4 — auto-advance poller + multi-machine claim-ownership
|
|
143
|
+
|
|
144
|
+
Fourth PR of project-scope Phase 1b. Ships three pieces:
|
|
145
|
+
|
|
146
|
+
- **`MachineHeartbeat`** — per-machine liveness signal at
|
|
147
|
+
`.instar/machine-health/<machineId>.json` (git-synced). Written
|
|
148
|
+
every 30 minutes; consulted by the claim-ownership flow for the
|
|
149
|
+
48-hour staleness check. Machine ids with weird characters are
|
|
150
|
+
URL-encoded into the file name so a stray slash cannot escape
|
|
151
|
+
`.instar/machine-health/`.
|
|
152
|
+
|
|
153
|
+
- **`ProjectAutoAdvancePoller`** — periodic scan (1-minute tick) for
|
|
154
|
+
project rounds whose `autoAdvanceAt` has elapsed. Server-side
|
|
155
|
+
filter: `kind:'project'`, `status:'active'`, owner machine matches
|
|
156
|
+
current, `unacknowledgedAdvanceCount < 2`. On fire, calls
|
|
157
|
+
`ProjectRoundRunner.preflight`; structural rejects (first-launch
|
|
158
|
+
ack missing, cap hit, project inactive, etc.) clear `autoAdvanceAt`
|
|
159
|
+
to prevent re-firing on every tick.
|
|
160
|
+
|
|
161
|
+
- **`POST /projects/:id/claim-ownership`** — OCC-protected
|
|
162
|
+
multi-machine ownership transfer. Refuses with 409 when the current
|
|
163
|
+
owner has a fresh heartbeat unless `{force:true}` is passed.
|
|
164
|
+
Idempotent on already-owns. Response carries `previousOwner` for
|
|
165
|
+
audit.
|
|
166
|
+
|
|
167
|
+
Plus a one-shot **post-restore reconciler** at server startup: any
|
|
168
|
+
project round flagged `in-progress` is downgraded to `pending`. The
|
|
169
|
+
previous owner may have crashed or migrated; no TaskFlow yet exists
|
|
170
|
+
to verify whether a child is actually live.
|
|
171
|
+
|
|
172
|
+
### Project-scope Phase 1b PR 3 — round runner + halt/advance/ack endpoints
|
|
173
|
+
|
|
174
|
+
Third PR of project-scope Phase 1b. Ships the single-chokepoint
|
|
175
|
+
runner that the spec names in § Phase 1.5, plus the four mutating
|
|
176
|
+
HTTP routes that thread through it:
|
|
177
|
+
|
|
178
|
+
- `POST /projects/:id/advance` — single-item stage transition driven
|
|
179
|
+
by the existing `StageTransitionValidator`. Requires `If-Match`
|
|
180
|
+
(OCC). Body: `{itemId, targetStage, artifact: {specPath?, prNumber?,
|
|
181
|
+
taskFlowRecordId?, skippedReason?, skippedBy?, unskippedAt?}}`.
|
|
182
|
+
Returns 409 with `{error, code, reason}` on validator reject.
|
|
183
|
+
- `POST /projects/:id/halt` — emergency stop. Writes `haltedAt` +
|
|
184
|
+
`haltReason` to the active round, releases the round-runner lock
|
|
185
|
+
if the calling machine holds it. Idempotent.
|
|
186
|
+
- `POST /projects/:id/ack` — records user acknowledgment for a round.
|
|
187
|
+
Populates `firstLaunchAckAt` if absent, advances
|
|
188
|
+
`lastAckedRoundIndex`, resets `unacknowledgedAdvanceCount`.
|
|
189
|
+
Idempotent on `forRoundIndex`.
|
|
190
|
+
- `POST /projects/:id/accept-partial` — closes a partially-complete
|
|
191
|
+
round. Non-merged, non-skipped child items transition to
|
|
192
|
+
`skipped` (requires `reason` + `skippedBy` per the validator);
|
|
193
|
+
round status → `complete-with-skips`; counts as ack for the
|
|
194
|
+
current `roundIndex`.
|
|
195
|
+
|
|
196
|
+
Behind the routes: `ProjectRoundRunner` runs the 9 deterministic
|
|
197
|
+
preflight checks from § Phase 1.5 (lock free, round shape valid,
|
|
198
|
+
items resolve, first-launch ack on round 0, unacked-advances cap,
|
|
199
|
+
ack-gap cap, owner machine matches, target repo is a git repo, no
|
|
200
|
+
pending reconciliation conflicts). Drift check (step 10) is
|
|
201
|
+
intentionally deferred until the drift-check HTTP endpoint and its
|
|
202
|
+
cache + ledger wiring ship in a follow-up PR — the drift verdict
|
|
203
|
+
cache and cost ledger from v0.28.94 are on disk waiting for that
|
|
204
|
+
consumer.
|
|
205
|
+
|
|
206
|
+
The autonomous-delegating `run()` loop (lazy worktrees, dynamic
|
|
207
|
+
stop-condition revalidation, SIGTERM/SIGKILL of process groups,
|
|
208
|
+
partial-complete detection) is also deferred to the next PR; this
|
|
209
|
+
PR ships the state-management verbs without the orchestration loop
|
|
210
|
+
so the routes can be exercised end-to-end against real validators
|
|
211
|
+
today.
|
|
212
|
+
|
|
213
|
+
Lock primitive lives at `.instar/local/round-runner.lock` —
|
|
214
|
+
machine-local (not git-synced) per spec. `O_CREAT|O_EXCL` rename +
|
|
215
|
+
stale-PID sweep on every acquire, so a crashed runner doesn't
|
|
216
|
+
permanently block subsequent acquires.
|
|
217
|
+
|
|
218
|
+
## What to Tell Your User
|
|
219
|
+
|
|
220
|
+
- **The autonomous round loop is wired up end-to-end**: when a round
|
|
221
|
+
starts, I now spawn the actual autonomous work in its own process
|
|
222
|
+
group, watch every minute for changes to which items the round is
|
|
223
|
+
working on, and clean up after myself (process group + worktrees)
|
|
224
|
+
when I am done or you halt me. If you manually skip an item or
|
|
225
|
+
re-order the round mid-run, I gracefully stop the work I am doing
|
|
226
|
+
and relaunch with the new plan — no orphaned compilers, no
|
|
227
|
+
orphaned worktrees.
|
|
228
|
+
|
|
229
|
+
- **Round-complete digests are duplicate-safe**: when I finish a
|
|
230
|
+
round, the message I send you is built from a template that refuses
|
|
231
|
+
to send if any required field is missing, and the delivery layer
|
|
232
|
+
remembers what it already sent so a tone-gate retry can't spam you
|
|
233
|
+
with the same digest twice. If delivery fails permanently, I will
|
|
234
|
+
surface it through your attention queue rather than retry forever
|
|
235
|
+
in silence.
|
|
236
|
+
|
|
237
|
+
- **Projects have a dashboard tab now**: open the dashboard and you'll
|
|
238
|
+
see every active project I'm tracking with its round-by-round
|
|
239
|
+
progress, which items have merged vs which are still in flight, and
|
|
240
|
+
any drift warnings I've recorded. Halt and ack buttons are right
|
|
241
|
+
there if you want to stop me or tell me you've seen the digest. The
|
|
242
|
+
Initiatives tab no longer mixes project plumbing in by default —
|
|
243
|
+
flip the "Show projects + children" toggle if you want to see
|
|
244
|
+
everything together.
|
|
245
|
+
|
|
246
|
+
- **You can now drive a project round through the HTTP layer**: I can
|
|
247
|
+
advance a single item one stage with a real artifact check, halt the
|
|
248
|
+
active round on demand, record acknowledgment when I've shown you a
|
|
249
|
+
digest, or close out a round that landed only some of its items —
|
|
250
|
+
all from the dashboard or directly through the API. The actual
|
|
251
|
+
autonomous round loop that walks through items one by one is the
|
|
252
|
+
next piece I'm building.
|
|
253
|
+
|
|
254
|
+
- **I can recover ownership when a peer machine goes offline**: if my
|
|
255
|
+
other machine has been silent for more than 48 hours, I can claim
|
|
256
|
+
back ownership of a project we'd been working on together. If my
|
|
257
|
+
peer is still online, I will refuse the claim unless you tell me to
|
|
258
|
+
force it.
|
|
259
|
+
|
|
260
|
+
- **Time-based auto-advance is wired up**: when a round completes
|
|
261
|
+
cleanly, I will schedule the next round automatically. If you have
|
|
262
|
+
asked me to pause and have not acked, I will not auto-advance again
|
|
263
|
+
until you do — the two-rounds-ahead-without-ack brake stops me from
|
|
264
|
+
running off on my own.
|
|
265
|
+
|
|
266
|
+
## Summary of New Capabilities
|
|
267
|
+
|
|
268
|
+
- `ProjectRoundRunner` class — single chokepoint for round-start.
|
|
269
|
+
`preflight(projectId, roundIndex)` runs deterministic checks and
|
|
270
|
+
returns a structured `PreflightResult`. `halt`, `recordAck`,
|
|
271
|
+
`acceptPartial` are idempotent state-management verbs that mutate
|
|
272
|
+
through `InitiativeTracker.update()` with OCC. Static
|
|
273
|
+
`validateChildFrontmatter` for callers that need to assert
|
|
274
|
+
`review-convergence: true` AND `approved: true` outside the runner.
|
|
275
|
+
- `ProjectRoundLock` class — machine-local mutex at
|
|
276
|
+
`.instar/local/round-runner.lock`. Atomic acquire via rename,
|
|
277
|
+
stale-PID sweep on every call. Exposes `acquire`, `release`, `read`.
|
|
278
|
+
- New HTTP routes: `POST /projects/:id/advance`,
|
|
279
|
+
`POST /projects/:id/halt`, `POST /projects/:id/ack`,
|
|
280
|
+
`POST /projects/:id/accept-partial`. All require Bearer auth;
|
|
281
|
+
`/advance` requires `If-Match`.
|
|
282
|
+
- `RouteContext.projectRoundRunner` — wired from
|
|
283
|
+
`AgentServer({ projectRoundRunner })` so other future routes (drift
|
|
284
|
+
check, run-round, claim-ownership) can route through the same
|
|
285
|
+
runner instance.
|
|
286
|
+
- `MachineHeartbeat` class — `start()` / `stop()` / `writeOnce()` /
|
|
287
|
+
`read(machineId)` / `isStale(machineId)` / `listAll()`. File-backed,
|
|
288
|
+
git-syncable, defense-in-depth on malformed reads.
|
|
289
|
+
- `ProjectAutoAdvancePoller` class — `tick()` returns a structured
|
|
290
|
+
`{scanned, fired, rejected, cleared}` report. Caller-driven cadence;
|
|
291
|
+
server wires a 60-second interval.
|
|
292
|
+
- `POST /projects/:id/claim-ownership` — body `{force?: boolean}`;
|
|
293
|
+
header `If-Match` (OCC). Returns 200 with
|
|
294
|
+
`{ownerMachineId, previousOwner, version}` on success; 409 on fresh
|
|
295
|
+
peer heartbeat or version mismatch.
|
|
296
|
+
- `RouteContext.machineHeartbeat` — bundled `{api, config}` so the
|
|
297
|
+
claim-ownership route can compare against the local machine id
|
|
298
|
+
without an extra top-level ctx field.
|
|
299
|
+
- Server startup runs a one-shot reconciler that downgrades any
|
|
300
|
+
`in-progress` round to `pending`. Best-effort; OCC races are
|
|
301
|
+
silently retried on subsequent reconciler passes (or via the
|
|
302
|
+
auto-advance poller's filter).
|
|
303
|
+
- Dashboard `Projects` tab — read-only project cards with progress
|
|
304
|
+
bar + drift badge + halt/ack buttons. 15-second poll while active.
|
|
305
|
+
- Dashboard `Initiatives` tab now defaults to hiding project-kind +
|
|
306
|
+
child records. "Show projects + children" checkbox bypasses.
|
|
307
|
+
- `GET /initiatives` accepts new optional query params: `excludeKind`
|
|
308
|
+
(drops records where `kind ?? 'task'` matches) and
|
|
309
|
+
`excludeParented=true` (drops records with a `parentProjectId`).
|
|
310
|
+
Additive; existing clients see no behavior change.
|
|
311
|
+
- `formatRoundCompleteMessage(input)` — pure template function with
|
|
312
|
+
required-field PRESENCE gate (empty strings accepted, undefined
|
|
313
|
+
rejected). Halt-flavor events additionally require `whatHalted`.
|
|
314
|
+
Returns `{message, idempotencyKey}` on success.
|
|
315
|
+
- `ProjectRoundWorktrees` class — lazy worktree allocator under
|
|
316
|
+
`<targetRepoPath>/.worktrees/<projectId>/<roundIndex>/<itemId>`.
|
|
317
|
+
Static `pathFor`/`allocate`/`prune`/`remove`/`ensureExcludeEntry`.
|
|
318
|
+
- `runRound(input, deps)` function — the run loop. Acquires lock,
|
|
319
|
+
spawns autonomous child detached, polls every 60s, SIGTERMs on
|
|
320
|
+
dynamic-stop changes or halt, verifies per-item artifacts on
|
|
321
|
+
natural exit, sets `round.status`. Returns
|
|
322
|
+
`{outcome, mergedItemIds, unmergedItemIds, relaunchCount,
|
|
323
|
+
resumeAttempts, reason}`.
|
|
324
|
+
- `verifyMergedItemsViaGit(targetRepoPath, childIds, tracker)` —
|
|
325
|
+
exported production verifier; checks `git merge-base --is-ancestor
|
|
326
|
+
<child.mergeCommitOid> origin/main` per item.
|
|
327
|
+
- `RoundCompleteDeliveryHelper` class — retry + idempotency wrapper.
|
|
328
|
+
3-attempt exponential backoff (configurable); records the
|
|
329
|
+
idempotency key in `.instar/local/round-complete-sent.json` so
|
|
330
|
+
duplicate sends are suppressed. `onPermanentFail` callback fires
|
|
331
|
+
once when all retries are exhausted.
|
|
332
|
+
|
|
333
|
+
## Evidence
|
|
334
|
+
|
|
335
|
+
Not reproducible in dev — this release is a feature addition, not a regression fix.
|
|
336
|
+
The words "crashed", "broken", and "resolve" in What Changed describe design properties
|
|
337
|
+
of new primitives, not prior bugs that were observed and repaired:
|
|
338
|
+
|
|
339
|
+
- "crashed runner" (lock primitive): stale-PID sweep so an interrupted runner doesn't
|
|
340
|
+
permanently hold the lock — a designed invariant of the new `ProjectRoundLock`, not
|
|
341
|
+
a fix for an observed crash.
|
|
342
|
+
- "broken" (reconciler): `ciCheckedAt`-debounced ancestor check transitions a child to
|
|
343
|
+
'regressed' when its commit is not reachable from origin/main — a designed state
|
|
344
|
+
machine transition, not a regression repair.
|
|
345
|
+
- "resolve-conflict" (GET /next action verbs): one of seven structured action strings
|
|
346
|
+
the endpoint returns; the word "resolve" is part of the verb name, not a claim that
|
|
347
|
+
a conflict was resolved as a bug fix.
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- bump: minor -->
|
|
4
|
+
<!-- Valid values: patch, minor, major -->
|
|
5
|
+
<!-- patch = bug fixes, refactors, test additions, doc updates -->
|
|
6
|
+
<!-- minor = new features, new APIs, new capabilities (backwards-compatible) -->
|
|
7
|
+
<!-- major = breaking changes to existing APIs or behavior -->
|
|
8
|
+
|
|
9
|
+
## What Changed
|
|
10
|
+
|
|
11
|
+
### Project-scope Phase 1.7 — `/project` skill surface (full command set)
|
|
12
|
+
|
|
13
|
+
Final code piece of project-scope Phase 1: the user-invocable
|
|
14
|
+
`/project` skill, the conversational interface spec § Phase 1.7
|
|
15
|
+
specifies as twelve canonical commands. Phase 1a shipped read-only
|
|
16
|
+
commands (`create`, `status`, plus a placeholder `next`); this PR
|
|
17
|
+
fleshes out the full mutating surface and adds the three small HTTP
|
|
18
|
+
routes the spec named but hadn't materialized yet.
|
|
19
|
+
|
|
20
|
+
- **`.claude/skills/instar-project/SKILL.md` rewritten end-to-end.**
|
|
21
|
+
Documents every spec § 1.7 command with its backing endpoint, body
|
|
22
|
+
schema, expected response codes, and a one-line conversational
|
|
23
|
+
rendering rule. The Phase 1a "next is a 501 placeholder" language
|
|
24
|
+
is gone — the structured `{action, params, skillCommand}` payload
|
|
25
|
+
shipped in the connect-the-dots PR is now the documented contract.
|
|
26
|
+
- **`POST /projects/:id/run-round`** — manual round-start trigger.
|
|
27
|
+
Calls `ProjectRoundRunner.preflight()` for the deterministic gate
|
|
28
|
+
checks and, on accept, sets `autoAdvanceAt = now` so the existing
|
|
29
|
+
auto-advance poller fires the executor on its next tick (≤60s).
|
|
30
|
+
Does NOT spawn the autonomous child directly — keeps one fire path
|
|
31
|
+
through one lock. Body: `{roundIndex}`. Returns 200 with
|
|
32
|
+
`{id, roundIndex, scheduledAt, version}` on accept, 409 with the
|
|
33
|
+
preflight verdict's `code` + `reason` on reject (surface those
|
|
34
|
+
verbatim — they're written for users).
|
|
35
|
+
- **`POST /projects/:id/resume`** — resume a halted or failed round.
|
|
36
|
+
Clears `haltedAt`/`haltReason`, schedules the round for the poller,
|
|
37
|
+
re-actives `project.status` from `'halted'`/`'abandoned'` back to
|
|
38
|
+
`'active'`. For rounds at `status: 'failed'` with
|
|
39
|
+
`resumeAttempts >= 3` (the spec's three-attempt cap), `{force: true}`
|
|
40
|
+
is required and the counter resets to zero on accept. Returns 409
|
|
41
|
+
when the round is neither halted nor failed.
|
|
42
|
+
- **`POST /projects/:id/abandon`** — archive a halted project.
|
|
43
|
+
Sets `project.status = 'abandoned'` and clears any future
|
|
44
|
+
`autoAdvanceAt` on remaining rounds so the poller stops considering
|
|
45
|
+
them. Children's `pipelineStage` is left untouched (per spec).
|
|
46
|
+
Idempotent. Refuses (409) if a round is currently `in-progress` —
|
|
47
|
+
halt first.
|
|
48
|
+
- **`skillCommandForAction()` realigned to canonical command names.**
|
|
49
|
+
The connect-the-dots PR added a mapping that returned
|
|
50
|
+
`/project approve` and `/project drift-check` for the
|
|
51
|
+
`await-user-approval` and `run-drift-check` actions. Spec § 1.7
|
|
52
|
+
uses `/project ack` and `/project drift` as the canonical command
|
|
53
|
+
names; the mapping now matches the spec so dashboard hints and
|
|
54
|
+
skill invocations stay in sync.
|
|
55
|
+
|
|
56
|
+
Tests added:
|
|
57
|
+
|
|
58
|
+
- `tests/unit/instar-project-skill.test.ts` — structural coverage of
|
|
59
|
+
SKILL.md (28 assertions). Pins the frontmatter contract, asserts
|
|
60
|
+
every spec § 1.7 command has a section header, and asserts each
|
|
61
|
+
documented endpoint actually exists in `src/server/routes.ts`.
|
|
62
|
+
Catches skill drift — if a route is renamed or removed without
|
|
63
|
+
updating the skill, the test fails on the next push.
|
|
64
|
+
- `tests/integration/projects-api.test.ts` — 10 new cases (44 total)
|
|
65
|
+
for the three new endpoints: preflight reject (409), happy path
|
|
66
|
+
(200 + autoAdvanceAt landed), out-of-range round (404),
|
|
67
|
+
haltedAt-clear on resume (200), neither-halted-nor-failed reject
|
|
68
|
+
(409), failed-at-cap-without-force reject (409),
|
|
69
|
+
failed-at-cap-with-force happy path (counter zeroed), abandon
|
|
70
|
+
happy path (autoAdvanceAt cleared + status='abandoned'),
|
|
71
|
+
abandon-while-in-progress reject (409), abandon idempotent
|
|
72
|
+
(`alreadyAbandoned: true`).
|
|
73
|
+
|
|
74
|
+
## What to Tell Your User
|
|
75
|
+
|
|
76
|
+
- **You can now drive any project end-to-end through /project.**
|
|
77
|
+
Twelve commands, one skill: register a project from a plan doc,
|
|
78
|
+
read its state, advance a child item to its next stage, run a
|
|
79
|
+
drift check, fire a round, halt the active round, record your
|
|
80
|
+
acknowledgment, resume from halted or failed, abandon a project
|
|
81
|
+
permanently, close out a partially-complete round, claim ownership
|
|
82
|
+
on another machine. The skill walks me through each one with the
|
|
83
|
+
right API call and the right error handling — I don't have to
|
|
84
|
+
improvise the curl invocations.
|
|
85
|
+
|
|
86
|
+
- **The "what should I do next" answer is now actionable.**
|
|
87
|
+
Asking /project next returns a structured action — "approval
|
|
88
|
+
pending," "drift check needed," "round ready to start" — plus the
|
|
89
|
+
exact follow-up command. I'll surface the suggested next step to
|
|
90
|
+
you in plain English rather than dumping the JSON.
|
|
91
|
+
|
|
92
|
+
- **Halted projects can come back.** If a round halted because of a
|
|
93
|
+
spec drift, a halted child, or a runtime failure, /project resume
|
|
94
|
+
re-schedules it for the poller and re-actives the project. Rounds
|
|
95
|
+
that hit the three-attempt failure cap need an explicit force flag
|
|
96
|
+
before resuming, and the counter resets so the runner gets a fresh
|
|
97
|
+
budget.
|
|
98
|
+
|
|
99
|
+
- **Permanently shelving a project is a single command.**
|
|
100
|
+
/project abandon clears all pending auto-advance scheduling and
|
|
101
|
+
marks the project as abandoned. Children stay where they were —
|
|
102
|
+
you don't lose any pipeline progress; you just stop the auto-loop.
|
|
103
|
+
|
|
104
|
+
## Summary of New Capabilities
|
|
105
|
+
|
|
106
|
+
- `.claude/skills/instar-project/SKILL.md` — twelve-command skill body
|
|
107
|
+
with frontmatter `user_invocable: true`. Each command section
|
|
108
|
+
documents its backing endpoint, body schema, response shape, and
|
|
109
|
+
expected error codes. Final "Conversational rendering" section pins
|
|
110
|
+
the don't-paste-curl rule.
|
|
111
|
+
- `POST /projects/:id/run-round` — body `{roundIndex: number}`.
|
|
112
|
+
Returns 200 `{id, roundIndex, scheduledAt, version}` on accept,
|
|
113
|
+
409 `{error, code, reason}` on preflight reject, 404 on out-of-range
|
|
114
|
+
round, 503 if `ProjectRoundRunner` is not wired. OCC-protected via
|
|
115
|
+
`tracker.update(... ifMatch: project.version)`.
|
|
116
|
+
- `POST /projects/:id/resume` — body `{roundIndex?: number,
|
|
117
|
+
force?: boolean}`. Returns 200 `{id, roundIndex, scheduledAt,
|
|
118
|
+
forced, version}` on accept, 409 when the round is neither halted
|
|
119
|
+
nor failed, 409 when failed-at-cap without `force`. Restores
|
|
120
|
+
`project.status` from `'halted'`/`'abandoned'` to `'active'` and
|
|
121
|
+
zeroes `resumeAttempts` on forced accept.
|
|
122
|
+
- `POST /projects/:id/abandon` — no body required. Returns 200
|
|
123
|
+
`{id, status: 'abandoned', version}` on accept, 200
|
|
124
|
+
`{... alreadyAbandoned: true}` on idempotent repeat, 409 with
|
|
125
|
+
`activeRound` set when a round is in-progress. Clears
|
|
126
|
+
`autoAdvanceAt` on every remaining round.
|
|
127
|
+
- `skillCommandForAction()` now emits `/project ack` for both
|
|
128
|
+
`await-user-approval` and `ack-required` action verbs, and
|
|
129
|
+
`/project drift` for `run-drift-check`. Aligns the dashboard hint
|
|
130
|
+
strings with the spec § 1.7 canonical command set.
|
|
131
|
+
|
|
132
|
+
## Evidence
|
|
133
|
+
|
|
134
|
+
Not reproducible in dev — this release is a feature addition, not a
|
|
135
|
+
regression fix. The words "halted", "failed", "regression repair",
|
|
136
|
+
and "skill drift" in What Changed describe design properties of the
|
|
137
|
+
state machine and the structural test the PR introduces:
|
|
138
|
+
|
|
139
|
+
- "halted" / "failed" (resume route): these are valid round states
|
|
140
|
+
in the existing `RoundStatus` union; the new endpoint's job is to
|
|
141
|
+
transition out of them. Not a fix for an observed halt incident.
|
|
142
|
+
- "skill drift" (structural test): the new
|
|
143
|
+
`instar-project-skill.test.ts` asserts every endpoint documented
|
|
144
|
+
in SKILL.md exists in `routes.ts`. The "drift" is the test's
|
|
145
|
+
failure mode if a future PR renames a route without updating the
|
|
146
|
+
skill — a preventive guard, not the patching of a prior incident.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Side-Effects Review — project-scope Phase 1.7 skill surface
|
|
2
|
+
|
|
3
|
+
**Scope.** Closes the spec § Phase 1.7 user-facing surface. Rewrites
|
|
4
|
+
`.claude/skills/instar-project/SKILL.md` to cover all twelve canonical
|
|
5
|
+
commands, adds the three small HTTP routes the spec named but hadn't
|
|
6
|
+
materialized (`run-round`, `resume`, `abandon`), and realigns
|
|
7
|
+
`skillCommandForAction()` to the canonical command names.
|
|
8
|
+
|
|
9
|
+
## What's IN this PR
|
|
10
|
+
|
|
11
|
+
1. **`.claude/skills/instar-project/SKILL.md`** — full rewrite. Phase 1a
|
|
12
|
+
placeholder language for `/project next` is removed (the connect-the-dots
|
|
13
|
+
PR shipped the structured payload; the skill now documents that contract).
|
|
14
|
+
Every command from spec § 1.7 has a section header with its backing
|
|
15
|
+
endpoint, body schema, response codes, and a one-line rendering rule.
|
|
16
|
+
Final section pins the conversational-tone rule ("never paste a curl
|
|
17
|
+
command in a user-facing reply") that Echo's CLAUDE.md enforces.
|
|
18
|
+
2. **`POST /projects/:id/run-round`** — manual round trigger. On
|
|
19
|
+
`preflight.ok`, writes `autoAdvanceAt = now` to the named round and
|
|
20
|
+
returns 200 + `scheduledAt`. The auto-advance poller (wired in the
|
|
21
|
+
connect-the-dots PR) picks it up on its next tick (≤60s) and dispatches
|
|
22
|
+
`ProjectRoundExecution.runRound()`. No child-process spawn from the
|
|
23
|
+
request handler — keeps one fire path through one lock.
|
|
24
|
+
3. **`POST /projects/:id/resume`** — clears `haltedAt`/`haltReason`,
|
|
25
|
+
schedules the round via `autoAdvanceAt = now`, transitions
|
|
26
|
+
`project.status` from `'halted'`/`'abandoned'` back to `'active'`.
|
|
27
|
+
For `status: 'failed'` rounds at the 3-attempt cap, `{force: true}`
|
|
28
|
+
is required AND the `resumeAttempts` counter resets to zero on
|
|
29
|
+
accept. Returns 409 when the round is neither halted nor failed.
|
|
30
|
+
4. **`POST /projects/:id/abandon`** — sets `project.status = 'abandoned'`,
|
|
31
|
+
strips `autoAdvanceAt` from every remaining round so the poller
|
|
32
|
+
stops considering them, leaves each child's `pipelineStage`
|
|
33
|
+
untouched. Idempotent (`alreadyAbandoned: true` on repeat).
|
|
34
|
+
Refuses 409 when any round is `in-progress` — halt first.
|
|
35
|
+
5. **`skillCommandForAction()` realignment.** The connect-the-dots PR
|
|
36
|
+
returned `/project approve` and `/project drift-check` for the
|
|
37
|
+
`await-user-approval` and `run-drift-check` action verbs. Spec § 1.7
|
|
38
|
+
uses `/project ack` and `/project drift`; the mapping is updated so
|
|
39
|
+
the dashboard hints + the `/projects/:id/next` payload + the skill
|
|
40
|
+
stay in sync.
|
|
41
|
+
6. **Tests.** `tests/unit/instar-project-skill.test.ts` (28 assertions)
|
|
42
|
+
pins the SKILL.md contract — frontmatter, every spec § 1.7 command
|
|
43
|
+
has a header, every documented endpoint exists in `routes.ts`.
|
|
44
|
+
`tests/integration/projects-api.test.ts` adds 10 cases for the new
|
|
45
|
+
routes covering happy paths + every documented error case.
|
|
46
|
+
|
|
47
|
+
## What's explicitly DEFERRED
|
|
48
|
+
|
|
49
|
+
- **`ProjectRoundRunner.resume` / `.abandon` methods.** The new
|
|
50
|
+
endpoints mutate state via `tracker.update()` directly rather than
|
|
51
|
+
routing through a runner method. The runner's value-add is the
|
|
52
|
+
`preflight()` gate; resume/abandon don't have preflight semantics
|
|
53
|
+
(they're transitions OUT of stuck states, not transitions INTO an
|
|
54
|
+
autonomous run). If a future need surfaces — e.g., resume needs to
|
|
55
|
+
hold the round-runner lock briefly, or abandon needs to release a
|
|
56
|
+
worktree — those can land as runner methods then. Today, the
|
|
57
|
+
shorter path is cleaner.
|
|
58
|
+
- **Resume worktree cleanup.** Spec § 1.5 step "Halt switch" says
|
|
59
|
+
"Worktrees retained for inspection (cleanup deferred to user
|
|
60
|
+
`/project resume` or `/project abandon`)." The new resume/abandon
|
|
61
|
+
routes do NOT prune worktrees — the autonomous child the poller
|
|
62
|
+
fires next will recreate them lazily. Worktree GC is a separate
|
|
63
|
+
follow-up (registered as a child of the spec's deferral list).
|
|
64
|
+
- **No telemetry on action verbs.** The dashboard hint
|
|
65
|
+
(`skillCommandForAction()`) is read-only suggestion; we don't yet
|
|
66
|
+
count which verbs fire most frequently. A simple counter on the
|
|
67
|
+
`/projects/:id/next` handler would surface "what's the most common
|
|
68
|
+
unblocking step?" — interesting telemetry, but not Phase 1 scope.
|
|
69
|
+
- **Skill body translation hooks.** The SKILL.md is a single file; we
|
|
70
|
+
don't (yet) split per-command bodies into separate files the agent
|
|
71
|
+
can lazy-load. At ~10KB the file is well within the context budget
|
|
72
|
+
for a `/project` invocation. If the surface keeps growing past
|
|
73
|
+
Phase 1, splitting per-command is the natural next move.
|
|
74
|
+
|
|
75
|
+
## Side-effects review
|
|
76
|
+
|
|
77
|
+
**Over-block.** All three new routes are pure tracker.update() calls
|
|
78
|
+
with OCC. No child-process spawn, no git shell-out, no LLM call.
|
|
79
|
+
P99 latency in test runs ~20ms (single tracker write + JSON encode).
|
|
80
|
+
The skill body grew from ~3.5KB to ~10KB — adds one file read per
|
|
81
|
+
`/project` invocation; well below the context-window pressure
|
|
82
|
+
threshold.
|
|
83
|
+
|
|
84
|
+
**Under-block.** `resume` does not re-run drift before scheduling.
|
|
85
|
+
The poller's preflight (which fires after the route writes
|
|
86
|
+
`autoAdvanceAt`) re-runs drift if the cached verdict is stale (>24h)
|
|
87
|
+
per spec § Phase 1.4. So drift is enforced at fire time, not at
|
|
88
|
+
resume time — same level as the original `/project run-round`. This
|
|
89
|
+
is intentional: resume is a state mutation, not a fire trigger.
|
|
90
|
+
|
|
91
|
+
**Level-of-abstraction fit.** Each new route sits at the same layer
|
|
92
|
+
as the surrounding routes:
|
|
93
|
+
- `run-round` belongs alongside `halt` and `ack` (all three are
|
|
94
|
+
user-initiated state changes that thread through the runner).
|
|
95
|
+
- `resume` and `abandon` belong alongside `accept-partial` (all
|
|
96
|
+
three close out a specific round-end condition).
|
|
97
|
+
|
|
98
|
+
The skill is the highest layer — it's the conversational shell over
|
|
99
|
+
the HTTP API. SKILL.md is markdown the agent reads on `/project`
|
|
100
|
+
invocation, not executable code; the body's instructions ARE the
|
|
101
|
+
implementation.
|
|
102
|
+
|
|
103
|
+
**Signal vs authority.** The new routes are authority paths — they
|
|
104
|
+
mutate state. None of them consult an LLM signal. `run-round` does
|
|
105
|
+
delegate to `ProjectRoundRunner.preflight()` for the deterministic
|
|
106
|
+
gate checks; that's the spec's single-chokepoint pattern, not a
|
|
107
|
+
signal/authority mix.
|
|
108
|
+
|
|
109
|
+
**Interactions.**
|
|
110
|
+
- `run-round` + `auto-advance poller`: the route writes `autoAdvanceAt`,
|
|
111
|
+
the poller reads it. The poller's `inFlight: Set<projectId>` guard
|
|
112
|
+
(shipped in connect-the-dots) ensures a manual run-round followed
|
|
113
|
+
by a poller tick doesn't double-fire.
|
|
114
|
+
- `resume` + `halt`: idempotent in either direction. `halt` writes
|
|
115
|
+
`haltedAt`; `resume` strips it. Repeat halts on an already-halted
|
|
116
|
+
round are no-ops; repeat resumes on an already-resumed round
|
|
117
|
+
return 409 ("round is not halted or failed").
|
|
118
|
+
- `abandon` + `claim-ownership`: abandon sets `project.status =
|
|
119
|
+
'abandoned'`; the auto-advance poller's owner filter already
|
|
120
|
+
excludes abandoned projects. A peer claiming ownership of an
|
|
121
|
+
abandoned project would just re-claim — the poller still skips
|
|
122
|
+
abandoned-status projects regardless of owner.
|
|
123
|
+
- `skillCommandForAction()` realignment: existing test
|
|
124
|
+
`expect(typeof res.body.skillCommand).toBe('string')` doesn't
|
|
125
|
+
assert exact strings, so the rename is backwards-compatible at
|
|
126
|
+
the test level. Dashboard consumers render the string verbatim —
|
|
127
|
+
they get `/project ack` instead of `/project approve`, which is
|
|
128
|
+
what spec § 1.7 documented.
|
|
129
|
+
|
|
130
|
+
**Rollback cost.** Three options:
|
|
131
|
+
1. **Skill-only rollback** — restore the prior SKILL.md from
|
|
132
|
+
`git show HEAD~1:.claude/skills/instar-project/SKILL.md`. Agent
|
|
133
|
+
reverts to Phase 1a read-only behavior; new HTTP routes still
|
|
134
|
+
exist but are not surfaced. Zero state risk.
|
|
135
|
+
2. **Routes-only rollback** — `git revert` the three new route
|
|
136
|
+
handlers. Skill still references them; agent surfaces 404 from
|
|
137
|
+
`/project run-round`, `/project resume`, `/project abandon`.
|
|
138
|
+
Surface-but-not-functional, but no data corruption — state
|
|
139
|
+
mutations all go through `tracker.update()` with OCC, which is
|
|
140
|
+
already proven safe.
|
|
141
|
+
3. **Full revert** — drop the whole commit. No on-disk state from
|
|
142
|
+
this PR persists (the abandon/halt/resume state fields all
|
|
143
|
+
pre-exist in the `Initiative` schema from Phase 1a).
|
|
144
|
+
|
|
145
|
+
All three are clean. The PR introduces no new persisted state, no
|
|
146
|
+
new file paths, no new background work — it adds surface area over
|
|
147
|
+
the existing state machine.
|
|
148
|
+
|
|
149
|
+
## Files touched
|
|
150
|
+
|
|
151
|
+
- `.claude/skills/instar-project/SKILL.md` (rewritten)
|
|
152
|
+
- `src/server/routes.ts` (three new route handlers + one mapping fix)
|
|
153
|
+
- `tests/integration/projects-api.test.ts` (10 new cases)
|
|
154
|
+
- `tests/unit/instar-project-skill.test.ts` (new file, 28 assertions)
|
|
155
|
+
- `upgrades/NEXT.md` (new release notes)
|
|
156
|
+
|
|
157
|
+
## Spec references
|
|
158
|
+
|
|
159
|
+
- `docs/specs/PROJECT-SCOPE-SPEC.md` § Phase 1.7 — canonical
|
|
160
|
+
command list.
|
|
161
|
+
- `docs/specs/PROJECT-SCOPE-SPEC.md` § Phase 1.5 — runner preflight
|
|
162
|
+
contract; halt/resume worktree-retention rule.
|
|
163
|
+
- `docs/specs/PROJECT-SCOPE-SPEC.md` § Phase 1.3 — HTTP layer
|
|
164
|
+
doesn't enforce gates the runner already enforces. `run-round`
|
|
165
|
+
delegates to `preflight()` cleanly.
|
|
166
|
+
|
|
167
|
+
## Coverage
|
|
168
|
+
|
|
169
|
+
- Unit: structural coverage of SKILL.md + endpoint existence —
|
|
170
|
+
`tests/unit/instar-project-skill.test.ts`.
|
|
171
|
+
- Integration: happy + error paths for all three new routes against
|
|
172
|
+
the real `AgentServer` + `InitiativeTracker` + `ProjectRoundRunner`
|
|
173
|
+
— `tests/integration/projects-api.test.ts`.
|
|
174
|
+
- Lint: clean.
|
|
175
|
+
- Type-check: clean (new `RoundStatus` import).
|