Skip to content

fix(vibe): ship the 1.0.76 fleet lessons release - #337

Merged
shafty023 merged 14 commits into
mainfrom
fix/vibe-lessons-20261022b
Oct 22, 2026
Merged

shafty023 merged 14 commits into
mainfrom
fix/vibe-lessons-20261022b

Conversation

@shafty023

@shafty023 shafty023 commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Purpose

This PR ships the fourteen open vibe lessons ledger entries, the operator rulings and two consistency fixes from the 2026-10-22 fleet run as one pull request and one vibe 1.0.76 release. Each lesson gets a mechanical check where one fits, and each check extends the 1.0.75 scripts rather than adding a parallel one.

Release metadata

  • One changelog entry: vibe v1.0.76.
  • One version bump: vibe 1.0.75 to 1.0.76 in both manifests.
  • Contract coverage for the release: plugins/vibe/tools/python/test_vibe_1076_lessons_contract.py.
  • Older ledger lines that carry no release marker are either recorded as not a plugin change or are the Desktop E2E deadline entry, where the operator ruled out a plugin change. None of them get code here.
  • Net instruction length: Markdown under plugins/vibe grows by about 1,550 words (2,564 added, 1,016 removed).
    • The six gated lessons account for about 370 of that. Prose a check now enforces was shortened, for example the PRD executed bullet and the status audit bullet.
    • Most of the rest is new operator rulings with no prior text: the single review round, the 45-minute cadence, and the Program practices section.
  • Older tests changed because a ruling changed their expectation:
    • test_vibe_1074_lessons_contract.py and push-cadence.test.mjs move the push cadence from 90 to 45 minutes (operator ruling, 2026-10-22).
    • status-audit.test.mjs fixtures now carry ticket bodies and scoped out-of-scope reasons.
    • handoff-inventory.test.mjs records a migration test for its migration case.

Affected files

  • fleet-orchestrator also gains scripts/green-watch.mjs, codex-fleet-monitor scripts/claude-usage.mjs, and agents/vibe-requirements-worker.md changes its reference mode.
  • New plugin hook: plugins/vibe/hooks/hooks.json with kill-guard.mjs and its test.
  • codex-fleet-monitor: scripts/recover.mjs, scripts/watch.sh, scripts/snapshot.sh, scripts/classify.sh, their tests, SKILL.md.
  • fleet-orchestrator: scripts/board.mjs, scripts/codex-session.mjs, scripts/status-audit.mjs, scripts/qa-plan-check.mjs, scripts/fleet.sh, new scripts/load-check.sh, their tests, SKILL.md, references/templates.md, references/setup.md.
  • handoff: scripts/executed-check.mjs, scripts/criteria-audit.mjs, scripts/rulings-check.mjs, scripts/diff-rules.mjs, scripts/review-threads.mjs, new scripts/spec-rules.mjs and scripts/rollout-gate.mjs, their tests, SKILL.md.
  • vibe: scripts/session-questions.mjs, scripts/shepherd.mjs, scripts/push-cadence.mjs, scripts/commit-worktree.mjs, new scripts/migration-publish.mjs, scripts/pr-threads.mjs and scripts/review-followup.mjs, their tests, SKILL.md, references/guardrails.md.
  • agents: vibe-ticket-worker.md, vibe-environment-worker.md, vibe-guardrails-reviewer.md.
  • cl-execute references/monitoring-merge-completion.md, cl-sweep references/open-pr-lifecycle.md.

Prevention closeouts

The orchestrator processed no teammate messages for about 7 hours overnight

