Skip to content

Prevent unsafe and silent vibe worker paths - #341

Merged
shafty023 merged 4 commits into
closedloop-ai:mainfrom
shafty023:fix/vibe-lessons-1079
Oct 23, 2026
Merged

shafty023 merged 4 commits into
closedloop-ai:mainfrom
shafty023:fix/vibe-lessons-1079

Conversation

@shafty023

Copy link
Copy Markdown
Collaborator

Purpose

Ships vibe 1.0.79 with prevention for the 2026-10-23 lessons. Shared behavior now has one named owner, Claude writers enter through a recorded vibe session, worker tools bind in both runtimes, unsafe evidence shortcuts are blocked, and watchers without messaging can still wake the orchestrator.

Release metadata

  • Both vibe manifests move from 1.0.78 to 1.0.79.
  • The existing 1.0.79 changelog entry lists every lesson in this branch.
  • The README runtime inventory remains accurate at 12 skills, 16 agents, and one hook manifest.
  • Markdown under the vibe plugin is 103 words shorter than in 1.0.78, measured at the final head by instruction-words.mjs.

Lesson ledger

2026-10-23: shared behavior was composed in three page adapters

  • Class: an operator-specified shared capability was implemented separately in page adapters, so consumers drifted and only one page received the complete behavior.
  • Rung: 3. The plan records one owning parent, every inheriting consumer and each prototype route. Handoff's shared-capability-owner diff rule blocks the same capability component in two or more page adapters unless a cited ruling explicitly requires separate compositions.
  • Proof: the old code accepted the valid component types and imports at rungs 1 and 2, then had no architecture guard at rung 3. The new PRD-721 shaped fixture fails on that old rule set and passes only when one parent owns the capability or a specific separate-composition ruling exists. Visual QA now compares every consumer with its own approved prototype route.

2026-10-23: the fleet launched an internal vibe worker directly

  • Class: an orchestrator used an internal worker type as the writer, bypassing the vibe session lifecycle and the tools that lifecycle supplies.
  • Rung: 2. The Agent pre-tool hook refuses direct vibe:vibe-*-worker launches outside a recorded vibe session.
  • Proof: the rule already existed in the skill and was skipped. The new hook test fails on the old code, then covers denial, allowed public vibe launch and an in-session internal launch.

2026-10-23: worker contracts assumed one runtime

  • Class: worker launch and frontmatter contracts encoded Codex spellings or omitted Claude collaboration tools, so a valid worker could not start or report.
  • Rung: 1. MCP names are canonicalized at the capability schema and launcher boundary, definitions load from either runtime manifest, and required built-ins are enumerated for both runtime fixtures.
  • Proof: the cross-runtime contract fails on the old launcher for hyphenated ClosedLoop tools and missing SendMessage or Agent. It now covers the exact mcp__closedloop__create-document, update-document and create-artifact-link names used by the ticket worker.

2026-10-23 03:55Z: credential improvisation

  • Class: an agent escalated credential handling to obtain evidence that CI logs or an API already held.
  • Rung: 2. A Bash pre-tool guard blocks commands that combine DOCKER_CONFIG, docker login or an inline auths block with a token. Every worker brief stops at the first authentication failure and uses CI logs, the API, or asks.
  • Proof: the guard test fails on the old branch because no safety hook exists. It covers local and remote commands, inline auth, login through standard input, and safe log or API alternatives.

2026-10-23 04:58Z: a watcher could not report

  • Class: a duty required an event message from an agent type that had no messaging tool.
  • Rung: 3. An agent without SendMessage writes an atomic Markdown event under the orchestration event directory, and watch.sh includes event checksums in its durable snapshot.
  • Proof: the watcher test times out on the old script, then passes when a new green-moved.md event wakes the watcher and appears in its event diff.

2026-10-23 11:35Z: proof refs were force-pushed

  • Class: a no-force-push rule was treated as applying only to GitHub, leaving another machine and scratch refs as exceptions.
  • Rung: 2. The same Bash pre-tool guard blocks force flags and force refspecs even inside a remote shell command. Worker briefs require a fresh proof/<sha>-<n> ref.
  • Proof: the test fails on the old branch and now refuses force pushes to origin, a fork, another machine and a scratch remote while allowing a normal push to a fresh proof ref.
  • Related tool binding is covered by the runtime contract above. If launcher tooling cannot mint a supported publisher grant, the documented fallback stages a plain noncommitting merge of origin/main and finishes through commit-worktree.mjs; publication still waits for a real grant. The merge fixture proves the guarded commit path, while fallback selection remains a written operational rule.

Commands run

  • Repository Node suite: 765 passed.
  • Worker Vitest suite: 759 passed in 29 files.
  • Python suite: 2,483 passed and 3 skipped.
  • Manifest Vitest suite: 6 passed.
  • Ruff and Pyright: clean.
  • Worker package typecheck and build: clean; committed bundles match the sources.
  • Instruction length gate: 1,162 words added, 1,265 removed, net 103 shorter.

Risks

  • The shell guard is deliberately conservative when a credential route and token appear in the same command.
  • Event files wake the orchestrator by filename and checksum; the orchestrator still reads the Markdown file for its full report.

- Require shared plans to name the owning parent and all consumers
- Block repeated page-level capability composition
- Compare every consumer with its approved prototype route

Testing: Node tests, pytest, Ruff, Pyright, instruction word gate

Risks: Static import matching may require an explicit separate-composition ruling
- Refuse internal vibe worker subtypes outside a recorded vibe session
- Route fleet Claude writers through the vibe skill and its full lifecycle
- Cover denied, allowed and in-session Agent launches

Testing: Node tests, pytest, Ruff, Pyright, instruction word gate

Risks: The guard matches only scoped internal worker agent types
- Normalize Claude and Codex MCP tool spellings at the launcher boundary
- Preserve change worker collaboration tools and both manifest layouts
- Cover every worker tool contract with dual runtime fixtures

Testing: Full Node, worker, and Python suites; Ruff; Pyright; package typecheck and build

Risks: Low; aliases widen accepted spellings while role capability checks remain enforced
- Block credential workarounds and force pushes before Bash runs
- Surface file events from watchers without SendMessage
- Document and test runtime binding and main sync fallbacks

Testing: Full Node, worker, and Python suites; Ruff; Pyright; manifest tests; package typecheck and build

Risks: Shell matching is deliberately conservative around credential and force-push commands
@shafty023
shafty023 merged commit b2a56b7 into closedloop-ai:main Oct 23, 2026
10 checks passed
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