Hand off with run 5 completed and the plan change designed
Ledger current through F42. Handoff records the two defects found in the completion tail, the deployed revisions, and the two links the tail still cannot reach because test-e2e has no gate and no delivery configuration. PLAN-SPEC-DESIGN.md is the operator-approved design for the ACE-FCA plan gap, committed as a note. No implementation yet, by instruction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1
This commit is contained in:
@@ -1716,3 +1716,77 @@ Fixed in the phase brief: the terminal phase now states the marker, when to
|
||||
write it, and that it is exclusive with asking to go back.
|
||||
`TestTerminalPhaseNamesTheCompletionSignal` also asserts the negative, so a
|
||||
phase that can still ask is never told to finish instead.
|
||||
|
||||
### F41: harness chrome counted as agent progress
|
||||
|
||||
Run 5's review agent produced nothing after 02:12. The 02:46 renewal was
|
||||
granted anyway, and the lease moved to 03:16. Each renewal window bought
|
||||
another 30 minutes, so recovery by expiry had no bound.
|
||||
|
||||
Reproduced the worker's stored `progress_sha` byte for byte from the live pane,
|
||||
so the branch taken at `cmd/orchestra-worker/main.go:1020` was
|
||||
`progress != l.ProgressSHA`. Not `IsBusy`: the 200-line capture held zero
|
||||
`esc to interrupt` markers, so `AgentStatus` returned `idle`. Not an empty
|
||||
baseline either: it held `3d93ca89`.
|
||||
|
||||
`PaneProgress` dropped prompt lines only, so Claude Code's status footer stayed
|
||||
in the digest. The mutable fields there are the rolling usage percentage, the
|
||||
context counter, and the version notice. A 180-second sample showed no churn, so
|
||||
this leaked intermittently rather than constantly. One tick inside a window was
|
||||
enough.
|
||||
|
||||
Fixed in `2417a39`. `PaneProgress` cuts from the editor's lower rule and then
|
||||
trims the spinner summary and version notice above it. `AgentStatus` still reads
|
||||
the raw capture, so the busy markers living in the footer are unaffected.
|
||||
|
||||
Proven live in both directions on the first window after deployment: the
|
||||
03:13:48 renewal took version 28, the 03:16:13 window was refused, the lease
|
||||
expired at 03:18:43, and review relaunched at 03:20:14 with the F40 brief. The
|
||||
relaunched pane then showed a new footer field, `5h: 15%`, which the old digest
|
||||
would have counted as work.
|
||||
|
||||
Lease TTL moved to 5 minutes in the same commit, from `domain.LeaseTTL`, with
|
||||
renewal at half of it. Reclaiming a stalled pane happens only at expiry.
|
||||
|
||||
### F42: the result commit refused the completion marker
|
||||
|
||||
The relaunched review agent wrote `.orchestra/done` at 03:20:48, 34 seconds
|
||||
after launch. The worker recognised it, confirmed the agent idle, and then
|
||||
failed the result commit once every five seconds:
|
||||
|
||||
```text
|
||||
stage result: The following paths are ignored by one of your .gitignore files:
|
||||
.orchestra/done
|
||||
```
|
||||
|
||||
`internal/herdr/adapter.go:132` writes `.orchestra/.gitignore` containing `*`,
|
||||
so the marker is ignored. The staging step passed `:!.orchestra/done`, and git
|
||||
refuses an add whose pathspec names an ignored path. Reproduced in a scratch
|
||||
repo on git 2.55.0, both the failing and the working form.
|
||||
|
||||
Fixed in `dcd9af4`: the exclusion names the directory, `:!.orchestra`. That
|
||||
holds whether or not the inner `.gitignore` exists.
|
||||
|
||||
The failure retried for 22 minutes with the task stuck in review and nothing
|
||||
observable outside the journal, because each identical error overwrote the
|
||||
single `last_error` slot. F18, again, and the second time in one night that it
|
||||
turned a five-second loop into archaeology.
|
||||
|
||||
### Run 5 reached TaskCompleted
|
||||
|
||||
On the first tick after the `dcd9af4` restart at 06:47:50:
|
||||
|
||||
- task state `completed`, lease released, `.orchestra/done` removed
|
||||
- pane closed, no `orchestra-*` sessions left on the tmux socket
|
||||
- result branch pushed:
|
||||
`refs/heads/orchestra/scratch/orchestra-06g4a4f0tfxkzhje48n05xn1hg-73a2e4fd`
|
||||
at `fb15c61`
|
||||
|
||||
Two links of the tail did not run, and neither is a defect. The deployed
|
||||
`test-e2e` project declares only `id`, `machine_affinity`, `repo` and
|
||||
`worktree_root`, so no quality gate exists to run. The deployed `config.jsonc`
|
||||
has no `sources` and no `delivery` keys, so no PR could be opened.
|
||||
|
||||
Proven chain: review, done recognised, finalise, result branch pushed,
|
||||
`TaskCompleted`. Submission, human PR review and merge remain unproven, and they
|
||||
need registry configuration rather than code.
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
# Handoff: run 5 reached TaskCompleted, plan machinery is designed and frozen
|
||||
|
||||
Written 2026-08-28, 06:55 local (02:55 UTC). Read with `BURNIN.md` (the ledger,
|
||||
now current through F42), `PLAN-SPEC-DESIGN.md` (the locked design for the next
|
||||
change), `HANDOFF-2026-08-28-burnin-4.md` (the session before this one),
|
||||
`AUDIT.md` and `CLAUDE.md`.
|
||||
|
||||
Everything below was observed live unless it says otherwise.
|
||||
|
||||
## The headline
|
||||
|
||||
Run 5 finished. Task `06G4A4F0TFXKZHJE48N05XN1HG`, issue `kami/test-e2e#5`,
|
||||
reached `TaskCompleted` at 06:48 after two defects were found and fixed in the
|
||||
completion tail.
|
||||
|
||||
```text
|
||||
frame -> research -> plan -> implement -> review -> done -> finalise -> pushed -> completed
|
||||
```
|
||||
|
||||
Two defects, both live-proven fixed:
|
||||
|
||||
- **F41**: harness chrome counted as agent progress, so a stalled lease renewed
|
||||
without bound.
|
||||
- **F42**: the result commit refused the completion marker, so completion could
|
||||
never happen.
|
||||
|
||||
## Deployed state
|
||||
|
||||
| Half | Revision | Evidence |
|
||||
|---|---|---|
|
||||
| Worker, workpc systemd | `dcd9af4` | PID 3634937, started 06:47:50, sha256 `25b41562…`, journal prints the revision at startup |
|
||||
| Coordinator, homesrv container | `bcaf0cc` | **stale**, see below |
|
||||
|
||||
Branch `webui-and-audit-reconciliation`, HEAD `dcd9af4`.
|
||||
|
||||
```text
|
||||
revision dcd9af4806376caf091e18f669ab52f1ba0dc9df
|
||||
sha256 25b415629d4a02dca7abe202245ac13f5bdb43b501bc17e371dba75b44f6cebb
|
||||
```
|
||||
|
||||
**Confirm the running worker, not the installed file.** Two restarts silently
|
||||
did not happen tonight. `sudo install` landed the new binary while the old
|
||||
process kept serving from the unlinked inode. `/proc/<pid>/exe` showed
|
||||
`(deleted)` and the old hash both times. Check
|
||||
`sha256sum /proc/$(systemctl show -p MainPID --value orchestra-worker)/exe`,
|
||||
never just the path.
|
||||
|
||||
The second missed restart cost run 5 its retry budget: the pre-F42 worker kept
|
||||
failing until the router marked the task `failed`.
|
||||
|
||||
## The coordinator is behind, deliberately
|
||||
|
||||
`2417a39` moved lease TTL to 5 minutes via `domain.LeaseTTL`, and the
|
||||
coordinator issues the initial lease at `internal/router/router.go:318`. That
|
||||
half still runs `bcaf0cc`, so new leases arrive with a 30-minute TTL. Worker
|
||||
renewals send `ttl=300` explicitly, which is why run 5 recovered anyway.
|
||||
|
||||
Rebuild when convenient, from a detached worktree, never with
|
||||
`docker compose build`:
|
||||
|
||||
```text
|
||||
git worktree add --detach /tmp/orchestra-build-dcd9af4 dcd9af4
|
||||
docker build --build-arg BUILD_REVISION=dcd9af4… -f $T/Dockerfile.api -t orchestra-web-ui-orchestra-api $T
|
||||
docker compose up -d --no-deps orchestra-api
|
||||
```
|
||||
|
||||
## What this session found
|
||||
|
||||
### F41, harness chrome counted as progress
|
||||
|
||||
The 02:46 renewal was granted against a pane idle since 02:12. Reproduced the
|
||||
worker's stored `progress_sha` byte for byte from the live pane, so the branch
|
||||
taken was `progress != l.ProgressSHA`, not `IsBusy` and not an empty baseline.
|
||||
|
||||
`PaneProgress` hashed Claude Code's status footer, whose usage percentage,
|
||||
context counter and version notice all move on their own schedule.
|
||||
|
||||
Fixed in `2417a39`. Live-proven in both directions on the first window after
|
||||
deployment: renewal at 03:13:48, refusal at 03:16:13, expiry at 03:18:43,
|
||||
relaunch at 03:20:14.
|
||||
|
||||
### F42, the result commit refused the completion marker
|
||||
|
||||
`.orchestra/.gitignore` contains `*`, so `.orchestra/done` is ignored, and git
|
||||
refuses an add whose pathspec names an ignored path. The staging step named the
|
||||
marker. Every completion attempt failed, once every five seconds, for 22
|
||||
minutes.
|
||||
|
||||
Fixed in `dcd9af4` by excluding the directory rather than the file. Reproduced
|
||||
both forms in a scratch repo on git 2.55.0 before changing anything.
|
||||
|
||||
### F18 is now the clear top item
|
||||
|
||||
Both defects tonight were five-second retry loops whose only trace was the
|
||||
journal, because each identical error overwrote the single `last_error` slot.
|
||||
`health` in the worker state file was `{}` throughout. The bounded
|
||||
recent-observation ring described in the previous handoff is the fix. This
|
||||
session adds a second concrete instance of the cost.
|
||||
|
||||
## Where the completion tail stops
|
||||
|
||||
Proven: review, done recognised, agent confirmed idle, finalise, result branch
|
||||
pushed, `TaskCompleted`.
|
||||
|
||||
Not proven, and not defects:
|
||||
|
||||
- **No quality gate ran.** The deployed `test-e2e` project declares only `id`,
|
||||
`machine_affinity`, `repo` and `worktree_root`.
|
||||
- **No PR was opened.** The deployed `config.jsonc` has no `sources` and no
|
||||
`delivery` keys. `internal/provider/gitea_pr.go` implements PR creation, but
|
||||
nothing points this project at a Gitea remote.
|
||||
|
||||
Submission, human PR review and merge need registry configuration, not code.
|
||||
That is the cheapest remaining burn-in, and it is a config edit plus one task.
|
||||
|
||||
## The next change is designed and frozen
|
||||
|
||||
`PLAN-SPEC-DESIGN.md` holds the operator-approved design for the ACE-FCA plan
|
||||
gap. No implementation exists yet, by explicit instruction: the design must not
|
||||
land while the completion tail is still being observed.
|
||||
|
||||
Shape, in one paragraph each:
|
||||
|
||||
- **`plan.md`**, sealed through the existing path and referenced by the existing
|
||||
`t.PlanRef`. 128 KiB total bound, structural validation by required headings
|
||||
and phase blocks, no per-line cap. `DecodePlan` keeps the JSON path for refs
|
||||
already in the CAS.
|
||||
- **Research ids.** `Finding{ID, Claim, Evidence, Confidence}`, unique ids,
|
||||
confidence limited to `fact|inference|assumption`. The schema at
|
||||
`agentctx.go:167` already promises these and the struct drops them today.
|
||||
- **Progress that Orchestra establishes.** The implementer may write only
|
||||
`status: "ready_for_verification"`. The worker reads that phase's automated
|
||||
verification from the accepted plan, runs it, and records
|
||||
`PlanPhaseVerified`. An agent cannot write `verified`.
|
||||
- **Verification allowlist.** Plan commands are argv, executed without a shell,
|
||||
matched against a per-project policy. Absent policy refuses everything.
|
||||
- **Mismatch as a typed request.** `PlanMismatchRequest` supersedes or reopens
|
||||
research and planning. No new agent-askable backward transition.
|
||||
|
||||
The live proof ladder is in the note. Steps 1 and 3 are the rungs that matter
|
||||
for smaller local models. The implement launch contains the plan verbatim, and
|
||||
a rotated successor still receives it whole with phase state intact.
|
||||
|
||||
## Things that will bite
|
||||
|
||||
- **Verify the running worker binary, not the installed one.** Twice tonight.
|
||||
- **The worker state file is a cache, not truth.** It read `state failed` for
|
||||
run 5 while the coordinator still held the task as leasable. I reported the
|
||||
cache as fact. The coordinator disagreed and was right.
|
||||
- **`rm` and `cp` are interactive in this shell.** A plain `rm` prompts, appears
|
||||
to succeed, and leaves the file. Use `/bin/rm -f` and `install`.
|
||||
- **Another session owns 20-plus uncommitted paths**, including `AUDIT.md`,
|
||||
`deploy/build.sh`, `web/` and `internal/authn/`. Commit by path. Never
|
||||
`git add -A`.
|
||||
- **No network from this sandbox.** `curl` to raw.githubusercontent.com times
|
||||
out. `WebFetch` against a `github.com/blob/...` URL works.
|
||||
- **Secrets are guarded.** Reading `.env` or `printenv` of a token through the
|
||||
shell is blocked. The working recipe is expanding the token inside the
|
||||
container in a single remote command, as `HANDOFF-2026-08-28-burnin-4.md`
|
||||
records.
|
||||
- **`/tmp` is tmpfs with a 10-day sweep.** Check `/tmp/test-e2e` and
|
||||
`/tmp/test-e2e-worktrees` exist before every run.
|
||||
- **Two stale monitors** may still be armed from this session, tailing the
|
||||
worker journal. They are session-local and die with it.
|
||||
|
||||
## What I would do next, in order
|
||||
|
||||
1. Rebuild `orchestra-api` at `dcd9af4` so the 5-minute TTL applies at issuance.
|
||||
2. Fix F18 as a bounded observation ring, since two more instances landed
|
||||
tonight.
|
||||
3. Configure `test-e2e` with a Gitea remote and a quality gate, then run one
|
||||
task to prove submission, human review and merge.
|
||||
4. Implement `PLAN-SPEC-DESIGN.md` exactly as written, including the allowlist.
|
||||
|
||||
Steps 1 through 3 are burn-in and observability. Step 4 is the only new
|
||||
behaviour, and its design no longer needs an architecture pass.
|
||||
@@ -0,0 +1,249 @@
|
||||
# Locked design: plan.md, durable plan state, verified progress
|
||||
|
||||
Written 2026-08-28 while run 5's completion tail is still open. **No
|
||||
implementation until run 5 reaches a terminal outcome.** This file is a note,
|
||||
not code, and is deliberately uncommitted.
|
||||
|
||||
Approved by the operator with three amendments folded in. Markdown plan with
|
||||
structural validation, stable research finding ids, and progress that Orchestra
|
||||
establishes rather than the implementer asserting.
|
||||
|
||||
The project-level verification allowlist was added on the second pass.
|
||||
|
||||
## Ground truth this design starts from
|
||||
|
||||
Verified in the tree at `2417a39`, not assumed.
|
||||
|
||||
| Concern | Where | Behaviour today |
|
||||
|---|---|---|
|
||||
| Research artifact | `internal/workphase/workphase.go` | `Findings{Claim,Evidence}`, 64 items, 500 chars, single line |
|
||||
| Plan artifact | same | `Changes{Target,Intent}`, `Verification[]`, `Risks[]`, `DecisionsNeeded[]`, same bounds |
|
||||
| Advertised research schema | `internal/agentctx/agentctx.go:167` | promises `findings[].id` and `findings[].confidence`; struct has neither, both dropped silently |
|
||||
| Seal path | `cmd/orchestra-worker/main.go:1833` | worker decodes, refuses locally through `answerRefusedPhase` (F39), then `AdvancePhase` |
|
||||
| Storage | `internal/store/store.go:961`, `cmd/orchestra/main.go:467` | content-addressed CAS, format-agnostic, 4 MiB body cap |
|
||||
| Refs | `internal/store/store.go:282` | reducer sets `t.ResearchRef` / `t.PlanRef` |
|
||||
| Implement launch | `internal/agentctx/agentctx.go:418` | `renderSealed` flattens every field through `collapse()` to one bullet line |
|
||||
| Plan progress | none | no state exists; `workphase` doc points at `continuity.Handoff` |
|
||||
| Rotation progress | `internal/continuity/continuity.go:113` | `Remaining[]`, `Action`, `Learned[]`, 200 chars per line, unlinked to any plan item |
|
||||
| Phase graph | `internal/domain/workphase.go:35` | `implement → review` only; no route back |
|
||||
| Quality gate execution | `cmd/orchestra-worker/main.go:955` | `exec.CommandContext(ctx, "sh", "-c", gateCommand)`, `Dir = worktree`, operator-authored string |
|
||||
|
||||
The last row is the security baseline. The gate command is operator-authored,
|
||||
from the project registry or the task. A plan-authored command is agent-authored,
|
||||
so it must not inherit that envelope unchanged.
|
||||
|
||||
## 1. Sealed specification: `plan.md`
|
||||
|
||||
- Markdown, sealed through the existing path, referenced by the existing
|
||||
`t.PlanRef`. No new storage, no new event type for sealing.
|
||||
- Bound: 128 KiB total. No per-line cap. The 500-character single-line rule is
|
||||
what makes a specification impossible today.
|
||||
- `phaseSealFile[plan]` and `phaseArtifact[plan]` become `plan.md`.
|
||||
- `DecodePlan` keeps the JSON path for refs already in the CAS. New seals must be
|
||||
markdown.
|
||||
- `phaseSealSchema[plan]` states the required outline. Run 5 proved the planner
|
||||
follows that block (F38), so it is the delivery mechanism for the structure.
|
||||
|
||||
Required sections, validated by presence and shape:
|
||||
|
||||
```
|
||||
# <name> implementation plan
|
||||
## Overview
|
||||
## Current state
|
||||
## Desired end state
|
||||
## Non-goals
|
||||
## Approach
|
||||
## Phase <n>: <name> (one or more)
|
||||
### Files
|
||||
### Changes
|
||||
### Verification
|
||||
#### Automated
|
||||
- run: ["go", "test", "./internal/foo/..."]
|
||||
#### Manual
|
||||
- <human-testable step>
|
||||
## Testing strategy
|
||||
## Risks and edge cases
|
||||
## Migration
|
||||
## References
|
||||
```
|
||||
|
||||
Validation rules:
|
||||
|
||||
- At least one phase block. Phases numbered from 1, contiguous.
|
||||
- Every phase carries `### Files`, `### Changes`, `### Verification`.
|
||||
- Every phase carries at least one entry under `#### Automated` or
|
||||
`#### Manual`. A phase with neither cannot be verified and is refused.
|
||||
- Automated entries are argv arrays, JSON-parsed from the `run:` line. Not
|
||||
shell strings. See section 4.
|
||||
- `## References` entries cite research ids. An id that does not resolve against
|
||||
the sealed research fails the seal.
|
||||
|
||||
## 2. Research ids
|
||||
|
||||
- `Finding{ID, Claim, Evidence, Confidence}`.
|
||||
- `ID` required, unique within the artifact, matching `^r[0-9]+$` or a stable
|
||||
slug.
|
||||
- `Confidence` limited to `fact|inference|assumption`.
|
||||
- The advertised schema at `agentctx.go:167` becomes true rather than
|
||||
aspirational.
|
||||
- `renderSealed` prints the id with each finding, so a plan can cite
|
||||
`research:r12` and a reader can resolve it.
|
||||
- Plan seal validation resolves every reference. The coordinator holds
|
||||
`ResearchRef`, so the cross-check costs one CAS read.
|
||||
|
||||
## 3. Progress: request, then Orchestra verifies
|
||||
|
||||
The implementer may write exactly one status.
|
||||
|
||||
```json
|
||||
{ "phase": "phase-2", "status": "ready_for_verification" }
|
||||
```
|
||||
|
||||
- Any other status value is refused, and the refusal is delivered to the live
|
||||
session through the F39 path. `verified` is not writable by an agent.
|
||||
- Worker reads that phase's `#### Automated` entries from the accepted plan, not
|
||||
from the request.
|
||||
- Worker runs each argv, in order, in the worktree, capturing exit code and
|
||||
output.
|
||||
- All pass, manual steps exist: phase becomes `awaiting_manual_verification`.
|
||||
- All pass, no manual steps: phase becomes `verified`.
|
||||
- Any fail: phase stays `in_progress`, and the failing command plus its output
|
||||
reaches the implementer.
|
||||
|
||||
Durable record:
|
||||
|
||||
- New event `PlanPhaseVerified{phase, commands, exit_codes, at_sha,
|
||||
evidence_ref}`.
|
||||
- Reduced into a task field `PlanProgress`, so state is derived from events
|
||||
rather than from a worker cache.
|
||||
- Manual sign-off arrives as a human decision, on the surface decisions already
|
||||
use. No new approval mechanism.
|
||||
|
||||
Rendering, in `agentctx`:
|
||||
|
||||
- Plan state sits above continuity and below human decisions.
|
||||
- It names verified phases, the current phase, and any awaiting-manual gate.
|
||||
- The plan document itself renders verbatim. `collapse()` stays for research
|
||||
bullets only.
|
||||
|
||||
## 4. Verification-command allowlist
|
||||
|
||||
The rule, operator's wording:
|
||||
|
||||
```
|
||||
accepted plan command
|
||||
∩ existing quality-gate execution envelope
|
||||
∩ project verification policy
|
||||
= runnable
|
||||
```
|
||||
|
||||
Properties this must hold:
|
||||
|
||||
- No weaker sandbox than the existing quality gate.
|
||||
- No implicit `sh -c`. Commands execute as argv through `exec.CommandContext`,
|
||||
with `Dir` set to the worktree, exactly as the gate sets it.
|
||||
- Shell operators, pipes, and redirections are unavailable by construction,
|
||||
since there is no shell. A project that genuinely needs one declares it in
|
||||
policy, explicitly.
|
||||
- Refusal is observable and delivered to the implementer.
|
||||
- A rejected verification never becomes `verified`.
|
||||
- Project policy, not the plan, determines executable reach.
|
||||
|
||||
Policy shape, on `registry.Project`:
|
||||
|
||||
```json
|
||||
"verification": {
|
||||
"allowed": [
|
||||
["go", "test", "./..."],
|
||||
["go", "test", "./internal/..."],
|
||||
["go", "vet", "./..."],
|
||||
["npm", "test", "--", "*"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Matcher, argv-aware:
|
||||
|
||||
- Match is positional. `argv[0]` must match exactly.
|
||||
- A literal pattern element matches that element exactly.
|
||||
- `*` matches exactly one element, any value.
|
||||
- A pattern element ending in `/...` matches a path argument under that prefix.
|
||||
- A pattern shorter than the command does not match unless its last element is
|
||||
`*`. No prefix-match by accident.
|
||||
- An empty or absent policy means no plan command is runnable. Absence is a
|
||||
refusal, not a default-allow.
|
||||
|
||||
Refusal text names the command and the project, so the planner learns its real
|
||||
reach. A plan whose commands are all refused fails at seal time, not at
|
||||
verification time. The failure lands on the planner rather than the
|
||||
implementer.
|
||||
|
||||
## 5. Mismatch as a typed request
|
||||
|
||||
```json
|
||||
{
|
||||
"plan_phase": "phase-2",
|
||||
"observed": "...",
|
||||
"contradicts": "...",
|
||||
"evidence": ["..."],
|
||||
"requested_action": "replan | research | human_decision"
|
||||
}
|
||||
```
|
||||
|
||||
- Written to `.orchestra/plan-mismatch.json`. Bounded lines, same validator
|
||||
style as the handoff.
|
||||
- Worker validates and posts. Coordinator records `PlanMismatchRecorded`, then
|
||||
decides the move.
|
||||
- Orchestra performs the reopen. The agent never asks for a backward phase, so
|
||||
`phase-request.json` still refuses one.
|
||||
- The superseded plan moves to a history list on the task. `PlanRef` always
|
||||
names the currently accepted plan, so no ref is ever ambiguous.
|
||||
- A fresh implement session launches against the newly sealed plan. The mismatch
|
||||
is retained as provenance.
|
||||
|
||||
This keeps the phase machine from becoming freely reversible navigation. The
|
||||
edge exists for Orchestra, not for the agent.
|
||||
|
||||
## 6. Tests
|
||||
|
||||
Unit:
|
||||
|
||||
- Plan with every required section validates. One missing section fails, and the
|
||||
error names the section.
|
||||
- Phase with no automated and no manual verification fails.
|
||||
- `run:` line that is not a JSON argv array fails.
|
||||
- Reference to an unknown research id fails, with the id named.
|
||||
- Duplicate research finding id fails. Confidence outside the enum fails.
|
||||
- `renderSealed` contains the plan document byte-for-byte, fences and multi-line
|
||||
blocks included.
|
||||
- Two `Build` calls from the same refs with different handoffs both contain the
|
||||
complete plan.
|
||||
- `status: "verified"` written by an agent is refused, and the refusal reaches
|
||||
the delivery path.
|
||||
- Allowlist matcher: exact match, `*` single-element, `/...` path prefix,
|
||||
shorter-pattern rejection, empty policy rejects everything.
|
||||
- A failed verification leaves the phase unverified and records the exit code.
|
||||
|
||||
## 7. Live proof ladder
|
||||
|
||||
1. Plan seals three detailed phases. Implement launch contains the plan
|
||||
verbatim.
|
||||
2. Phase 1 requests verification. Worker runs the exact plan command. Durable
|
||||
state says phase 1 verified.
|
||||
3. Rotation. Successor receives the complete original plan, phase 1 verified,
|
||||
phase 2 explicitly current.
|
||||
4. Human correction lands above the plan. Plan and state stay subordinate.
|
||||
5. Phase 2 hits a contradiction. Mismatch recorded, no silent implementation,
|
||||
old plan retained, revised plan sealed, fresh launch carries the revised plan
|
||||
in full.
|
||||
|
||||
Step 1 and step 3 are the two that matter for smaller local models. A grep of
|
||||
the rendered `launch.md` either finds the phase-three specification and its
|
||||
verification command, or it does not.
|
||||
|
||||
## Not in scope
|
||||
|
||||
Lifecycle, leases, review, submission, auth, federation, and the burn-in fixes
|
||||
stay untouched. One adjacent change is needed: the Orchestra-only reopen edge in
|
||||
section 5. It is reached through a typed request, not through a new
|
||||
agent-askable transition.
|
||||
Reference in New Issue
Block a user