Prevention

  • Class: the orchestrator ended its turn with nothing armed to wake it, and writers' questions carried no default. Their questions sat unread for 7 hours while they waited.
  • Instances: fixed codex-fleet-monitor/scripts/watch.sh (with no change it ran up to 200 ticks, about 6.7 hours, before its TIMEOUT exit), fleet-orchestrator/scripts/board.mjs, vibe/scripts/session-questions.mjs, codex-fleet-monitor/scripts/recover.mjs, fleet SKILL.md and the worker preamble.
  • Search: rg -n "seq 1 200|TIMEOUT|watcher.mjs|NEEDS-REPLY|session-questions" plugins/vibe/skills plugins/vibe/agents
  • Rung: 1 via session-questions.mjs ask. It refuses a question with no --default and --deadline-minutes, except flag, copy, destructive, credential and scope questions, and returns the If no answer by <time>: <default> line. Rung 3 via the recovery pass: once the deadline passes it resumes an idle Codex writer on its default (proceeded-on-default). Also rung 3: watch.sh exits with HEARTBEAT every HEARTBEAT_SECONDS (30 minutes), and board.mjs status fails with WATCHER-MISSING. Rung 4 covers the recurring schedule while the operator is away.
  • Proof: removing each piece fails a test. The default requirement fails session-questions.test.mjs. proceedOnDefaults fails recover.test.mjs. The watcher check fails board.test.mjs. The heartbeat exit fails watcher.test.mjs (run on the second machine, because it starts processes).
  • Why not higher: a Claude teammate's message to the lead cannot be read by a script, so draining it stays a rule.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

Takeover writers found handoff claims of "done" that weren't

Prevention

  • Class: a takeover writer built on the previous writer's "done" claims without re-verifying them. The result was a half-fixed thread, six broken design-system tests and an unmeasured trigger that cost 10 s per insert.
  • Instances: fixed fleet-orchestrator/scripts/codex-session.mjs and references/templates.md (the takeover line and the resume prompt). Thread resolution already needs a verified verdict (handoff/scripts/review-threads.mjs). The per-row cost is the migration check below.
  • Search: rg -n "Done:|Takeover|resume" plugins/vibe/skills/fleet-orchestrator
  • Rung: 2 via codex-session.mjs (every fleet.sh turn). It refuses a prompt with a Done: line and no Re-verify: line. Rung 4 covers the Claude takeover preamble.
  • Proof: removing the refusal fails codex-session.test.mjs.
  • Why not higher: whether a claim was re-verified is judgment. The prompt gate makes the instruction impossible to omit.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

A pushed migration became shared state through the preview CI

Prevention

  • Class: a migration was pushed before it was tested, so preview CI applied it to two shared preview schemas and the later fix needed a cleanup run.
  • Instances: fixed vibe/scripts/migration-publish.mjs (new), agents/vibe-environment-worker.md (run before every push), handoff/scripts/diff-rules.mjs (same check at handoff), vibe/references/guardrails.md. The guardrail's old sentence said the build applied a migration only to the session's own data. That was false for preview CI, and it is corrected.
  • Search: rg -n -i "migration" plugins/vibe/agents/vibe-environment-worker.md plugins/vibe/skills/vibe/references plugins/vibe/skills/handoff/scripts/diff-rules.mjs
  • Rung: 2 via migration-publish.mjs. Each migration added since the last push needs Migration test: <id>: <test file>, with the test file present at HEAD. A trigger, function or FOR EACH ROW also needs Scale test: <id>: <test file>: <n> rows, <time>.
  • Proof: removing the loop fails migration-publish.test.mjs and the diff-rules migration case.
  • Why not higher: the push itself runs inside the compiled main-sync bundle. A refusal inside it would need a vibe-workers rebuild, and running the check first gets the same refusal.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

An agent rewrote a human-authored spec to match its code

