Skip to content

feat(vibe): Test named mechanisms and decision provenance in vibe sessions - #299

Merged
shafty023 merged 3 commits into
mainfrom
feat/vibe-mechanism-provenance
Oct 16, 2026
Merged

shafty023 merged 3 commits into
mainfrom
feat/vibe-mechanism-provenance

Conversation

@shafty023

@shafty023 shafty023 commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Purpose

#298 taught cl-execute to weigh a mechanism a ticket names against what already exists, to trace recorded decisions to a human, and to keep human-decision findings out of the planner's hands. Vibe sessions had none of that, so a writer could still build whatever mechanism a ticket named, read a Decisions section as settled, and a reviewer finding that needed a human could be quietly absorbed. This ports the same intent to vibe without adding a plan review, a lint script or a build-loop step, and without asking the vibe person any engineering question.

What changed

  • Guardrails reviewer (handoff only) gains a Mechanisms check: for each new poller, interval, background lane, socket, cross-process channel, route, table, cursor, or write into another tool's files, name the existing mechanism that could do the job and why it was not used, state what its cost scales with at 10x and 100x plus the per-device idle cost, and never argue from fleet size. Idle-scaled cost when an activity-scaled design meets the outcome, or a write into another tool's data with no cited human decision, is blocking. A ticket-named mechanism gets the same test, and the plan's Cost Scaling table is checked against the diff.
  • New Engineering decision: <question>; built: <...>; evidence: <...> item. Handoff section 6 and quality-loop.md: a finding that a mechanism, a one-way door, or an unratified or conflicting decision needs a human decision is never the writer's to resolve, downgrade or absorb; it goes on the live ticket's Engineering checklist, never to the person. Ticket template and ticket worker place it.
  • Change worker: only the outcome binds; a named mechanism is a proposal. When it is dominated, the writer builds the better option and logs the swap as an Engineering decision (never NEEDS_PERSON). Plans that add periodic or cross-surface work carry a Cost Scaling table using cl-execute's planning-and-gates.md columns and rules.
  • Requirements worker tags named mechanisms mechanism (proposal) instead of acceptance criteria.
  • quality-loop.md defines settled: a decision traces to a human statement (who, where, which question). Unratified or conflicting ones go through the existing product necessity gate or become an Engineering decision; never resolved by recency. Every "never re-ask a settled decision" now points at that definition.
  • closedloop-graph.md gains a route for channels that already connect two surfaces.
  • Frozen rows describe user-visible behavior, never mechanism.
  • vibe 1.0.47 to 1.0.48, CHANGELOG entry.

Commands run

node --test <every plugins/**/*.test.{mjs,cjs,js} file, as CI does>   # 392 pass
uv run pytest plugins/ -q                                            # 2316 passed, 3 skipped
uv run ruff check . && uv run pyright                                 # clean
cd tools/plugin-manifests && npm ci && npm test                       # 6 passed

New test_vibe_mechanism_provenance_contract.py pins the rules above; each test fails when its pinned phrase is removed. Three existing tests pin the Engineering checklist row in the ticket template's ownership table; that row now also lists engineering decisions, and the assertions were updated to the new row (no rule removed). The lean loop, same-family and no-plan-review contract tests pass unchanged.

Second commit: tools/python/vibe_contract_text.py now holds the flat, section, paragraph and agent helpers that 22 vibe contract tests each kept a copy of. Where copies differed, the shared section stops at the same or a higher heading level and matches its heading at line start (exact line preferred), paragraph splits list items, and agent returns raw text (three files now call flat(agent(...))). Every call site whose output changed was compared against its old helper, and only positive assertions read those sites. pytest plugins/vibe: 252 passed; pytest plugins/: 2316 passed, 3 skipped.

…sions

- The handoff guardrails reviewer checks each new poller, lane, socket,
  channel, route, table, cursor, or write into another tool's files:
  the existing mechanism it could reuse, what its cost scales with at
  10x and 100x, and its per-device idle cost, against the plan's Cost
  Scaling table when there is one.
- A finding that needs a human decision becomes an Engineering
  decision item on the live ticket's Engineering checklist; the writer
  never resolves, downgrades, or absorbs it, and it never reaches the
  person.
- The change worker treats a mechanism a ticket names as a proposal,
  builds the better option when it is dominated, and logs the swap as
  an Engineering decision. Plans with periodic or cross-surface work
  carry a Cost Scaling table.
- The requirements worker tags named mechanisms as proposals instead of
  acceptance criteria.
- A decision is settled only when it traces to a human statement;
  conflicts are never resolved by recency.
- A closedloop-graph route for existing channels between surfaces.
- Frozen rows describe user-visible behavior, never mechanism.
- Release vibe 1.0.48.

Testing: node --test over every plugin test file (392 pass), pytest
plugins/ (2316 pass, 3 skipped), ruff and pyright clean, manifest
version check passes. New contract tests fail when each pinned phrase
is removed.
Risks: Reviewers may flag ordinary routes or tables as mechanisms; the
check only asks for the reuse and cost reasoning, so the cost is a
longer review.
- Move flat, section, paragraph and agent into
  tools/python/vibe_contract_text.py and import them from all 22 vibe
  contract tests instead of keeping a copy in each.
- section stops at the next heading of the same or a higher level
  and matches its heading at the start of a line, preferring an exact
  line. Older copies stopped at the same level only, at "## " only, or
  took a level argument; one required an exact line.
- paragraph also splits list items; one copy did not.
- agent returns raw text; the three files whose copy flattened it now
  call flat(agent(...)).

Testing: pytest plugins/vibe (252 pass) and pytest plugins/ (2316
pass, 3 skipped); ruff and pyright clean. Every call site whose result
changed was compared against its old helper; each only narrowed a
section or paragraph that positive assertions still find.
Risks: None identified
- Guardrails item 12 counts a recorded decision as the cited human
  decision only when it traces to a human statement; a Decisions
  section alone is a record, and an untraced one is an engineering
  decision finding.

Testing: pytest plugins/vibe (252 pass); the new assertion fails when
the sentence is removed.
Risks: None identified
@shafty023
shafty023 merged commit 0f7035e into main Oct 16, 2026
10 checks passed
@shafty023
shafty023 deleted the feat/vibe-mechanism-provenance branch October 16, 2026 16:28
shafty023 added a commit that referenced this pull request Oct 16, 2026
…sion's base (#301)

* fix(vibe): Judge Storybook stories by the lifecycle scheme of the session's base

symphony-alpha #8867 (ISS-12610) made the plain roots current and moved the
old ones to <Level> v1.0 with a deprecated tag. The handoff inventory read
every plain-root story as frozen v1.0, so a synced session was blocked.

- Read the scheme from the base's story-group-order.ts: the lifecycle
  scheme takes its frozen tag and v1.0 root suffix from StorybookLifecycle
  and storybookBaselineVersion; an older base keeps the v1.0/v2.0 rules.
- Keep the freeze, the compile-only repair, the retitle check against the
  synced base and the Start Here exemption on both schemes.
- Translate the placement rules in guardrails, the decomposer, the writer,
  the reviewer, design pass, handoff and README.

* chore(vibe): Release 1.0.49

Changelog entry for the Storybook lifecycle scheme fix, and the
base-scheme contract test reads agent text through flat() after the
shared helpers from #299 started returning raw text.
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