Fix 'failed to fetch workflow' parse error: ocr.yml referenced a local reusable workflow path (./.github/workflows/ocr-review.yml) that does not exist in this repo. Point at Heretek-AI/.github org reusable workflow pinned to main HEAD (a440b485).
Concurrent workers on the same exhausted tier no longer push the fleet
past the next tier: reportProviderFailure takes the failing session's
tier and ignores reports from a tier already left behind. tierModels is
parsed in the launcher constructor so a bad programmatic value fails at
startup instead of failing every task on that tier.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Pk7RD6oqBpDdpbWAYqj539
Addresses the SonarCloud quality gate on this PR: Array.sort() without a
compare function (reliability) and helpers duplicated between the
launcher and lifecycle tests (duplication on new code).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Pk7RD6oqBpDdpbWAYqj539
Closes the readiness gaps tracked in #2:
- G-1: APPROVE now calls landTask(): fleet/<task> is merged --no-ff into
the mission integration branch in a throwaway worktree, serialized
across approvals. Success records landedSha and moves the task to done;
a conflict (or any landing failure) keeps it waitingHuman, answers 409
landing-failed, and lists the conflicting files.
- G-2: done/failed/cancelled tasks release their worktree once no worker
runs in it (also swept on startup); failed/cancelled branches are then
anchored at refs/fleet-salvage/<task> via salvageWorktree().
- G-3: --roles-dir role packs (roles.ts). An expert task runs with
XDG_CONFIG_HOME=<roles-dir>/<role>, the role opencode.json copied into
the worktree .opencode/, the role prompt and SKILL.md paths in its task
prompt, and role.json maxConcurrent as its per-expert cap.
- G-4: --tier-models maps each tier to a provider/model passed as the v2
session.create model. Quota-shaped worker failures feed the tier
failover and re-queue the task without spending an attempt; quota on
the last tier pauses new launches until the retry time.
- G-5: exact dependency pins in dispatcher/ and web/ (the versions the
lockfile already resolved).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Pk7RD6oqBpDdpbWAYqj539
A comma inside a password split it into a fragment with no `@`, which
redactNatsUrl returned verbatim, leaking part of the password into the
startup and error logs.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012AuVk4nEvBXaTjs4G6wdW4
The scope refusal check in section 4 rejected .github/workflows/ci.yml,
the repo's own CI. List .github/ (CI workflows only) as allowed and show
it in the section 6 layout.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012AuVk4nEvBXaTjs4G6wdW4
Blocking:
- worker-session: runTurn no longer awaits the prompt admission request,
so a stalled POST cannot hold a turn past its timeout, abort, or worker
exit (test: never-answering prompt ends with outcome "timeout").
- web: refreshBoard drops responses superseded by a newer board request,
and loadDrilldown rechecks the selected task after reading the body, so
a late response cannot restore an old board or show another task's
drill-down and QA verdicts.
- main: redact userinfo from NATS_URL in the startup log line and the
JetStream-unreachable error (redactNatsUrl, with tests).
Minor:
- cli-args: parseArgs takes the known switch names; a value flag without
an operand and a switch given an operand now throw (new tests).
- director: reject dependency cycles in a tasks block (tests), and drop
the Director session after a timed-out turn so its late events cannot
end the next turn early.
- nats-bridge: only a missing stream is created; streams.update errors
now surface instead of being masked by streams.add.
- server: catch errors while applying a Director reply and record them
as a system message and audit event.
- approvals test: the cross-task check uses a fresh ticket and asserts
binding-mismatch instead of passing on reused-ticket.
- ui-smoke: launch Chromium inside the try so the server is always
stopped and failures are reported.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012AuVk4nEvBXaTjs4G6wdW4
Adds scripts/accept/wave-4.sh (approval attacks, launcher/Director,
NATS, web build, Chromium smoke test) to `make accept`, installs with
--frozen-lockfile --ignore-scripts, updates the wave-2/3 probes to the
pinned-key flow, and adds .github/workflows/ci.yml (pull_request + push
to main, bun 1.4.2, SHA-pinned actions, nats-server + Chromium,
make check + make accept). README documents key generation, starting
the dispatcher + UI, worker/Director configuration, and NATS.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012AuVk4nEvBXaTjs4G6wdW4
approval-client signs with crypto.subtle Ed25519 using a key file chosen
in the drawer (sealed human.key.enc unlocked with scrypt + AES-GCM, or a
PKCS#8 key), checks the signature against the pinned key and the
displayed diff hash before sending, and zeroes all secret bytes (I1).
No node:crypto or Buffer in browser code. index.html loads
./src/main.tsx; `bun run build:web` bundles to web/dist. The UI follows
named SSE events, refreshes the board, and talks to the Director over the
dispatcher. scripts/e2e/ui-smoke.ts drives the served UI in Chromium
through Director planning, a worker run, and a browser-signed approval,
asserting no console errors; screenshot in docs/evidence/ui.png.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012AuVk4nEvBXaTjs4G6wdW4
Approvals: the server loads a pinned Ed25519 human.pub (es-core format)
at startup and fails closed (503) without one. Tickets carry a
server-computed subjectHash (sha256 of the task diff) and a unique
timestamp; signatures cover es-core's canonical {runId, stage,
subjectHash, timestamp} payload (stages APPROVE/REJECT) and are verified
against the pinned key only. Client public keys and passphrases are
ignored; tickets are re-bound to the current diff at sign time.
Workers: a launcher turns scheduler decisions into `opencode serve`
processes in per-task worktrees with an isolated OPENCODE_DB, drives the
OpenCode v2 session/prompt/event API behind a small WorkerApi interface,
relays tool/status events to the bus, commits results, and kills workers
on shutdown. A long-lived Director session replies in chat; cards are
created only from a zod-validated ```tasks block (/ship kept as the
explicit fallback). Board state persists in bun:sqlite (WAL). NATS
JetStream/KV mirroring is real ([email protected].3) behind NATS_URL.
Tests cover forged keys, client-supplied keys, mismatched subject hashes,
replayed tickets/signatures, changed diffs, es-core key compatibility,
the launcher and Director against a fake opencode server, persistence,
and JetStream against nats-server.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_012AuVk4nEvBXaTjs4G6wdW4