What changed, and why
This PR touches no behavior. It is a truth-correction pass over froot's docs and docstrings, made because the code has out-run the prose. The old docs described one loop (dependency-patch), npm-only, pre-launch, with no track record and no reputation surface. None of that is true anymore.
What's true today: two loops run on the shared chassis — dependency-patch (now npm and uv) and a determinism reviewer for Temporal workflows. The dependency-patch loop has opened, CI-verified, and merged real PRs across several repos, and correctly closed at least one after CI went red. The reputation read-model that the SPEC promised as a non-goal-for-now now exists as a derived dashboard.
The risk in a docs PR is the opposite of a code PR: not "does it work" but "is each claim now accurate." So this guide walks the load-bearing edits — the dated Status block, the Roadmap stage markers, the reconciled Principle 7, the user-facing dashboard copy, the .env.example knobs, and the source docstrings — and checks each against the code it now describes.
SPEC.md — a dated Status block and honest stage markers
The SPEC is the charter; it had no statement of where reality stands against it. The PR adds a dated Status block right under the charter preamble. It names what is built and running (both dependency-patch ecosystems, the determinism reviewer, the derived dashboard) and what is not (security-patch, any autonomy gate — every PR is still human-approved).
The new Status block — dated, and explicit about built-vs-not.
SPEC.md · 271 lines
⋯ 14 lines hidden (lines 1–14)
⋯ 250 lines hidden (lines 22–271)
The Roadmap then gets per-stage markers (done / partial / not-started), so a reader can see the charter's staging against the actual state. Stage 1 is done and past its original bar; stage 2 (security-patch) is untouched; stage 3 is partial because the determinism reviewer shipped — and shipped ahead of stage 2, which the text flags as a deliberate deviation rather than hiding it.
Roadmap with ✅ / ◐ / ○ markers and the disclosed out-of-order stage 3.
SPEC.md · 271 lines
⋯ 223 lines hidden (lines 1–223)
⋯ 29 lines hidden (lines 243–271)
SPEC.md — non-goals catch up, and Principle 7 is reconciled
Two non-goals were written as future but have since happened, so the PR annotates them in place rather than deleting them — the deferral history stays legible. The track record now exists (real merges plus a CI-caught rejection). The reputation store is still deliberately not built, but the read-model dashboard now does exactly what that non-goal described: it computes reputation on read and stores nothing.
Non-goals annotated: the record exists; the dashboard is the read-model; a sibling loop already reuses the chassis.
SPEC.md · 271 lines
⋯ 247 lines hidden (lines 1–247)
⋯ 12 lines hidden (lines 260–271)
Principle 7 — grow by adding loops, not by broadening one — said froot "stays at one loop until it closes end-to-end." A second loop now exists, so the principle needed reconciling, not quiet contradiction. The edit keeps the principle and appends the disclosure: the determinism reviewer arrived before dependency-patch's planned successor, a deliberate, noted deviation that points back to the Roadmap.
Principle 7, with the disclosed deviation appended in italics.
SPEC.md · 271 lines
⋯ 93 lines hidden (lines 1–93)
⋯ 173 lines hidden (lines 99–271)
dashboard/render.py — user-facing copy that two loops broke
The dashboard renders strings a human reads. Two of them silently became wrong once a second judge appeared over the same model. The judgment section's heading said "the one model call" — but there are now two judges (changelog risk and the determinism frontier) sharing one model, so "one model call" is false. The fix scopes the claim to this loop and renames the heading to "the changelog verdict."
Heading and note rescoped: this loop's model job is the changelog verdict.
src/froot/dashboard/render.py · 659 lines
⋯ 292 lines hidden (lines 1–292)
⋯ 358 lines hidden (lines 302–659)
The other class of fix is ecosystem scope. Several strings said "npm patch bumps" when the loop now also handles uv. The track-record note and the authority-envelope footer both get the same correction: npm + uv patch bumps.
Track-record note: 'npm + uv patch bumps by construction.'
src/froot/dashboard/render.py · 659 lines
⋯ 237 lines hidden (lines 1–237)
⋯ 416 lines hidden (lines 244–659)
Footer authority envelope: trust is narrow to npm + uv patch bumps.
src/froot/dashboard/render.py · 659 lines
⋯ 498 lines hidden (lines 1–498)
⋯ 153 lines hidden (lines 507–659)
.env.example — the new knobs and the @uv repo syntax
The example env file is the operator's contract; it must list every knob that exists. The PR documents three real things the code already reads. First, the repo list now takes a per-repo ecosystem suffix: a bare slug is npm, @uv marks a Python/uv repo, and both loops read the same list.
FROOT_REPOS @uv suffix and the dependency-patch scan interval.
.env.example · 41 lines
⋯ 8 lines hidden (lines 1–8)
⋯ 25 lines hidden (lines 17–41)
Then the two subsystems that had no documented config: the determinism-review loop (enable flag, poll cadence, call-depth) and the read-model dashboard (enable flag, port). Both are on by default and commented out here as the defaults. The model comment is also rescoped — it is the shared judge model behind both judges, not just the changelog judge.
FROOT_REVIEW_* and FROOT_DASHBOARD_* — the two undocumented subsystems.
.env.example · 41 lines
⋯ 17 lines hidden (lines 1–17)
⋯ 14 lines hidden (lines 28–41)
The model comment rescoped: shared across both judges.
.env.example · 41 lines
⋯ 34 lines hidden (lines 1–34)
Source docstrings — 'one model call' and 'both loops' corrected
The same two falsehoods — one model call, and two (rather than four) workflows — lived in module docstrings too. These are the docs a developer reads at the import site, so they get the same correction. The changelog-judge module no longer claims to be "froot's one model call"; it is one of two, with the determinism reviewer's frontier judge as the other.
'froot's changelog model call — one of two.'
src/froot/adapters/model_judge.py · 89 lines
⋯ 78 lines hidden (lines 12–89)
The shared model module is retitled from "the model behind the changelog judgment" to "the shared judge model," and the adapters package docstring lists both judges over one shared model. The settings docstring for ModelSettings makes the same shared-endpoint point.
Retitled: the shared judge model, behind both judges.
src/froot/adapters/model.py · 53 lines
⋯ 49 lines hidden (lines 5–53)
ModelSettings: shared endpoint, used by both judges.
src/froot/config/settings.py · 230 lines
⋯ 112 lines hidden (lines 1–112)
⋯ 113 lines hidden (lines 118–230)
Finally the workflow package docstring described the chassis as if only dependency-patch's two workflows existed. It now names the chassis as shared by both loops and adds the reviewer's mirrored shape: review_workflow self-schedules and dispatches a pr_review_workflow per open PR — the same scan→per-item structure as scan/bump.
The chassis is shared by both loops; the reviewer mirrors scan/bump.