Skip to content

ISS-12135: Require per-key approval for vibe flags and no new tech debt - #316

Merged
shafty023 merged 7 commits into
mainfrom
fix/vibe-flag-key-guard
Oct 20, 2026
Merged

shafty023 merged 7 commits into
mainfrom
fix/vibe-flag-key-guard

Conversation

@shafty023

@shafty023 shafty023 commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

About 14 vibe sessions in symphony-alpha each added PostHog keys and nothing challenged them. One session gated a single Branches list redesign behind branches-list-adapter-connected, -disconnected and -rollback; others gated edits to shipped surfaces, and no handoff said which keys to turn on. Flags are now the tested case of a general rule: a change grows no tech debt without a cited reason. The PR applies that rule to itself: each rule is stated once, other files reference it in a line, and the script output carries the procedure.

Stated once (skills/vibe/references/guardrails.md):

  • "No new tech debt". These need a human source or a quoted repo rule, otherwise the writer reuses what exists: a new flag key, a parallel path (a second route, service, writer or store), a shim or code kept for a deprecated story, a second owner, a duplicated helper, type, constant, validator or bound, new config, or growth of a file near the size ceiling. The writer logs a Debt: line; its own rationale never counts.
  • "Feature flags".
    • No agent creates or adds a flag on its own.
    • A key is proposed only for what the target repo's own flag rule allows, quoted from its AGENTS.md with a human source. What the rule ships directly ships ungated.
    • One behavior gets one key, and a key already gating the surface (on main or in another session's list entry) is reused.
    • The person approves each key after seeing the key, why, what it gates and the whole stack. Only an explicit yes builds it. No answer, or a run with nobody to ask, builds nothing.
    • An agent never adds or edits a flag registration attestation (symphony-alpha ISS-8676: a ledger row or verification date only a human makes); the approval and "Keys to see this work" tell the human which key to ledger.

Referenced in a line each:

  • vibe-change-worker: propose and return NEEDS_FLAG_APPROVAL; follow "No new tech debt".
  • vibe/SKILL.md: the NEEDS_FLAG_APPROVAL status, and section 7 (ask the printed question, skipping the research gate).
  • quality-loop.md: flag approval stays with the person.
  • handoff/SKILL.md: ask a pending approval once, and tell the person the keys to see the work.
  • ticket-template.md and vibe-environment-worker (the PR summary): carry "Keys to see this work".
  • vibe-guardrails-reviewer, new check 17: each kind of debt the guardrails section lists without a Debt: reason is blocking (mechanisms found by checks 1, 7, 8 or 12, copied or divergent types, constants, validators and bounds, exports only tests or deprecated stories import, growth near the size ceiling); so is any key flag-keys.mjs show reports a problem for, or one that the repo rule does not support, splits one behavior, gates a change the rule ships directly, or duplicates an existing key. A cited source is not approval.

Carried by skills/vibe/scripts/flag-keys.mjs (record, question, approve, show):

  • record validates the justification and reports pending keys with the next step.
  • question prints the exact approval wording and how to ask it, record it, and approve or decline.
  • approve saves who and when, and only for an answer to that exact question. An approval covers one key and the proposal it answered.
  • record refuses a read of an unapproved or declined key, and a second new key on one surface.
  • ticket-sections renders "Keys to see this work" (per surface and host, every key that must be on, its approval and its snapshot value). vibe-sessions.mjs list shows each session's keys. live-ticket-check.mjs fails a ticket without the keys block and, with --worktree, a session with an unapproved key.

Cross-session view. vibe-sessions.mjs list is vibe's existing one. cl-work-report and cl-sweep are ClosedLoop ticket views that do not read vibe session records, so they are unchanged.

Instruction length. 8 vibe agent and skill markdown files changed: +77 / -16 lines (net +61), +614 words. Down from net +228 lines and +2,615 words in the previous head.

Coordination.

Validation

  • node --test on the vibe, handoff and codex-fleet-monitor scripts: 174 tests. One untouched run-bounded.test.mjs timing case failed under machine load, then passed twice alone.
  • flag-keys.test.mjs covers: no read without a yes, the exact-question check, who and when recorded, a stale proposal asked again, no carry-over between keys, declined, and no operator.
  • uv run pytest plugins/vibe: 343 passed, including test_vibe_flag_keys_contract.py (all its tests kept, rewritten against the shorter text and the script's messages).
  • uv run ruff check .: clean. uv run pyright plugins/vibe: 0 errors.
  • The pm-flag-key-sprawl-blocked eval scored 1.00 in most runs. This is indicative only: the eval CLI refuses the closedloop-core plugins entry every vibe case carries, and from a temporary copy it often failed to load vibe.

Risk

A session cannot hand off a gated change until the person approves each key or the gate is removed. Sessions started on an older version must record their keys at handoff.

- Add a canonical Feature flags section to guardrails.md: the target
  repo's own flag rule decides, each new key or read needs a quoted
  rule sentence and a human source, one behavior gets one key, and an
  existing key for the surface is reused
- Add vibe-sessions.mjs flag-keys, which records per surface the keys
  that must be on to see the work and refuses unjustified or split keys;
  list shows them per session and ticket-sections renders them
- Make live-ticket-check.mjs fail a ticket without the keys block
- Have the writer justify keys before building, add guardrails review
  check 17, and carry the keys through handoff, the ticket, the summary
  and a requested pull request
- Add a contract test and the pm-flag-key-sprawl-blocked eval case
- Bump vibe to 1.0.61

Testing: node --test for the vibe and handoff scripts (161 passed),
uv run pytest plugins/vibe (341 passed), ruff, pyright, manifest
parity, and the new eval case (1.00 on this branch, 0.67 on main)
Risks: handoff now blocks until the session records its flag keys
- Move flag keys to flag-keys.mjs with record, question, approve and
  show; a proposed key is built only after the person answers its exact
  question with a yes, recorded with who and when, and only for that
  proposal
- Add the No new tech debt principle to guardrails.md, mapped onto the
  writer's owner research and reviewer checks 1, 7, 8, 12 and 17
- Have the writer return NEEDS_FLAG_APPROVAL instead of gating, and the
  vibe skill ask the question and record the answer
- Make live-ticket-check fail a session with an unapproved key
- Extend the contract test and the eval case to cover approval

Testing: node --test for vibe and handoff scripts (167 passed),
uv run pytest plugins/vibe (343 passed)
Risks: a session cannot hand off a gated change until the person
approves each key or the gate is removed
@shafty023 shafty023 changed the title ISS-12135: Guard vibe flag keys and list keys to see the work ISS-12135: Require per-key approval for vibe flags and no new tech debt Oct 20, 2026
- Keep No new tech debt and Feature flags only in guardrails.md and
  point the writer, skills, reviewer, ticket template, quality loop and
  environment worker at them in a line each
- Move the approval procedure into flag-keys.mjs output: record lists
  pending keys with the next step, question prints how to ask and record
  the answer
- Fold the tech debt check into reviewer check 17 and leave checks 1,
  7, 8 and 12 as they are on main
- Rewrite the contract test against the shorter text and the script

Testing: node --test vibe, handoff and fleet monitor scripts (174, one
unrelated timing test passed on rerun), uv run pytest plugins/vibe (343)
Risks: none beyond the previous commit
- Make check 17 block a copied or divergent type, constant, validator or
  bound, an export or component only tests or deprecated stories import,
  and growth of a file near the repo's size ceiling, not only mechanisms
  checks 1, 7, 8 and 12 find

Testing: uv run pytest plugins/vibe/tools/python (323 passed)
Risks: None identified
- Add one line to the Feature flags rule: an agent never adds or edits a
  flag registration ledger row or verification date; the per-key approval
  and Keys to see this work tell the human which key to ledger

Testing: uv run pytest test_vibe_flag_keys_contract.py (9 passed)
Risks: None identified
@shafty023
shafty023 merged commit a4abd28 into main Oct 20, 2026
10 checks passed
@shafty023
shafty023 deleted the fix/vibe-flag-key-guard branch October 20, 2026 18:13
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