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
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,22 @@ 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.52

#### Fixed
- A handoff check that is already red on main no longer stops the handoff (operator ruling, 2026-10-16). Three times in two days a handoff stopped on a check that failed on main itself, not because of the session: the API path filter, the e2e volume guard and a capability-parity fixture. Each time the session ended with "a needed fix stops the handoff".
- `main-sync-validate` now compares every failing handoff check against the main the session captured. It runs the same check on the transaction's `mainSha` in one disposable checkout of that commit, bootstrapped once with the repository's own `.closedloop-ai/loops-setup.sh` (the pristine bootstrap input readiness already uses) and removed afterwards. A check whose selection follows the session's diff (`typecheck:affected`, `test:affected`, the pinned Turbo filter) runs the same task over the packages the session selected, so main is not asked a different question. A check that also fails there is `main-preexisting`: publication proceeds and the result lists it under `mainPreexistingFailures`. One that passes on main gets one rerun on the session commit: it is `session-caused` and blocks when it fails again, and `flaky` (counted as passed, listed under `flakyChecks`) when it passes. A check that cannot run on main (no bootstrap, a failed bootstrap, a time limit, a checked file main does not have) is `session-caused`, the conservative reading.
- The receipt keeps the classification and every run's private log and digest: on each command of a successful validation, and under `failedChecks` for a blocked one. Consumption re-verifies those logs like the command logs. Checks that passed are never rerun, every new run is bounded by that check's own time limit through `scripts/run-bounded.mjs`, and the handoff recipe binds a comparison reserve (one bootstrap plus a main run and a rerun per check) so the Claude validation envelope covers it. Build-loop deploys are unchanged: a failing quick check still blocks. Dist bundles rebuilt.

#### Changed
- `quality-loop.md` gains "Failures already on main", the one statement of the rule. `guardrails.md` ("Plugins and tooling") and the handoff skill say a main-only failure is not a needed fix and never stops the handoff; only a failure the session caused blocks. Handoff step 7 continues when the result lists `mainPreexistingFailures` and passes each entry to step 8's ticket worker; a `session-caused` refusal goes back to the same writer. The change worker returns those entries unchanged and never fixes, skips or weakens such a check; the environment worker pushes and requests as usual for them.
- The ticket template and the ticket worker place each one once at the end of the Engineering checklist, `- [ ] Known main failure: <check>; fails on captured main <mainSha>; evidence: ...`, never also on the Pre-existing failures line and never routed to the person. The person's update stays plain and never presents it as a stop.

#### Tests
- `main-sync-main-comparison.test.ts` (7 cases): checks failing on both (including `typecheck:affected` and the pinned Turbo filter, run on main over the session's selected package), publication then pushing; a check failing only on the session commit, blocked with all three runs recorded; a flaky check passing on its one rerun; a check that cannot run on main because its bootstrap fails, blocked with no rerun; a build-loop failure still blocking with no comparison; plus the pinned main command and the recipe-bound Claude envelope reserve. The four classification cases fail on 1.0.50's bundle. The checkout is removed in every case.
- `test_vibe_main_only_failures_contract.py` (6 cases) pins the prose and the ticket placement in quality-loop, guardrails, handoff, the ticket template and the change, environment and ticket workers; all six fail on 1.0.50. Three contract tests and one budget test that pin the Engineering checklist row and the handoff recipe's keys follow the new row and the `comparison` reserve.
- Two eval cases tagged `main-failures` (Claude, and Codex through the runner): a check that also fails on the captured main after the writer reported it as "a needed fix", and a session-caused failure. One run each: both pass on both harnesses here. On 1.0.50 the main-only case fails on both harnesses (Claude Sonnet 0.67, Codex fail: neither placed it as a Known main failure); the session-caused case passes on 1.0.50 too, as expected, since 1.0.50 blocks every failure.

### vibe v1.0.51

#### Added
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.51",
"version": "1.0.52",
"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.51",
"version": "1.0.52",
"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
14 changes: 14 additions & 0 deletions plugins/vibe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ retained-report references and global private planning protection remain enforce
An unrelated capable reader is not selected consumption. Ordinary bootstrap health cannot mint validation proof. Meaningful retained report data,
default verdicts and replay still block, while proven same-call test fixtures
remain nonretained inputs and never stand in for required coverage.
At handoff a failing check runs again on the main the session captured
(operator ruling, 2026-10-16). One that also fails there is `main-preexisting`:
it blocks neither publication nor assignment and goes once on the live ticket's
Engineering checklist as a Known main failure. One that passes there gets one
rerun on the session commit and blocks as `session-caused` if it fails again
(`flaky` if it passes); one that cannot run on main counts as session-caused.
The comparison uses one disposable bootstrapped checkout of the captured main,
`run-bounded.mjs` time limits and a recipe-bound Claude envelope reserve, and
never reruns a check that passed. The build loop's checks are unchanged.
An existing registered writer retains its definition root, exact binding and
actual ID while the definition matches. A missing definition or proven upgrade
uses the same `writer-state.mjs migrate` command in Codex and Claude Code.
Expand Down Expand Up @@ -337,6 +346,11 @@ deprecated go in a new entry in a current root, and after a main sync a
session's own `<Level> v2.0/` entry moves to the plain root with a lifecycle
tag without counting as a retitle.

The two cases tagged `main-failures` rehearse handoff's validation step: a
check that also fails on the captured main is recorded on the ticket's
Engineering checklist and the handoff goes on, and a session-caused failure
goes back to the writer.

## Runtime Files

Version `1.0.14` contains 10 skills and 16 agents. It has no standalone
Expand Down
12 changes: 10 additions & 2 deletions plugins/vibe/agents/vibe-change-worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,12 @@ resume with the Root-bound `mainSyncValidation` grant; never invent a duration,
recipe, PASS or source identity. Then run `vibe-sessions.mjs main-sync-validate`
with parent context/transaction/
purpose on stdin, executing actual checks against committed input. Publication
purpose never grants test authoring. Managed localFix commit exclusions MUST
purpose never grants test authoring. At handoff the executor runs each failing
check again on the captured `mainSha` (operator ruling, 2026-10-16): return its
`mainPreexistingFailures` to ROOT unchanged, publication still ready, and never
fix, skip or weaken such a check; a `session-caused` refusal is yours to fix in
the session's own code (`../skills/vibe/references/quality-loop.md`, "Failures
already on main"). Managed localFix commit exclusions MUST
NOT exclude validation input: preserve interfering bytes/records and BLOCK
through the existing same-writer workaround/exclusion/restore flow, never
stash/copy/discard or claim a pass for different pushed source. A generator
Expand Down Expand Up @@ -354,7 +359,10 @@ engineering. Never return `NEEDS_PERSON` about a test or any other
engineering choice.

An explicit handoff continuation has you run the existing affected tests and
fix your own code when one fails, never what a test asserts. Use the whole
fix your own code when one fails, never what a test asserts. A test or check
that also fails on the captured main is not yours to fix: leave it and report
it, because `main-sync-validate` classifies it (`../skills/vibe/references/quality-loop.md`,
"Failures already on main"). Use the whole
session decision table, all requests and interacting row IDs, not only the latest
plan. Check executed existing coverage against every required unsuperseded
row and fix every source gap. Each row's planned Required Tests that no
Expand Down
9 changes: 7 additions & 2 deletions plugins/vibe/agents/vibe-environment-worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,13 @@ every conflict/source problem. Do not run a raw merge/commit/push yourself.
timeout override or change any test/assertion/hook deadline. A failure the
brief lists as a deliberately obsolete expectation must carry the exact
human ruling; the test stays unedited and is on the missing tests list
for engineering, because nobody writes tests in vibe or handoff. Any
unexplained failure, or running out of time, returns `BLOCKED` with the failing suites and the first error line of
for engineering, because nobody writes tests in vibe or handoff. A handoff
check the executor returned under `mainPreexistingFailures` (it also fails
on the captured main; `../skills/vibe/references/quality-loop.md`,
"Failures already on main"; operator ruling, 2026-10-16) does not block:
push and request as usual and return each entry with the first error line
of its main run's log, for the ticket's Engineering checklist. Any other
unexplained failure, including one the executor called `session-caused`, or running out of time, returns `BLOCKED` with the failing suites and the first error line of
each, and pushes nothing. Never skip, filter out, or loosen a failing test
to get a push through. Never write or edit tests in deployment mode.
4. Consume the exact transaction with `main-sync-push` and parent context
Expand Down
9 changes: 8 additions & 1 deletion plugins/vibe/agents/vibe-ticket-worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ the existing ticket template; compact helper responses are references, not the
complete evidence inventory. It also receives the table's Frozen Behavior row
IDs, the writer's feature docs record (each existing product docs page edited
as a marked agent draft, or that none covers the changed features), the
writer's missing tests list, and the running app verification's control
writer's missing tests list, every `mainPreexistingFailures` entry the final
validation returned with its main run's log, and the running app verification's control
artifact paths with what each proves and any surface it could not drive.

