Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ All notable changes to the claude-plugins project will be documented in this fil

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Entries are listed newest-first; each plugin section is treated as released when merged to `main`.

### vibe v1.0.84

A mid-run scope change or answer sent to a Claude writer is now acknowledged by id and watched, so a writer stuck in one long turn is caught within the hour instead of when the operator asks (ISS-13096).

#### Added
- `board.mjs deliver --kind scope` records a mid-run change to what a writer builds, with its text and the head of the writer's worktree (`writer --worktree`). `status` prints `UNACKED-SCOPE` once a told change waits 15 minutes unacknowledged, and `SCOPE-NOT-STARTED` when, 30 minutes after it was told, the worktree is still clean at that head.
- `board.mjs pending <writer>` lists the writer's unacknowledged scope changes and answered questions (`answered --text` stores the answer), exiting 1 while any remain; `ack` acknowledges a scope change by its id as it does an answer.
- `board.mjs watch-lines`, read by the watcher's snapshot, makes those flags and late questions watcher events, so an armed watcher wakes the orchestrator on each.

#### Changed
- The worker preamble says messages arrive only when the turn ends, forbids `sleep` polls as well as long foreground waits, and has the writer read its pending list before each commit and each verification phase. It replaces the "check the worker inbox" line, which named no action.
- The fleet skill sends every mid-run scope change through the board and interrupts or relaunches a writer on `UNACKED-SCOPE` or `SCOPE-NOT-STARTED`, since a resend does not reach a writer mid-turn.
- Instruction text is about 2 words shorter than in 1.0.81.

### platform v1.1.6

#### Changed
Expand Down
2 changes: 1 addition & 1 deletion plugins/vibe/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vibe",
"description": "Vibe sessions, handoff, seed refresh, and ClosedLoop ticket workflows",
"version": "1.0.83",
"version": "1.0.84",
"dependencies": ["closedloop-core"],
"author": {"name": "ClosedLoop", "email": "support@closedloop.ai"}
}
2 changes: 1 addition & 1 deletion plugins/vibe/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vibe",
"version": "1.0.83",
"version": "1.0.84",
"description": "Vibe sessions, handoff, seed refresh, and ClosedLoop ticket workflows",
"author": {"name": "ClosedLoop", "email": "support@closedloop.ai"},
"homepage": "https://github.com/closedloop-ai/claude-plugins",
Expand Down
2 changes: 1 addition & 1 deletion plugins/vibe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ harness switch instead of another worker doing its work.

## Runtime Files

Version `1.0.83` contains 12 skills, 16 agents, and one hook manifest
Version `1.0.84` contains 12 skills, 16 agents, and one hook manifest
(`hooks/hooks.json`). It has no standalone commands, root-level shell scripts,
or production Python tools under
`tools/python/`; that directory contains the skill-contract test modules and
Expand Down
1 change: 1 addition & 0 deletions plugins/vibe/skills/codex-fleet-monitor/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Start it as its own `run_in_background` call, never with `&` (a second copy exit
| `codex-usage ... LIMIT-SPENDING-CREDITS` | limit reached and turns are now consuming paid credits | tell the user at once and stop sending turns unless they approve paid credits |
| `codex-usage ... unknown` / `unreadable READFAIL` | the App Server answered without rate limits, or it or the remote host could not be read | check `codex` on that machine |
| `requests <local\|remote> <count> <newest file>` / `requests ... UNREADABLE` | a session dropped (or you removed) a delegated-work request file | hand the new request to a Claude worker, then steer its result back to the session that asked |
| `board UNACKED-SCOPE`, `SCOPE-NOT-STARTED`, `UNACKED`, `QUESTION-UNANSWERED` | a fleet-orchestrator `board.json` flag | act as fleet-orchestrator says |
| `open-prs <repo> <numbers>` | open PR list changed | add the new PR to `prs.txt` and start its review |
| `pr ... BLOCKED(unresolved threads)` | unresolved review threads (`threads=<n>`) block the merge even when approved with auto-merge armed | route each thread to the owning writer now; it is not a missing approval |
| `pr ... STALE-PR(...)` | an open PR with unresolved threads, a conflict, a draft flag or a failed check has had no push for `STALE_PR_SECONDS` (45 minutes) | ask its writer for an ETA, unblock it, or reassign it, however busy the writer looks |
Expand Down
4 changes: 4 additions & 0 deletions plugins/vibe/skills/codex-fleet-monitor/scripts/snapshot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ if [ -n "${REMOTE_HOST:-}" ]; then out=$(remote_node ratelimit.mjs remote); echo
if [ -n "${REQUESTS_DIR:-}" ]; then requests local ls -t $REQUESTS_DIR; fi
if [ -n "${REMOTE_REQUESTS_DIR:-}" ]; then requests remote ssh -o ConnectTimeout=10 ${REMOTE_HOST:?set REMOTE_HOST} "ls -t $REMOTE_REQUESTS_DIR"; fi
zsh $K/classify.sh $W/item-history.txt $STALL_SECONDS ${QUESTION_SECONDS:-900} < $W/live.txt
# Late questions, unacknowledged answers and scope changes, and a writer whose worktree has not moved since a scope
# change (fleet-orchestrator's board.mjs) are lines too: a pane writer once missed an answer and two scope additions
# for two hours while nothing woke its orchestrator (2026-10-26).
[ -f $W/board.json ] && node $K/../../fleet-orchestrator/scripts/board.mjs $W watch-lines 2>/dev/null
# The watch set is prs.txt plus every open PR in WATCH_NEW_PR_REPOS that PR_SEARCH lists or that
# WATCH_PR_AUTHOR (default the gh account) opened, whatever its branch prefix: a hand-kept prs.txt once held
# only merged PRs while a writer sat silent for 1h40m on two live ones, so STALE-PR never ran on them.
Expand Down
15 changes: 15 additions & 0 deletions plugins/vibe/skills/codex-fleet-monitor/scripts/snapshot.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,18 @@ test('a PR another chat owns is never added or reported: prs-ignore.txt, or anot
assert.doesNotMatch(mine, /^acme\/app 9149$/m, 'the other program owns it');
assert.match(mine, /^acme\/app 9058$/m);
});