Prevention

  • Class: an agent edited a human's spec (project-hierarchy-spec.ts, "reject moves beyond four levels") so it agreed with the code. A written rule already forbade that.
  • Instances: fixed handoff diff rules (new spec-rules.mjs), rulings-check.mjs --criteria-before for ticket acceptance criteria, agents/vibe-ticket-worker.md (runs it before each body write), agents/vibe-guardrails-reviewer.md.
  • Search: git ls-files | rg -- "-spec\.(ts|tsx|md)$" in the product repo; rg -n "Spec change|criteria-before" plugins/vibe
  • Rung: 2. This moves up a rung from the existing written rule, as the prevent-recurrence method requires. Any change to an existing *-spec.* file, or to Markdown under prd/, prds/, spec/ or specs/, blocks unless the change log records Spec change: <path>: <human name or operator ruling>, <YYYY-MM-DD>, <where>. A ticket version that rewords or drops a criterion needs a ticket ruling amending that criterion's id.
  • Proof: removing checkSpecChanges from evaluateDiff fails the diff-rules spec case, and the case shows a plan citation is refused. Emptying criteriaChangeProblems fails rulings-check.test.mjs.
  • Why not higher: a spec edit is a legitimate change once a human asks for it, so it cannot be made impossible.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

Tickets were bulk-set DONE with acceptance criteria unchecked

