v0.9.0 — Apply the proven preferences
vibe-council v0.9.0 — Apply the proven preferences (tighten-only, advisory)
Ready for the v0.9.0 tag — not yet tagged. The version bump (
backend/__init__.py,
pyproject.toml,uv.lockself-version) and the datedCHANGELOG.mdsection are done
(chore: prepare v0.9.0 release); the repo now reports0.9.0. The git tag and the GitHub
Release are still a separate, manual step — see §"Final release checklist" below and
docs/release-checklist.md.
The first release of the v0.9.x line. Its theme — "Apply the proven; describe the personas; defer
their behavior" — is the "later" that the whole v0.8.x line was built toward: v0.8.2 defined and
validated the tighten-only preference schema; v0.9.0 finally applies it, but only the four
mechanically-proven, add-friction-only keys, and only in bounded, advisory, tighten-only ways. The
deterministic trust boundary, the executor, the Workbench, and the council prompt/ranking/synthesis
paths remain completely preference-blind — locked by tests. No named-persona behavior, no prompt
emphasis, no new command, no new dependency. Based on and crediting
karpathy/llm-council.
No commercial-clearance claim. License/provenance remains an unresolved "Question 0"
commercial gate; noLICENSEis added.
How v0.9.x was planned (council-backed + Fable architecture)
- Council-backed planning (docs-only, two
vibe review --preset balancedpasses):
v0.9.x-council-debate.md,
v0.9.x-phase-brief.md, and
v0.9.x-fable-input.md. The council rejected shipping persona
behavior as premature — the dissent-suppression / viewpoint-bias failure mode is a new risk
class the tighten-only proof machinery does not cover. - Fable architecture plan (docs-only, version-line):
v0.9.x-architecture-plan.mdresolved the theme into a two-
release line — v0.9.0 apply-the-keys (PRs 1–5) and v0.9.1 persona lenses + v0.10.x
dissent-preservation sketch — and a spec/CLI resolution (schema v1'sfullis a council mode,
not a--presetvalue → notice-only; schema v1 stays frozen).
What this release adds — the four proven keys, applied
The clamped reader (PR 1)
- New
effective_suggestions(project_root) → Suggestionsin
backend/preferences.py: pure, read-only, fail-closed. It reuses
the v0.8.2 validator internals and returns clamped tighten-only values only — raw parsed JSON never
escapes:review_preset_flooris emitted only for a preset strictly above the project baseline
(so "lower" is inexpressible andpremiumis unrepresentable), the path fields are re-validated,
de-duplicated relative-path tuples, andrequire_usageis a plain bool. Any anomaly (missing /
invalid / oversized / multiple-block / out-of-root / undecodable block, or any validation error) →
NEUTRAL. It reads exactly one file (PREFERENCES.md, realpath-inside-root), writes nothing, and
never reads a.council/profile.*store.
Review/diff preset floor + --no-preferences (PR 2)
vibe reviewandvibe diffresolve their preset through the reader — tighten-only and
CLI-first. Precedence (highest wins): an explicit--preset(CLI always wins) → the new
--no-preferencesescape hatch (baseline; byte-identical to pre-v0.9.0) → a validated preference
floor (can only raise toward more review, never lower, neverpremium) → the project
baseline (DEFAULT_PRESET).- Because the baseline is already
balanced(the strongest non-premium preset), the observable effect
is that adefault_review_preset: "full"block prints one pinned stderr notice recommending
vibe fulland leaves the preset at the baseline —fullis a council mode, not a--preset
value; nothing is silently reinterpreted. A genuine raise (a future lower baseline) is likewise
announced. extract/mini/fullmodes never consult preferences.
Usage-flag warning (PR 3)
- On
vibe review/vibe diffonly, when the block setsrequire_usage_flag: trueand--usageis
absent, a single pinned stderr warning ("pass--usageto see token usage (advisory)"). It
never adds--usage, never fails, never changes cost/model behavior, and is suppressed by
--usage,--no-preferences, or an explicit choice.
Doctor staged-path advisories (PR 3)
vibe project doctoremits advisory[warn]lines in its staged-file section when a staged path
equals/prefixes a declarednever_stage_extra/extra_sensitive_pathsentry — naming the preference
key (never its configured value / raw JSON), volume-capped at 10 lines plus a roll-up.
Advisory only: READY/NOT-READY, exit code, and the[FAIL]dangerous-staged set are byte-stable;
a missing/invalid block leaves doctor behavior unchanged. It does not change staging, git, or the
no-stage guard.
Isolation lock-in (PR 4)
tests/test_preferences_isolation.pylocks the boundary:
an allowlist-first static scan (thebackend.preferencesimporter set is exactly{cli.py};
effective_suggestionsis referenced only in{preferences.py, cli.py}; an explicit forbidden-
surface list — allworkbench_*,council.py,openrouter.py/providers.py,guards.py,mcp_*,
context_pack.py— imports none of it), a behavioral byte-identity check (the deterministic trust
evaluation and the dry-run executor are identical with and without a maximal valid block), pointer-
only guide/context-export tripwires, doctor READY stability, and a pack-21/21-with-block check.
Safety posture
- Advisory / tighten-only only. Every preference effect can only add friction/warnings — never
enforce, never block, never fail a command, never lower a preset, never selectpremium, never
auto-add--usage, never change staging/git. An explicit--preset/--usage(or--no-preferences)
always wins. - Preference-blind trust surface. The guard, executor, Workbench, proposal/importer/panel, and the
council prompt / ranking / synthesis paths never read preferences (proven by static scan +
byte-identity tests). Guide / context-export stay pointer-only. No.council/profile.*store. - Raw JSON never escapes the reader; all preference-driven text is stderr-only and pinned.
- No new dependency (stdlib-only; the only
uv.lockchange is thevibe-councilself-version
line). Redaction lint stays 0 critical (30 warnings);context checkand MCP health stay
21/21.
Explicit non-goals (deferred, not started in this release)
No named-persona behavior · no prompt emphasis · no ranking/synthesis influence · no persona schema
objects or persona validator (v0.9.1 documents the review lenses as prose; v0.10.x is the persona-
behavior line behind a dissent-preservation framework) · no UI/dashboard · no .council/profile.*
local store · no Workbench trust-boundary/executor change · no command-allowlist growth · no hosted /
network / LAN / mobile mode · no vector DB / database stack · no /council real command. v0.9.0 applies
the four proven keys, advisory/tighten-only, and nothing more.
Release checklist (must be clean before tagging)
-
uv run python -m unittest discover -s tests -t .is green (990 tests). -
uv run python -m backend.cli --versionprintsvibe-council 0.9.0. -
vibe lint --redactionreports 0 critical (30 warnings, expected). -
vibe decisions lintpasses. -
vibe project doctorreports READY and shows thePreferences (machine-readable, advisory):
section. -
vibe context checkscores 21/21. -
vibe mcp inspect --context --healthscores 21/21. -
uv syncproduces nouv.lockdiff beyond the intentional self-version sync in this PR. - No
.council/artifact (incl..council/runtime/payloads/and.council/profile.*)
staged/committed; no rootCLAUDE.md/AGENTS.md/FABLE.md; no private/local plan file staged.
Final release checklist (manual steps, after this PR merges)
These are not automated by this PR — a maintainer performs them once the checklist above is clean
on master:
- Merge this PR (
chore: prepare v0.9.0 release) intomaster. - On the merged
master, re-confirm: tests green,vibe --versionreportsvibe-council 0.9.0,
redaction lint 0 critical (30 warnings), decisions lint passes,vibe project doctorREADY (with
the preferences advisory section),context check21/21, MCP health 21/21, anduv syncshows no
drift. - Verify
git status --shortand the merged diff carry no private/local artifacts
(.council/,.council/profile.*, the two untracked private plan files,.env/.venv//data/,
secrets, generated packs/exports, rootCLAUDE.md/AGENTS.md/FABLE.md). - Create an annotated git tag:
git tag -a v0.9.0 -m "vibe-council v0.9.0"on the merge
commit, thengit push origin v0.9.0. - Create the GitHub Release for
v0.9.0manually, using this document
(docs/releases/v0.9.0.md) as the release-notes body.
Known limitations
- Small observable delta by design. With the
balancedbaseline, the preset floor is notice-only
(no preset actually changes); v0.9.0's deeper value is the application pattern (single clamped
consumption point, fixed precedence, byte-identity guarantees) that later work reuses. - Preferences are advisory. Nothing here enforces or blocks; an explicit CLI flag always wins.
- Personas are not applied. Named council personas remain a future direction (v0.9.1 documents them
as prose review lenses; v0.10.x is the behavior line). - No hosted/team/mobile/voice — deferred to later phases.
Next after v0.9.0
Cut the v0.9.0 tag/GitHub Release, then continue the v0.9.x line per
v0.9.x-architecture-plan.md: v0.9.1 — the council review
lenses as pure documentation (Security Guardian, Cost Skeptic, Local-first Guardian + stubs; no
schema, no behavior) plus the v0.10.x dissent-preservation design sketch and a v0.9.0 dogfood pass.
Persona behavior, a .council/profile.* store, named-persona UX, mobile/LAN/voice, and hosted/team
all stay deferred behind their own gated designs.