test('board lines are snapshot lines: a scope change the writer has not acknowledged wakes the watcher (2026-10-26)', TIMEOUT, () => {
const f = fixture();
assert.doesNotMatch(f.run('ok'), /^board /m, 'no board, no board lines');
const board = fileURLToPath(new URL('../../fleet-orchestrator/scripts/board.mjs', import.meta.url));
const tree = join(f.root, 'tree');
mkdirSync(tree);
const boardRun = (...args) => spawnSync(process.execPath, [board, f.work, ...args], { encoding: 'utf8' });
assert.equal(boardRun('writer', 'w1', '--worktree', tree).status, 0);
assert.equal(boardRun('deliver', 'item-4', '--kind', 'scope', '--to', 'w1', '--text', 'match legacy Archived Projects').status, 0);
assert.equal(boardRun('told', 'item-4', 'w1', '--now', new Date(Date.now() - 20 * 60_000).toISOString()).status, 0);
assert.match(f.run('ok'), /^board UNACKED-SCOPE item-4 w1$/m);
assert.equal(boardRun('ack', 'w1', '--id', 'item-4').status, 0);
assert.doesNotMatch(f.run('ok'), /^board /m);
});
10 changes: 4 additions & 6 deletions plugins/vibe/skills/fleet-orchestrator/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ The closedloop-graph MCP server is required; decide from it, not from guesses. T
- Before dispatching a ticket, and in every QA-prep brief, run vibe's newest-ruling search ([requirements worker](../../agents/vibe-requirements-worker.md), Newest ruling); on a hit, stop and ask the operator.
- In the same step, check every plan linked to the ticket (`list-artifact-links`, PRODUCES) with `node ../handoff/scripts/rulings-check.mjs --plan <json> --plan-body <text>`. Each exclusion it reports is scope the brief tells the writer to build, or you escalate; never one to skip.
- Before a shared change, read its callers and importers (`code_callers`, `code_importers`).
- After any change, fix or merge, run every lane the repo's lane selector names for the diff, never a hand-picked subset, then rerun only failures.
- Trace a surprising difference in a before and after comparison to the commit and ticket that introduced it before blaming the PR.
- Attribute a file's origin or a first failure from local history only through `scripts/history-origin.mjs --repo <repo> --path <file>`.

Expand All @@ -70,10 +69,10 @@ The closedloop-graph MCP server is required; decide from it, not from guesses. T

Run [codex-fleet-monitor](../codex-fleet-monitor/SKILL.md) (`vibe:codex-fleet-monitor`) on the same `<orchDir>` in the background for the whole program and act on its lines as its table says. It reads the session state files `fleet.sh` writes, listed as `local|remote <name>` in `sessions.txt`.