Prevention

  • Class: tickets were set DONE because their PR merged, without checking their acceptance criteria. About 60 children were closed with criteria unchecked, minutes after the merges.
  • Instances: fixed handoff/scripts/executed-check.mjs (--kind ticket), fleet-orchestrator/scripts/status-audit.mjs (it used to report a merged child as expected DONE on the merge alone), agents/vibe-ticket-worker.md mergeCheck, cl-execute completion, cl-sweep terminal reconciliation.
  • Search: rg -n -i -e 'to \?DONE' -e 'mark[^.]{0,20}done' -e 'merged pull request means DONE' plugins/vibe`
  • Rung: 1 via executed-check.mjs --kind ticket, the check every DONE path runs. It refuses while a criterion is unchecked, unless that criterion names Follow-up: <slug> or Excluded: <human name or operator ruling>, <YYYY-MM-DD>. Rung 3 via status-audit.mjs, which needs each merged or DONE child's body and prints UNREAD and UNCHECKED.
  • Proof: returning no problems from ticketProblems fails executed-check.test.mjs. Skipping the criteria finding fails status-audit.test.mjs.
  • Why not higher: the ticket store is external, so the status write itself cannot be refused from here.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

Approved PRs blocked by unresolved threads (writer side), as amended by the single review round ruling

Prevention

  • Class: a writer read armed auto-merge as done and stopped watching, so new unresolved review threads blocked the merge for hours.
  • The operator's single review round ruling replaces "treat any new thread as work". The two entries are reconciled in one mechanism:
    • Round-one threads are work.
    • Later threads are triaged into the PR's one follow-up ticket, replied to with its link, and resolved, so they never block the merge.
  • Instances: fixed vibe/scripts/shepherd.mjs, new vibe/scripts/review-followup.mjs, handoff/scripts/review-threads.mjs, the vibe, handoff and fleet SKILL.md. The monitor side shipped in 1.0.75. board.mjs and the shepherd share vibe/scripts/pr-threads.mjs.
  • Search: rg -n "reviewThreads|isResolved|resolveReviewThread" plugins/vibe/skills
  • Rung: 3 via shepherd.mjs --round-one-fixed-at.
    • It stops with threads (exit 13) on a round-one thread the writer has not answered last.
    • It stops with later-threads (exit 14) on a thread created after the round-one fix push.
    • Rung 1 via review-followup.mjs record: it refuses a second follow-up ticket for the same PR, so a later round appends to the recorded one.
    • review-threads.mjs resolves a later thread only with a follow-up link or a reason, and refuses to treat it as fixed in the PR.
  • Proof: each mutation below fails its suite.
    • Dropping the round split fails shepherd.test.mjs.
    • Allowing a second ticket fails review-followup.test.mjs ("a second later round reuses the recorded ticket").
    • Removing the later-round branch fails review-threads.test.mjs.
  • Why not higher: thread state lives on the code host.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

A writer killed processes by pattern on the shared laptop

Prevention

  • Class: a writer signalled processes by pattern on a shared machine, which can end other sessions' runs. A written rule already forbade it.
  • Instances: fixed by a new plugins/vibe/hooks/kill-guard.mjs registered for Bash in hooks/hooks.json, vibe/references/guardrails.md, fleet SKILL.md. Codex has no plugin hook, so Codex writers keep the written rule.
  • Search: rg -n "pkill|killall|xargs kill|kill -- -" plugins and the existing test_vibe_session_friction_contract.py and test_vibe_signal_safety_contract.py scans.
  • Rung: 1. This moves up from the written rule. Every Claude Bash call goes through the hook, which denies:
    • the by-name killers and xargs kill;
    • kill with a variable or a command substitution;
    • a pid of 1 or lower;
    • a pid the session did not record with kill-guard.mjs own <pid>. A recorded pid is tied to its directory and the process start time.
  • Proof: disabling the by-name refusal fails kill-guard.test.mjs, and so does disabling the 1-or-lower refusal, even when a ledger claims the pid. The test only asks the guard for decisions and sends no signal.
  • Why not higher: the hook is the enforcement point. The written rule remains for Codex.
  • Follow-up: a Codex-side equivalent once Codex plugins can register a pre-command hook.
  • Event recorded: vibe lessons ledger.

Open PRs sat for hours with no push while their writers looked busy

Prevention

  • Class: PR freshness was never tracked, only writer state. A PR that needed its writer (5 threads) went about 6 hours with no push while the writer looked busy.
  • Instances: fixed codex-fleet-monitor/scripts/snapshot.sh, vibe/scripts/push-cadence.mjs (now 45 minutes), vibe/SKILL.md, fleet SKILL.md and templates, monitor SKILL.md.
  • Search: rg -n "headRefOid|committedDate|isDraft|90 minutes" plugins/vibe/skills
  • Rung: 3 via the watched PR line.
    • It reads STALE-PR when 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), whatever its writer reports.
    • The vibe commit reports pushDue after 45 minutes unpushed.
    • Rung 4 covers the orchestrator's response (ask for an ETA, unblock it, or reassign it) and never holding a push for optional evidence.
  • Proof: removing the flag fails the snapshot.test.mjs STALE-PR case. Restoring 90 minutes fails push-cadence.test.mjs.
  • Why not higher: a push cannot be forced.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

A writer held its PR for six hours on an unanswered question

Prevention

  • Class: a question held a PR on both sides. The writer held auto-merge pending an answer, and nobody tracked how long the question had waited.
  • Instances: this extends the overnight entry's defaults and deadlines rather than duplicating them. Fixed vibe/scripts/session-questions.mjs, codex-fleet-monitor/scripts/classify.sh (via snapshot.sh), fleet-orchestrator/scripts/board.mjs, the vibe and fleet SKILL.md, templates.
  • Search: rg -n "NEEDS-REPLY|deadline|hold" plugins/vibe/skills/vibe/scripts/session-questions.mjs plugins/vibe/skills/codex-fleet-monitor/scripts
  • Rung: 1 via session-questions.mjs ask. It refuses a question that says it holds a merge, a push or the PR, and caps a default's deadline at 30 minutes. Rung 3 covers the age flags:
    • QUESTION-UNANSWERED(15m+) on an idle Codex writer's NEEDS-REPLY message;
    • board.mjs status QUESTION-UNANSWERED for questions Claude writers record with board.mjs question.
  • Proof: removing each piece fails a test. The hold refusal fails session-questions.test.mjs. The classify branch fails the liveness.test.mjs case. The board filter fails board.test.mjs.
  • Why not higher: a Claude writer's message is free text, so it records the question itself.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

Orchestrator practices still not in fleet-orchestrator

Prevention

  • Class: working orchestrator practices lived only in chat, so each restart relearned them.
  • Instances: fixed fleet SKILL.md (new Program practices section), references/setup.md (config keys GREEN_REF, GREEN_WATCH_AGENT, REPO_MEMORY_CMD, REQUIRED_DUTIES, MAX_LOAD_PER_CORE), scripts/board.mjs, scripts/fleet.sh, new scripts/load-check.sh. Repo names stay in config.
  • Search: rg -n "green|duty|HEAVY_SLOTS|load" plugins/vibe/skills/fleet-orchestrator
  • Rung: 3 or 1 where a check fits.
    • board.mjs status prints MISSING-DUTY green-watch while PRs are watched and nobody owns it.
    • fleet.sh slot heavy refuses a machine over MAX_LOAD_PER_CORE (exit 4) and names the other machine.
    • The close-out check is executed-check.mjs --kind prd (flags section first) and status-audit.mjs, both already shipped.
    • Repo knowledge capture, deadline mode and runtime choice are rung 4.
  • Proof: removing the duty check fails board.test.mjs. Removing the load comparison fails the fleet.test.mjs load case, run on the second machine.
  • Why not higher: deadline mode and runtime choice are operator judgment.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

A PRD's rollout gate was skipped and nobody caught it

Prevention

  • Class: a diff dropped a PRD's rollout gate (a closed-by-default flag, a legacy rollback path, a staged rollout), and its tickets were closed on a ruling about a different surface.
  • Instances: fixed handoff scripts/rollout-gate.mjs (new), scripts/rulings-check.mjs (--ruling --scope), scripts/executed-check.mjs (--kind ticket --slug), fleet scripts/status-audit.mjs (UNSCOPED), scripts/qa-plan-check.mjs (--criteria), handoff and fleet SKILL.md.
  • Search: rg -n -i "rollback|cohort|closed-by-default|out of scope|Excluded:" plugins/vibe/skills
  • Rung: 2 or 3.
    • Each flag, rollback or rollout requirement in the ticket criteria or the PRD's rollout section needs Rollout: <id>: <file>:<line> at HEAD, or a dated human ruling that names the ticket or PRD. A flag requirement's line must read a flag.
    • Every close-out ruling must name the ticket or its PRD.
    • The QA plan must cite each rollout requirement as a checkpoint.
  • Proof: the fixtures are shaped like PRD-729. Removing each check fails its suite:
    • the scope match fails rollout-gate.test.mjs;
    • the HEAD-line check fails it too;
    • the QA-plan check fails qa-plan-check.test.mjs;
    • UNSCOPED fails status-audit.test.mjs;
    • the exclusion scope fails executed-check.test.mjs.
  • Why not higher: whether code satisfies a rollout requirement needs a cited line, not a type.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

The orchestrator pushed a writer's partial commit

Prevention

  • Class: someone pushed a writer's committed head on its behalf while the writer was still fixing it, and auto-merge took the incomplete head.
  • Instances: fixed fleet-orchestrator scripts/board.mjs, SKILL.md, references/templates.md.
  • Search: rg -n "headRefOid|--head|push" plugins/vibe/skills/fleet-orchestrator/SKILL.md plugins/vibe/skills/fleet-orchestrator/scripts/board.mjs
  • Rung: 3 via the board.
    • A writer declares its push-ready head (writer <name> --ready <sha>).
    • push-check <writer> --head <sha> exits 1 unless the head is that one. A prefix shorter than 7 characters proves nothing.
    • Rung 4 covers the rule to run it before pushing for a writer.
  • Proof: disabling the comparison fails board.test.mjs.
  • Why not higher: a raw git push cannot be intercepted from the board.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

Green watch missed a batch that started later (detail of the practices entry)

Prevention

  • Class: the watcher listed main's batch runs once, so a push-triggered batch that started later, with an accessibility red from a program PR, went unreported.
  • Instances: fixed by a new fleet-orchestrator scripts/green-watch.mjs, and the green watch practice in SKILL.md.
  • Search: rg -n "run list|green" plugins/vibe/skills/fleet-orchestrator
  • Rung: 3. Every call re-lists the runs for the branch through gh and reports:
    • every lane of every run whose commit contains a program PR's merge commit (RED, PENDING);
    • NO-RUN when no run exists for the branch head.
  • Proof: removing each piece fails green-watch.test.mjs. Keeping only the first lane, or dropping NO-RUN, fails it. The command test adds a later run between two calls.
  • Why not higher: CI runs live on the code host.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger, with the practices entry.

Requirements worker said public help centers; the quality loop says live Asana

Prevention

  • Class: one instruction file kept guidance that a later release had retired elsewhere. Reference mode told the requirements worker to research public documentation and help centers.
  • Instances: fixed agents/vibe-requirements-worker.md. Reference mode now observes the live product through whoever has the operator's signed-in browser, or returns NEEDS_PERSON. Precedent mode already matched.
  • Search: rg -n -i "help cent|public documentation|release notes|web search" plugins/vibe
  • Rung: 3 via test_vibe_1076_lessons_contract.py: any sentence in the worker that sends it to help centers, public documentation, release notes or web search without "never" or "not" fails.
  • Proof: with the old worker text the test fails.
  • Why not higher: prose guidance.
  • Follow-up: none.
  • Event recorded: no ledger entry (an operator-reported contradiction).

Claude usage was called unreadable

Prevention

  • Class: the fleet skill said Claude usage could not be read, so Claude writers were moved only after they hit a limit.
  • Instances: fixed by a new codex-fleet-monitor scripts/claude-usage.mjs, plus snapshot.sh, monitor and fleet SKILL.md.
  • Search: rg -n -i "claude usage|cannot read Claude" plugins/vibe/skills
  • Rung: 3.
    • The watcher prints claude-usage local session=<band> week=<band> from claude -p "/usage", on the Codex bands, read at most every 10 minutes. CLAUDE_USAGE=0 turns it off.
    • A HIGH band carries its reset time. The fleet skill tells the operator, and moves Claude writers to Codex when the operator is away.
  • Proof: a fixture of the real output format; breaking the parser fails claude-usage.test.mjs.
  • Why not higher: usage is external state.
  • Follow-up: none.
  • Event recorded: no ledger entry (operator instruction).

Ticket and plan statuses drifted

Prevention

  • Class: statuses drifted after their work finished: plans behind DONE tickets, children absorbed by a closed umbrella, and DONE tickets with open criteria. The operator found each one.
  • Instances: fixed fleet-orchestrator scripts/status-audit.mjs (now the status sweep), scripts/board.mjs (a status-sweep duty), SKILL.md. DONE with open criteria is UNCHECKED (executed-check, above).
  • Search: rg -n "EXECUTED|absorb|IN_PROGRESS" plugins/vibe/skills/fleet-orchestrator plugins/vibe/agents/vibe-ticket-worker.md
  • Rung: 3.
    • The sweep prints PLAN-OPEN for a DONE ticket whose plan is not EXECUTED.
    • It prints ABSORBED-OPEN for a child still open after the umbrella that absorbed it closed.
    • MISSING-DUTY status-sweep appears until someone owns the sweep.
    • Rung 4 covers closing absorbed children together with their umbrella.
  • Proof: the fixture is shaped like the PRD whose plans stayed open. Removing each finding fails status-audit.test.mjs, and removing the duty fails board.test.mjs.
  • Why not higher: the ticket store is external.
  • Follow-up: none.
  • Event recorded: vibe lessons ledger.

Commands run

All suites ran on the second machine, in this branch's own worktree:

  • uv run ruff check ., uv run pyright and uv run pytest plugins/ all passed.
  • node --test over every plugin test file: 701 of 701 passed.
  • npm ci, typecheck, test and build for each tools/* package passed, and the dist tree was clean after the build.

The first run's vibe-workers vitest (three cases) and one cl-sweep black-box case timed out. The machine's load was about 34 on 18 cores, and this PR changes no TypeScript. Rerun alone, both passed.

  • The heartbeat check was proved on that machine: with the old 200-tick loop restored, the HEARTBEAT case in watcher.test.mjs fails, and with the loop as shipped it passes.
  • The other checks were proved by removing each fix and running its suite, which then failed.

- Questions carry a default and a deadline (session-questions.mjs
  --default, --deadline-minutes and the If no answer by line); the
  recovery pass resumes an idle Codex writer on its default once the
  deadline passes, never for a flag, copy, destructive, credential or
  scope question. watch.sh exits with HEARTBEAT every 30 quiet minutes
  and board.mjs status fails with WATCHER-MISSING. Writers' questions
  once sat unread for seven hours overnight.
- fleet.sh refuses a prompt with Done: claims and no Re-verify: line;
  the takeover prompt makes the new writer re-verify each claim.
- migration-publish.mjs refuses a push that adds a migration with no
  recorded test, or per-row work with no measured scale test; the
  environment worker runs it before every push and handoff runs the
  same check.
- Handoff's spec-change rule blocks an edit to an existing spec with
  no dated human source; rulings-check.mjs --criteria-before blocks a
  ticket version that rewords or drops a criterion no ruling amends.
- executed-check.mjs --kind ticket refuses DONE with unchecked
  criteria; the ticket worker, cl-execute and cl-sweep run it and
  status-audit.mjs reads each merged or DONE child's body.
- shepherd.mjs stops on a review thread the writer has not answered
  last; it shares pr-threads.mjs with board.mjs.
- vibe 1.0.76 and its changelog.

Testing: node --test on each changed suite and pytest on the vibe
contracts; each new check shown failing with its fix removed. Full
suites run on a second machine.
Risks: vibe questions other than flag, copy, destructive, credential
or scope ones now need a default and a deadline; status-audit needs
the body of every merged or DONE child.
- A flag approval asks about a "feature switch", so session-questions
  treats switch and toggle questions as waiting for the person, with
  no default or deadline; flag-keys.test.mjs caught it.
- Follow-up: accepts any tracker slug, and the fleet test fixture
  uses a generic one, as generic.test.mjs requires.
- An existing test that applies every migration counts as the
  migration test, since a vibe session writes no tests unless asked
  for a pull request; the inventory case records one.

Testing: flag-keys, session-questions, generic, status-audit,
executed-check and the inventory case pass locally.
Risks: None identified.
- The 1.0.76 entry says a feature switch question waits for the person
  and an existing test that applies every migration counts.

Testing: none needed for changelog text.
Risks: None identified.
- snapshot.sh marks STALE-PR when an open PR with threads, a conflict,
  a draft flag or a failed check has had no push for 45 minutes.
- classify.sh marks QUESTION-UNANSWERED on an idle writer's question
  after 15 minutes; board.mjs tracks Claude writers' questions the
  same way.
- session-questions.mjs caps a default's deadline at 30 minutes and
  refuses a question that holds a merge, a push or the PR.
- Writers push every 45 minutes (push-cadence.mjs), never held for
  optional evidence; the 1.0.74 contract follows the ruling.

Testing: snapshot, liveness, board, session-questions, flag-keys and
push-cadence suites.
Risks: questions that say they hold a PR are refused.
- shepherd.mjs splits unresolved threads at the round-one fix push
  (--round-one-fixed-at): round-one threads stop it as work (threads,
  exit 13), later ones stop it for triage (later-threads, exit 14).
- review-followup.mjs plans the pull request's one follow-up ticket:
  create once, then later rounds append criteria to the same ticket;
  recording a second ticket is refused.
- review-threads.mjs resolves a later-round thread answered with the
  follow-up link or a reason, and refuses to treat one as fixed here.

Testing: shepherd, review-followup and review-threads suites.
Risks: None identified.
- hooks/kill-guard.mjs, registered for Bash in hooks/hooks.json,
  denies pkill, killall, xargs kill, and kill of a pattern, a variable,
  a command substitution, a pid of 1 or lower or a pid this session did
  not record; kill-guard.mjs own <pid> records one, tied to its
  directory and process start time. A writer ran pkill -f on a shared
  laptop after a written rule forbade it.

Testing: kill-guard.test.mjs asks the guard for decisions only and
sends no signal.
Risks: Claude sessions with vibe installed must record a pid before
kill <pid>.
- rollout-gate.mjs (handoff) fails each flag, rollback or rollout
  requirement in the ticket criteria or the PRD's rollout section that
  records neither its code at HEAD (a flag requirement's line reads a
  flag) nor a human ruling naming the ticket or PRD.
- rulings-check.mjs --ruling --scope, executed-check --kind ticket
  --slug and status-audit (UNSCOPED) accept a ruling only when it names
  the ticket or its PRD.
- qa-plan-check.mjs --criteria makes every rollout requirement a cited
  checkpoint.

Testing: rollout-gate, rulings-check, executed-check, status-audit and
qa-plan-check suites with PRD-shaped fixtures.
Risks: executed-check --kind ticket now needs --slug.
- board.mjs status prints MISSING-DUTY for each REQUIRED_DUTIES entry
  without an owner (green-watch by default while PRs are watched).
- fleet.sh slot heavy runs load-check.sh first and refuses a machine
  over MAX_LOAD_PER_CORE, naming the other machine.
- fleet-orchestrator SKILL gains Program practices (green watch,
  placement, repo knowledge, close-out, deadline mode, runtime choice)
  with their config keys in setup.md.

Testing: board suite locally; fleet.test.mjs on the second machine.
Risks: a heavy slot on a loaded machine now refuses unless
FLEET_FORCE_HERE=1.
- The kill guard and its test name the by-name killers without the
  literal shapes the repository's signal and by-name scans read; the
  guardrail keeps its never-by-name sentence.
- A ledger cannot make a pid of 1 or lower killable; the test pins it.
- Changelog entries for the added items.

Testing: pytest on the vibe contracts and the kill-guard suite.
Risks: None identified.
- Keeps both changelog entries: vibe v1.0.76 above closedloop-core
  v1.5.2.

Testing: no code conflict; changelog only.
Risks: None identified.
- board.mjs writer --ready records a writer's push-ready head and
  push-check refuses any other head; the fleet rule pushes only that.
- green-watch.mjs re-lists main's runs on every call and reports every
  lane of every run containing a program PR, with NO-RUN for an
  unbatched head.
- claude-usage.mjs reads claude -p /usage into a claude-usage line on
  the Codex bands; the fleet skill moves Claude writers to Codex near
  the limit.
- The requirements worker's reference mode observes the live product
  through the signed-in browser or returns NEEDS_PERSON; a contract
  test refuses help center guidance.

Testing: board, green-watch, claude-usage, snapshot suites; the
contract test shown failing with the old worker text.
Risks: snapshot runs claude -p /usage every 10 minutes unless
CLAUDE_USAGE=0.
- status-audit.mjs, the program's status sweep, prints PLAN-OPEN for a
  DONE ticket whose plan is not EXECUTED and ABSORBED-OPEN for a child
  left open after the umbrella that absorbed it closed.
- status-sweep joins green-watch as a required board duty; the fleet
  skill runs the sweep at every merge, assignment and status report and
  closes absorbed children with their umbrella.

Testing: status-audit, board and generic suites; vibe contracts.
Risks: a program with watched PRs now needs a status-sweep owner.
- The sweep bullet keeps "At every writer assignment and every merge".

Testing: vibe contracts.
Risks: None identified.
- Ready heads, green watch, Claude usage, the status sweep and live
  reference research.

Testing: none needed for changelog text.
Risks: None identified.
@shafty023
shafty023 merged commit 8fbe595 into main Oct 22, 2026
10 checks passed
@shafty023
shafty023 deleted the fix/vibe-lessons-20261022b branch October 22, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant