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

#### Changed
- Plain words ask for a pull request (operator ruling, 2026-10-15). "Raise a PR", "open a pull request", "I'll get the PR merged", "I'll get it merged", "I'll ship it myself" and "I want to take it through review" are an explicit request and take handoff's requested pull request path. `guardrails.md` ("Tests") states it once; the vibe and handoff skills, the change worker and `quality-loop.md` point to it. Tests are still written only on that request, and the person is never offered the branch to merge.
- A requested pull request runs the whole path: the same writer's `pull-request` turn writes the tests, step 7 commits, pushes and publishes through `vibe-environment-worker` in redeploy mode, and from the request on every handoff brief carries the `pull-request` phase. No brief may say both that the person takes the work to a pull request and that no tests are authored.
- Handoff now opens the requested pull request as its last step. A new `pull-request` mode in `vibe-environment-worker` (its own launcher record action, with no record writes, no push and no commit) checks the branch is at the verified published commit, fills symphony-alpha's current PR template from the session's own record (Summary, an honest Breaking changes answer, the UI Feature Flag attestation only from the flag record, a Test plan of 20 lines or fewer, and a note for the reviewer where the record cannot support an answer), titles it with the ticket slug within 72 characters, and runs `gh pr create`. It never enqueues, merges, requests reviewers, triggers CI or adds the `warning-acknowledged` label. The ticket stays with the person, In Progress, its Next line says they take the pull request through review and merge, and the closing message gives them the pull request link. The old "handoff opens none" wording is gone.
- "Who should pick this up next?" is replaced by one plain two-way question, asked only when the person's words do not already answer it: "Do you want me to open a pull request so you can take it through review and merge, or pass it to someone, usually design and then engineering, to finish?" Handoff settles it before the checks (new step 0, "Settle how it ends") because a pull request changes what they run. Passing it on without a name asks "Who should I pass it to? A name or email is fine." right away, then uses the existing lookup. A confused reply ("oh i thought you were going to code it") gets the two endings explained plainly, never a name prompt. The vibe skill's section 8 asks the same question, adding the existing keep and throw away options.
- A new session's first reply carries one line, as a notice that never waits: "When you're done, I can either open a pull request for you, or pass it to design or engineering to finish." A resumed session does not get it.
- Person-facing text no longer uses "handoff" as an unexplained term. The stale-session line, the already-finished replies and the closing reminder say what happens in plain words. The `$handoff` skill name and its triggers are unchanged. The vibe skill and `guardrails.md` no longer assume every session goes to design and engineering, or that the person is always an executive.
- Both harnesses get the same behavior: the prose is shared, and the new `pull-request` record action is declared once in the launcher's record modes, which the Claude launcher (`claude-worker.mjs`) and the Codex native record grant (`writer-state.mjs acquire-record`) both read.
- Coverage: a new contract test file (`test_vibe_session_endings_contract.py`) whose ten tests all fail on 1.0.38; the two existing tests that pinned the old question and "handoff opens none" now pin the new wording; a Claude launcher test admits the environment worker's `pull-request` action with no record writes, and a Codex native record test grants it to that worker only. A new `evals/` suite of 15 cases for `claude plugin eval` (Claude Code only) checks each ending for both an executive and a product manager, including the real exchange that prompted this change (the README says how to run it).

### vibe v1.0.38

#### 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.38",
"version": "1.0.39",
"dependencies": ["closedloop-core"],
"author": {"name": "ClosedLoop", "email": "support@closedloop.ai"}
}
4 changes: 2 additions & 2 deletions 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.38",
"version": "1.0.39",
"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 All @@ -11,7 +11,7 @@
"interface": {
"displayName": "ClosedLoop Vibe",
"shortDescription": "Vibe sessions and ClosedLoop ticket workflows.",
"longDescription": "Vibe sessions let a non-engineer build product UI in the in-app browser on their own Vercel copy of symphony-alpha, kept on a live ClosedLoop ticket, and hand the work to design and then engineering.",
"longDescription": "Vibe sessions let a non-engineer build product UI in the in-app browser on their own Vercel copy of symphony-alpha, kept on a live ClosedLoop ticket, and finish it as a pull request they take through review or pass it to design and engineering.",
"developerName": "ClosedLoop",
"category": "Development",
"capabilities": ["Write", "Analyze"],
Expand Down
34 changes: 29 additions & 5 deletions plugins/vibe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,15 @@ checklist.
### `handoff`

Checks and reviews the session's work, verifies its current app environment or
canonical prototype publication, and completes the same live ticket. Resolves
the next owner named by the person and assigns the ticket with status In
Progress. Canonical prototype review and metadata transitions are preserved;
handoff ends at the branch without opening a pull request.
canonical prototype publication, and completes the same live ticket. A session
ends one of two ways, settled from the person's words or one plain question:
a pull request they take through review and merge, which handoff opens as its
last step with the ticket left with them, or passing it on, where handoff
resolves the next owner they name and assigns the ticket. The status stays In
Progress either way. Plain words such as "I'll get the PR merged" or "I'll
ship it myself" count as asking for a pull request, and the person is never
offered a branch to merge. Canonical prototype review and metadata
transitions are preserved.
Nobody authors tests in vibe or handoff. The same implementation writer only
repairs existing tests and fixtures the session's change broke (imports,
renamed or retired symbols, setup, type errors) without changing any
Expand Down Expand Up @@ -245,7 +250,7 @@ The plugin bundles the ClosedLoop ticket automation skill pack: `cl-policy`, `cl
| `vibe-setup-worker` | sonnet | Machine preflight, worktree bootstrap, owned local processes, and confirmed session discard |
| `vibe-requirements-worker` | sonnet | Read-only ticket, requirements, overlap, and owning-route discovery |
| `vibe-ticket-worker` | sonnet | Live ticket creation, next-owner lookup, handoff grading, assignment, and cancellation |
| `vibe-environment-worker` | sonnet | Vercel environment creation, redeploy, flag snapshots, Desktop setup, and deployment records |
| `vibe-environment-worker` | sonnet | Vercel environment creation, redeploy, flag snapshots, Desktop setup, deployment records, and opening a pull request the person asked for at handoff |
| `vibe-change-worker` | sonnet | Sole persistent implementation writer for local planning, full-stack changes, stories, fixes, the handoff missing tests list and serialized recording |
| `vibe-backend-worker` | opus | Read-only decision-table and backend guidance for that writer |
| `vibe-primitive-worker` | sonnet | Read-only reusable-component and steward-specification guidance for that writer |
Expand All @@ -259,6 +264,25 @@ The plugin bundles the ClosedLoop ticket automation skill pack: `cl-policy`, `cl
| `vibe-seed-fix-worker` | sonnet | Seed drift fixes, validation, and follow-up PR-check and review-comment fixes |
| `vibe-seed-pr-worker` | sonnet | Seed PR push, status, main sync, merge queue, review replies, and owned worktree cleanup |

## Evals

`evals/` holds a `claude plugin eval` suite for how a session ends: plain
words asking for a pull request, passing the work on, an unclear "I'm done",
the endings line in a new session's first reply, and the closing pull request
link. Each case is a one-turn rehearsal with the session state in the prompt,
so it needs no symphony-alpha checkout, and it is named by persona (`exec-`
for someone who describes the work and passes it on, `pm-` for someone who
iterates from reference screenshots and may take it to a pull request
themselves). The cases load `closedloop-core` too, which sits outside this
plugin, so run the suite from the repository's `plugins/` folder:

```bash
cd plugins
claude plugin eval . --eval-dir vibe/evals --model sonnet -j 2 --max-cost-usd 5 --no-publish
```

Add `--ablation none` to skip the no-plugin baseline arm. CI does not run it.

## Runtime Files

Version `1.0.14` contains 10 skills and 16 agents. It has no standalone
Expand Down
11 changes: 9 additions & 2 deletions plugins/vibe/agents/vibe-change-worker.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: vibe-change-worker
description: The one persistent implementation writer for a vibe session. Keeps the same context, worktree and branch while planning, implementing all frontend/backend/prototype/component work, fixing reviewed issues, repairing existing tests its change broke and listing the missing tests for engineering at handoff; it writes tests only when the person asks for a pull request. Uses graph-first owner and product-decision research, local canonical plans, Storybook and existing checks. Never commits or opens a PR.
description: The one persistent implementation writer for a vibe session. Keeps the same context, worktree and branch while planning, implementing all frontend/backend/prototype/component work, fixing reviewed issues, repairing existing tests its change broke and listing the missing tests for engineering at handoff; it writes tests only when the person asks for a pull request, in any plain words. Uses graph-first owner and product-decision research, local canonical plans, Storybook and existing checks. Never commits or opens a PR.
model: sonnet
tools: Read, Write, Edit, Grep, Glob, Bash, Skill
---
Expand Down Expand Up @@ -272,7 +272,14 @@ Obtain independent whole-table verification. Never claim final core alignment
with source gaps or unresolved rows; they block final handoff.

Only a `pull-request` continuation, which the orchestrator sends after the
person explicitly asks for a pull request, authorizes you to write tests: full
person explicitly asks for a pull request, authorizes you to write tests. Plain
words that say they will take the work through review and merge themselves
("raise a PR", "I'll get the PR merged", "I'll ship it myself", "I want to take
it through review") are that request (`guardrails.md`, "Tests"), but only the
orchestrator's `pull-request` turn carries the authority; when such words reach
you in another turn, quote them in your result so the orchestrator routes
them, and author nothing. Never
offer the person the branch to merge. The turn requires full
tests for every change in the session, covering acceptance criteria, every
unsuperseded row, production wiring and meaningful failure paths, and every
missing tests entry. Verify helpers/reviewers never author or fix tests.
Expand Down
47 changes: 45 additions & 2 deletions plugins/vibe/agents/vibe-environment-worker.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: vibe-environment-worker
description: Runs a vibe session's Vercel environment in symphony-alpha for the vibe and handoff orchestrators. Create mode takes the production flag snapshot in PostHog for the person's real account, pushes the vibe/<slug> branch, starts the environment through the repo's request workflow, follows it to the end, and records the app, API, and Storybook URLs and deployed commit it reports on the session and the live ticket. Redeploy mode pushes the commit the orchestrator made (this worker never commits) and requests the environment again so the new commit is deployed. Flags mode takes a new snapshot when the person asked; desktop mode requests the environment again so it signs in the session's local Desktop profile. Returns a short status.
description: Runs a vibe session's Vercel environment in symphony-alpha for the vibe and handoff orchestrators. Create mode takes the production flag snapshot in PostHog for the person's real account, pushes the vibe/<slug> branch, starts the environment through the repo's request workflow, follows it to the end, and records the app, API, and Storybook URLs and deployed commit it reports on the session and the live ticket. Redeploy mode pushes the commit the orchestrator made (this worker never commits) and requests the environment again so the new commit is deployed. Flags mode takes a new snapshot when the person asked; desktop mode requests the environment again so it signs in the session's local Desktop profile. Pull-request mode opens the pull request the person asked for at handoff, on the already pushed branch, and never merges it. Returns a short status.
model: sonnet
tools: Read, Write, Grep, Glob, Bash
---
Expand Down Expand Up @@ -37,7 +37,7 @@ Never self-declare continuation intent or borrow the publisher's lease.
Publication readiness is not completed handoff E2E coverage: preserve reported
incomplete scenarios and never assign/claim completion from a preview alone.

The mode (`create`, `redeploy`, `flags`, or `desktop`), the worktree path, the live
The mode (`create`, `redeploy`, `flags`, `desktop`, or `pull-request`), the worktree path, the live
ticket slug, and for redeploy the session summary, the session's
`localFixes` paths (managed local workarounds for a symphony-alpha bug, authored
historically by setup or currently by the sole implementation writer; they
Expand Down Expand Up @@ -332,6 +332,49 @@ and `baseBranchSource: "mcp_input"`. Then update the ticket per
flag snapshot, and Sessions (it shows the URLs only once they are verified),
and add your Progress line.

## Pull-request mode

Handoff sends this as its last step, only when the person asked for a pull
request (`../skills/handoff/SKILL.md`, "A requested pull request"). Your
grant is the `pull-request` record action: no ticket or session writes, no
push, no commit. Inputs: the worktree, the live ticket slug, the confirmed
summary, the final verified published commit, the tests the `pull-request`
turn wrote and the check results, and the ticket's Production impact and Flag
changes and gates fields.

1. Confirm the branch on `origin` is at the verified published commit
(`git -C "<wt>" ls-remote origin <branch>`). If not, return `BLOCKED`;
never push to make it so.
2. If an open pull request already exists for the branch
(`gh pr list --repo closedloop-ai/symphony-alpha --head <branch> --state open --json url`),
return `DONE` with its URL and create nothing.
3. Fetch the current PR template from main
(`gh api repos/closedloop-ai/symphony-alpha/contents/.github/pull_request_template.md --jq .content | base64 -d`)
and fill every section from the session's own record, never from
invention:
- Summary: the confirmed summary, with the live ticket's slug.
- Breaking changes: an honest answer from Production impact; write the
root `AGENTS.md`'s "none" answer only when Production impact supports it
(no API response field, database column or cross-process wire shape
changed).
- UI Feature Flag: the heading alone, unless the change adds a net-new
surface, screen or navigation item or a new flag primitive; then attest
`[ui-flag:gated] <key>` or `[ui-flag:N/A] <reason>` only from the Flag
changes and gates field.
- Test plan: at most 20 lines: the tests written and the checks run, with
their results.
When the record cannot support a required answer, say so in that section
for the reviewer instead of inventing one.
4. Title: `<live ticket slug>: <plain imperative summary>`, at most 72
characters including the ` (#NNNN)` GitHub appends, with no mention of AI
tools. Create it with
`gh pr create --repo closedloop-ai/symphony-alpha --base main --head <branch> --title "<title>" --body-file <file>`,
the body file in the session's private git directory.
5. Never add the `warning-acknowledged` label, request reviewers, trigger CI
by hand, arm auto-merge, enqueue or merge.

Return `DONE` with the pull request URL, or `BLOCKED` with why.

## Return (under 100 words)

`DONE` with the verified app, API, and Storybook URLs exactly as
Expand Down
4 changes: 3 additions & 1 deletion plugins/vibe/agents/vibe-ticket-worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ historical inspections accurately. Preserve the existing grading block.
table file stays in this worktree.
6. Fill Handoff and Design Review from the verified final packet/evidence you
were given, per the template; its
Next line names the next owner you were given. The Session decision table
Next line names the next owner you were given; when the person asked for a
pull request, that owner is the session's operator and the line takes the
template's pull request form. The Session decision table
line names the attachment and its Frozen Behavior row IDs. The Feature docs
line lists the edited pages from the writer's record and never claims a
page that is not in the diff (`git -C "<wt>" diff --stat <inventory baseCommit>`).
Expand Down
1 change: 1 addition & 0 deletions plugins/vibe/evals/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
results/
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
type: llm
criteria: |
Grade only the REPLY line. Pass when it explains, in plain words, that the code is already built and that the
session can end one of two ways (a pull request the person takes through review and merge, or passing it to
someone, usually design and then engineering, to finish), and then lets the person choose. Fail when it asks for a
name or email before they chose, or explains only one of the two options.
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: not_contains
flags: i
pattern: 'branch\b[^.\n]{0,40}\bmerge|merge\b[^.\n]{0,20}\bbranch'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: not_contains
flags: i
pattern: 'REPLY:[^\n]*\bhand ?-?off'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: not_contains
flags: i
pattern: 'name or (an )?email|who should (i|we) (pass|send|give)'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: contains
flags: i
pattern: 'ROUTE:\s*(ask-two-way|explain-and-ask-two-way)\b'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: regex
match: contains
flags: i
pattern: 'open a pull request[\s\S]{0,200}pass it (on|to)'
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
runs: 1
plugins: ["../..", "../../../closedloop-core"]
max_turns: 8
timeout_seconds: 300
allowed_tools: [Skill, Read, Grep, Glob]
tags: [exec, negative]
---

This is a rehearsal of one turn of a vibe session, with no real environment.
Load the instructions with the Skill tool (`vibe:vibe`) and decide your next
message to the person 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.

Session state:
- App session `projects-board-chips`, seeded data, live ticket ISS-4321, branch `vibe/projects-board-chips`.
- Every request is built, deployed to the person's preview, and shown to them. Nothing is queued.
- Earlier requests, in the person's words: "add a due date column to the projects board", "make the overdue ones stand out".
- The person said they were done, and you replied with the question your instructions give for how the session ends.

The person just said: "oh i thought you were going to code it"

Answer in exactly this form, and nothing else:
REPLY: <the message you would send the person now, word for word>
ROUTE: <one of: requested-pull-request | pass-on-lookup | ask-name | ask-two-way | explain-and-ask-two-way | start-new | resume | final-message>
Loading
Loading