- The watcher resumes a session after a transient provider failure on its own; read its `recovery` lines when you wake. Run its supervisor on every machine that hosts Codex writers. Set `AUTO_RESTART_DAEMON=1` only when the operator agrees.
- Run the watcher's supervisor on every machine that hosts Codex writers. Set `AUTO_RESTART_DAEMON=1` only when the operator agrees.
- Re-arm the watcher in the same step you read each event. Before ending any turn that handled an event, `node ../codex-fleet-monitor/scripts/watcher.mjs <orchDir>` must print `watcher ok`; on `MISSING` (`board.mjs status` prints `WATCHER-MISSING`), re-arm it, never with `&`.
- The armed watcher is your heartbeat (`HEARTBEAT` every 30 quiet minutes); on every wake read every teammate message first. While the operator is away, also keep a recurring schedule (loop or cron, 20 to 30 minutes) that does the same and checks `watcher.mjs`. Recovery resumes an idle Codex writer on its default once its question's deadline passes.
- Answer a writer's question within 15 minutes, echoing its id (`board.mjs <orchDir> answered <writer> --id <id>`); the watcher (Codex) and `board.mjs status` (Claude writers) print `QUESTION-UNANSWERED` for late questions and `UNACKED` for answers not acknowledged within 15 minutes.
- Answer a writer's question within 15 minutes, echoing its id (`board.mjs <orchDir> answered <writer> --id <id> --text <answer>`); send other mid-run changes to what it builds as `deliver <id> --kind scope --to <writer> --text <change>`, a message naming the id, then `told`. The watcher and `board.mjs status` flag `QUESTION-UNANSWERED`, `UNACKED` and `UNACKED-SCOPE` after 15 minutes, and `SCOPE-NOT-STARTED` for a worktree still clean at its old head 30 minutes on.
- On `STALE-PR` (a PR needing its writer with no push for 45 minutes), ask the owning writer for an ETA, unblock it, or reassign it, whatever it reports being busy with.
- Push for a writer only the head it declared push-ready (`--ready <sha>` on its board row): `board.mjs <orchDir> push-check <writer> --head <sha>` must print `READY` first; otherwise ask the writer and wait.
- Before you or a reviewer inspect a head, check it equals the pushed PR head (`gh pr view <n> --json headRefOid`).
Expand All @@ -99,7 +98,7 @@ scripts/fleet.sh <orchDir> local|remote session last <name> [--count <n>]
- Launch each Claude writer with the [worker preamble](./references/templates.md#worker-preamble) saved to `prompts/`.
- Hold an agent with TaskStop, never a message; check nothing it started still runs.
- Batch asks to a writer at milestones; side work goes to read-only workers.
- A pane teammate sees messages only when its turn ends, so writers keep turns short (vibe's foreground guard refuses a foreground wait over 5 minutes in a vibe worktree). On `UNACKED`, or an idle worker that ignored a message, resend once, then interrupt after the second unanswered delivery: ask the operator to press Esc in the named pane. Keep long briefs in a file and send its path.
- A pane teammate sees messages only when its turn ends, so writers keep turns short. On `UNACKED`, or an idle worker that ignored a message, resend once, then interrupt after the second unanswered delivery: ask the operator to press Esc in the named pane. A resend never reaches a writer mid-turn: on `UNACKED-SCOPE` or `SCOPE-NOT-STARTED` interrupt at once, or TaskStop and relaunch it with the change in its prompt. Keep long briefs in a file and send its path.
- A standing duty (main's post-merge health, a PR shepherd, the watcher) always has a live owner: `board.mjs <orchDir> duty <duty> --owner <writer or helper>`. Name the replacement in the same step you stop or move its owner; `helpers.mjs stopped` refuses an owner, and `board.mjs status` prints `UNOWNED-DUTY`.
- Stop a helper (reviewer, verifier, QA prep, poller) once you accept its result, recorded with `scripts/helpers.mjs <orchDir> start|accepted|stopped <name>`; run `helpers.mjs <orchDir> status` with every monitor event (`STOP`, `ORPHAN`). A helper records each process it starts with `helpers.mjs <orchDir> pid <name> <pid...>` (`--group` only for one it spawned detached), and `stopped` ends them.

Expand All @@ -108,8 +107,7 @@ scripts/fleet.sh <orchDir> local|remote session last <name> [--count <n>]
`scripts/board.mjs` keeps `<orchDir>/board.md`, the page every writer reads:

- The status sweep, a standing `status-sweep` duty. At every writer assignment and every merge, and at every status report, snapshot each in-scope PRD's children (status, writer, PRs, latest body of each merged or DONE one, plans, absorbing umbrella) and run `node scripts/status-audit.mjs --snapshot <file>` until each `MISMATCH`, `UNOWNED`, `UNREAD`, `UNCHECKED`, `PLAN-OPEN` and `ABSORBED-OPEN` child is fixed or under `outOfScope` with who ruled it out. Closing an umbrella closes its absorbed children in the same step. A merge alone never makes a ticket DONE: its criteria are checked against the merged code first.
- When you launch a writer, add its row (`board.mjs <orchDir> writer <name> --runtime codex|claude --pr <n> --repo <owner/repo> --head <sha> --status <text> --deps <a,b>`) and its PR's `<owner/repo> <n> <writer>` line to `prs.txt`, and give it the board path and the [worker preamble](./references/templates.md#worker-preamble). Update the row on every push report.
- Writers message each other directly: Codex writers through the `app-server-orchestrator` skill's send, Claude workers and QA guides through SendMessage.
- When you launch a writer, add its row (`board.mjs <orchDir> writer <name> --runtime codex|claude --worktree <path> --pr <n> --repo <owner/repo> --head <sha> --status <text> --deps <a,b>`) and its PR's `<owner/repo> <n> <writer>` line to `prs.txt`. Update the row on every push report.
- A settled UX ruling is saved once as a reference file with its screenshots in a shared evidence location. Find every renderer of the behavior with `code_grep` across each open PR's diff and main, then `board.mjs <orchDir> deliver <ruling> --kind ruling --reference <file> --to <every owning writer and QA guide>`. Every QA plan for that behavior cites it; one shared component beats each PR building its own.
- When QA prep marks a human checkpoint ready, or rebinds results, `deliver <checkpoint> --kind checkpoint --head <sha> --to <guide>` and message the guide yourself with the checkpoint, its launch steps and the head.
- After each message, `board.mjs <orchDir> told <item> <recipient>`. Run `board.mjs <orchDir> status` with every monitor event and act on each line: `STALE` (the board misses a pushed head), `UNTOLD`, `UNOWNED-DUTY`. A `SLOW` row is a Codex writer 30 minutes without a push on a PR with unresolved human threads: have it push and hand the branch to a Claude writer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ Habits that kept a multi-PR program on track. They apply the two principles in S
- One owner per behavior across PRs. When two PRs implement the same rule differently, rule once, put it in the owning PR and make the other inherit it. When two PRs create the same file, name which merges first and tell the second to fold into it. Being on main is not evidence of being the final design: ask the owner which side is scaffold before recommending which side to drop.
- Ask "what failed, and what exactly changed" of every test or fixture change before it is pushed. Setup fixes that use the repo's documented helpers pass; weakened assertions do not.
- Check parity at several widths and in both flag states. A single desktop width hid regressions more than once.
- When you accept a worker's alternative to your instruction, tell that worker at once that its version stands and your instruction is withdrawn; a late-arriving instruction otherwise makes it redo the work.
- When you accept a worker's alternative to your instruction, tell that worker at once, as a scope change, that its version stands and your instruction is withdrawn; a late-arriving instruction otherwise makes it redo the work.
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ Inherited scope: trace every exclusion, deferral, "obsolete" or narrowed criteri
Before editing, read the repo docs that own the paths you touch (AGENTS.md files or contributor guide) or the team knowledge base.
Tests: after any change, fix or merge (a conflict resolution included), run every lane the repo's lane selector names for the diff, never a hand-picked subset, then rerun only failures.
Heavy checks go through <fleet.sh slot command for the chosen machine>; never run windowed e2e on the operator's daily machine.
Commands expected to take more than a few minutes run in the background (never a foreground wait over 5 minutes), and you end the turn while they run, so messages reach you. Check the worker inbox between steps so orchestration rulings are applied before more work.
Questions to the orchestrator end with `If no answer by <time, 30 minutes on>: <your default>` and an id, recorded with `board.mjs <orchDir> question <you> --id <id> --text <question>`; acknowledge each answer by its id (`board.mjs <orchDir> ack <you> --id <id>`) in your next status; keep working meanwhile and past it proceed on the default (never for a flag, copy, destructive, credential or scope question). A question never holds your PR: ship what is done, arm auto-merge, put the answer-dependent part in a follow-up.
Messages reach you only when your turn ends: commands expected to take more than a few minutes run in the background, never a foreground wait or a `sleep` poll, and you end the turn while they run.
Before each commit and each verification phase, run `board.mjs <orchDir> pending <you>`; apply and acknowledge (`ack <you> --id <id>`) each change and answer it lists, naming what you applied in your next status.
Questions to the orchestrator end with `If no answer by <time, 30 minutes on>: <your default>` and an id, recorded with `board.mjs <orchDir> question <you> --id <id> --text <question>`; keep working meanwhile and past it proceed on the default (never for a flag, copy, destructive, credential or scope question). A question never holds your PR: ship what is done, arm auto-merge, put the answer-dependent part in a follow-up.
Credentials: after the first authentication failure, use CI logs or the API for the same fact, or stop and ask. Never write, export or route around credentials.
Git: commit messages per repo convention; regular push only, never force-push. This covers every remote. Proof and scratch commits use a fresh `proof/<sha>-<n>` ref; never amend after push.
Review threads: reply on the thread naming the fixing SHA and resolve it only after the fix is verified in code.
Expand Down
Loading
Loading