## Read first
Expand Down Expand Up @@ -224,6 +225,12 @@ historical inspections accurately. Preserve the existing grading block.
unchecked item at the end of the Engineering checklist in the template's
shape (`../skills/vibe/references/ticket-template.md`, "Compile-only v1.0
repairs").
Place each `mainPreexistingFailures` entry once, as an unchecked item at
the end of the Engineering checklist:
`- [ ] Known main failure: <check>; fails on captured main <mainSha>; evidence: <first error line of the main run's log>; exit <main exit code> on captured main, <session exit code> on the session commit`
(`../skills/vibe/references/ticket-template.md`, "Known main failures";
operator ruling, 2026-10-16). Not on the Pre-existing failures line too,
never to the person, and it never blocks assignment or the check below.
Fill Grading with the
template's Grading section copied unchanged, adding the section after
Handoff when the ticket has none.
Expand Down
2 changes: 1 addition & 1 deletion plugins/vibe/evals/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Visual cases (tag `visual-cues`) use the same stand-in repository and Pillow
renderer as Claude, which renders light and dark (`--theme dark`) from the
stand-in tokens' `:root` and `.dark` blocks. They attach the same PNGs with
Codex's image option and save history text in the prompt. No browser, Electron
window, actual app or external ticket is started. The 25 other cases rehearse
window, actual app or external ticket is started. The 27 other cases rehearse
one reply or decision from supplied session state in read-only mode. This is
behavior coverage, not a substitute for live app validation.

Expand Down
2 changes: 1 addition & 1 deletion plugins/vibe/evals/codex/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

def test_one_case_per_claude_prompt_and_role_names() -> None:
found = runner.cases()
assert len(found) == 31
assert len(found) == 33
assert len({c["name"] for c in found}) == len(found)
assert all(c["name"].startswith(("executive-", "product-manager-")) for c in found)
for case in found:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: contains
flags: i
pattern: 'TICKET:[^\n]*Known main failure[^\n]*check:source-gates[^\n]*4f1c2a9e'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: not_contains
flags: i
pattern: 'REPLY:[^\n]*\b(source.gates?|path filter|blocked|stuck|stopped|fail\w*|broken|main)\b'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: contains
flags: i
pattern: 'NEXT:\s*continue-handoff\b'
---
31 changes: 31 additions & 0 deletions plugins/vibe/evals/exec-main-only-failure-does-not-block/prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
runs: 1
plugins: ["../..", "../../../closedloop-core"]
max_turns: 12
timeout_seconds: 300
allowed_tools: [Skill, Read, Grep, Glob]
tags: [exec, main-failures]
---

This is a rehearsal of one step of a vibe session's handoff, with no real
environment. Load the instructions with the Skill tool (`vibe:handoff`) and
decide your next step exactly as those instructions say. You may Read the
plugin's own reference files. Do not run scripts, dispatch workers or call any
other tool, and do not ask me about the environment: the session state below
is everything that has already happened, including the output of every script
you would have run.

Session state:
- App session `board-scanning`, live ticket ISS-5150, branch `vibe/board-scanning`. The person is an executive. They asked to pass the work on to design, and the lookup resolved exactly one user.
- Handoff steps 1 to 6 are done: the reviews passed and the writer's own fixes are committed.
- In step 7 the SAME writer just ran `main-sync-validate` with handoff purpose. The script printed:
`{"status":"DONE","publicationReady":true,"requiredE2eComplete":true,"mainSha":"4f1c2a9e8b7d6c5f4e3d2c1b0a9f8e7d6c5b4a39","commandCount":11,"mainPreexistingFailures":[{"check":"pnpm check:source-gates","mainSha":"4f1c2a9e8b7d6c5f4e3d2c1b0a9f8e7d6c5b4a39","sessionExitCode":1,"mainExitCode":1,"mainArgv":["pnpm","check:source-gates"]}]}`
- The writer's turn then returned: `BLOCKED: pnpm check:source-gates fails with "api path filter: apps/api/routes/health.ts is not matched by the api path filter". The session never touched that file or the filter. A needed fix stops the handoff.`
- The first error line of that check's run on the captured main is the same api path filter line.

The person has not said anything new.

Answer in exactly this form, and nothing else:
NEXT: <one of: continue-handoff | send-to-writer-to-fix | ask-person | stop-handoff>
TICKET: <the one line you would have the ticket worker add to the live ticket for this check, word for word, or NONE>
REPLY: <the message you would send the person now, word for word, or NONE if you would send nothing>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: not_contains
flags: i
pattern: 'Known main failure'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: not_contains
flags: i
pattern: 'REPLY:[^\n]*\b(turbo|vitest|capability|parity|session-caused|rerun|captured main|main-sync|writer|worktree|commit)\b'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: contains
flags: i
pattern: 'NEXT:\s*send-to-writer-to-fix\b'
---
30 changes: 30 additions & 0 deletions plugins/vibe/evals/pm-session-caused-failure-blocks/prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
runs: 1
plugins: ["../..", "../../../closedloop-core"]
max_turns: 12
timeout_seconds: 300
allowed_tools: [Skill, Read, Grep, Glob]
tags: [pm, main-failures]
---

This is a rehearsal of one step of a vibe session's handoff, with no real
environment. Load the instructions with the Skill tool (`vibe:handoff`) and
decide your next step exactly as those instructions say. You may Read the
plugin's own reference files. Do not run scripts, dispatch workers or call any
other tool, and do not ask me about the environment: the session state below
is everything that has already happened, including the output of every script
you would have run.

Session state:
- App session `projects-board-chips`, live ticket ISS-4321, branch `vibe/projects-board-chips`. The person said "that's everything, can you raise a PR", so handoff ends with a pull request she takes through review and merge.
- Handoff steps 1 to 6 are done: the reviews passed and the writer's own fixes are committed.
- In step 7 the SAME writer just ran `main-sync-validate` with handoff purpose. It refused:
`{"ok":false,"status":"BLOCKED","error":"Required validation command failed: pnpm turbo test --filter=...[9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b] --continue; it passes on captured main 4f1c2a9e8b7d6c5f4e3d2c1b0a9f8e7d6c5b4a39 and failed again on a rerun of the session commit, so the session caused it (session-caused); private logs <three log paths>"}`
- The first error line of the session's run reads: `FAIL apps/app/__tests__/sessions-list-v2-host-capability-parity.test.ts > every host passes the chip filter props`.

The person has not said anything new.

Answer in exactly this form, and nothing else:
NEXT: <one of: continue-handoff | send-to-writer-to-fix | ask-person | stop-handoff>
TICKET: <the one line you would have the ticket worker add to the live ticket for this check, word for word, or NONE>
REPLY: <the message you would send the person now, word for word, or NONE if you would send nothing>
Loading
Loading