Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions EVOLUTION_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,10 @@ Graph nodes updated: task-log rows for #55 rounds 1–2 and this entry's close-o
**Lesson (reusable rule)** | A verification step that bundles two receipts into one output can misread its own evidence — one receipt per command, and grep for the EXPECTED string, not the absence of failure.
**Graph nodes updated** | v0.0.41 release row (#171/#173 shipped).

## 2026-09-26 — Importing the discipline, not just the doctrine (#177)
**What I tried** | The comments kept citing `common/src/testing/freebuff-offer-invariants.ts` as the thing "that exists to catch the offer-without-gate shape" — so fetch the cited file and port the checks, not the prose.
**What failed + evidence** | Two of my test expectations were wrong before the checker was right. First, the double-drift case: I expected the lock AND root violations, but #173 had given every row its own root — only the lock fires now, which is the corrected doctrine working (my assertion was a fossil of the old map). Second, CodeRabbit caught the checker's blind spot: my missing-label check accepted ANOTHER model's nonempty label — the exact GLM 5.2 Desktop incident upstream documented (earned row rendered under the fallback's label). Wrong ≠ missing, and only an independent expected lookup sees the difference.
**What worked** | `freebuffOfferViolations` in contracts with six checks wired to OUR gates, applied to three surfaces in their own tests, seeded violations for every check. One surprise from upstream's shape: an empty offer set is a violation — the test that wires itself to the wrong list is the failure a green run hides.
**Lesson (reusable rule)** | (1) An invariant cited in a comment is executable somewhere — find the file, port the checks, and wire them to the surfaces that must hold. (2) A checker that has never failed is a mirror: seed its violations or it tests nothing but the happy list. (3) Wrong and missing are different failure shapes — "not undefined" is not "correct"; verify labels against an independent expectation.
**Graph nodes updated** | offer-invariants row (#177).

1 change: 1 addition & 0 deletions docs/KNOWLEDGE_GRAPH.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,4 @@ Model opportunities seen in `free-agents.ts:125-132`: `deepseek`, `mimo`, `minim
| 2026-09-26 | PROBE ETIQUETTE AS CODE + BAN CHECK + LIVE RECEIPT (#171): the #141 laws became enforceable code — `probeEtiquette.ts` (pure, time injected): 1 admission / 60 min (the "1 hour" is the SPACING between admissions, not a session length — a session is a seat held for a turn and released; admission CHURN was the ban trigger), never 02:00-05:00 UTC for active probes, PASSIVE mode exempt so the checker works exactly when needed; `banCheckVerdict.ts` (pure): status+body → verdict, a bare 403 is NOT a ban (the wire keys on the body status); `ban-check.ts`: default ONE authenticated session-surface GET (no seat, no turn), `--active` requires `--i-understand-the-etiquette` and is VETOED by the guard (exit 3, runbook pointer, retry window). LIVE RECEIPT (passive, zero seats): still banned — `403 {"status":"banned"}`, first confirmation since 09-24; zero admissions run per the cheapest-touch law (the GET already proved what an admission would). Plus `docs/operations/ban-appeal-141.md` (drafted, UNSENT — owner decision) and the picker row-matrix audit test (servable/locked/drained classes, badge×lock×consent law in one table). Effect `globalDate` law caught two raw-Date constructions — pure logic takes epoch-ms in, scripts opt out via header. LAW: runbook rules that matter get compiled — a rule a 2am operator can override is a suggestion, not a law. | scripts/probeEtiquette.ts (+test), banCheckVerdict.ts (+test), ban-check.ts, docs/operations/ban-appeal-141.md, modelPickerRowMatrix.test.ts, PR #171 |
| 2026-09-26 | SERVER VERDICT GOVERNS THE LOCK (#173): research into the #166 doctrine's wire truth found its missing half — upstream `FreebuffFreebucksInfo.planRequiredModelIds` is the SERVER's per-viewer verdict ("a client deciding this for itself would be reading its own belief"), present = authoritative (the gate turns on the resolved access tier, and once the COUNTRY), absent = static-list fallback (old-client law). The port corrected a #162 doctrine error: tier-LOCKED ≠ INADMISSIBLE — upstream keeps gpt-6-luna, mimo-v2.6-pro AND gemini-3.8-flash in its pairing map (base3-free-luna-6, base3-free-mimo-2-6-pro, base3-free-gemini-3-8-flash), so entitled sticky picks run the REAL model through their own roots instead of silently coercing; a limited viewer is refused by the server with plan-required prose, which our gate renders client-side first. Contracts: field on FreebuffProviderFreebucks, pairing map = upstream's exact admission set (10 rows), offer-without-gate rewritten to upstream's TRUE shape (forbidden pair = locked ∧ FRESHLY SELECTABLE, not locked ∧ servable). Session: field declared on the freebucks projection (pass-through body already carried it — the census error was invisible because the wire was never read). Web: ONE precedence authority — every lock gate (picker join, disabled reason, switch, all three send legs) reads the server verdict through resolveLockedModelBlock; CodeRabbit Major #2 caught the stale-verdict hazard (helper declared after both useCallback'd plan handlers, deps missing it → snapshot-only refresh left a stale verdict blocking entitled viewers / admitting newly locked ones) — hoisted above both handlers, deps wired. Contracts 24/24, adapter 10/10, chat 294/294, typecheck clean, gates + Tests verified on final head. LAW: a client-side gate built from a census is a BELIEF — read the wire for the verdict; and the forbidden pair in any offer-without-gate law is the exact pair upstream's own comment names, not the one your first draft found convenient. | packages/contracts/src/server.ts, model.ts (+test), FreebuffSession.ts, FreebuffAdapter.modelcoherence.test.ts, modelLockedRow.logic(+test), ModelPickerContent.tsx, ChatView.tsx, modelPickerRowMatrix.test.ts, PR #173 |
| 2026-09-26 | RELEASE v0.0.41 (#171/#173→#175): published through the full runbook — bump PR #175 (gates + Tests verified on head) → build + stage → Publish gate GREEN on 7494a9636 (FIFTH consecutive) → publish → triad: E409, packument `latest=0.0.41` in 3 polls (~5 min), fresh-dir install 0.0.41 + audit 0 + `--version` match + boot 200/200 (teardown shape from #168 worked FIRST try). Carries #171 (etiquette guard + ban check + live still-banned receipt + appeal draft) and #173 (server verdict governs the lock; pairing map restored to upstream's exact admission set of 10 rows; locked ≠ inadmissible). | .publish-stage @ 0.0.41, PR #175, #171/#173 shipped |
| 2026-09-26 | OFFER-INVARIANTS CHECKER PORTED (#177): fetched the file upstream's own comments cite — `common/src/testing/freebuff-offer-invariants.ts` — and ported the discipline with our gates in the checks: model ids fan out across separate hand-kept lists ON PURPOSE, so only `offered ⊆ accepted` is checked (drift the other way is how a staged retirement works); an empty offer set is a VIOLATION ("the one failure mode a green run hides"); six checks (map membership → fresh surfaces never offer locked rows, listed surfaces skip it → resolver never coerces (#152) → roots answer their own pairing (the default root answering for a non-default model IS a coercion) → display names render the row as itself). Applied to THREE surfaces in their own tests (driver fresh picker, driver listed picker, adapter admission set) — adding a gate to the checker applies it everywhere at once. CodeRabbit Major caught the wrong-label shape: `nameFor` returning ANOTHER model's nonempty label passed every check — the exact GLM 5.2 Desktop incident upstream documented (earned row rendered under the fallback's label; wrong, not missing) — fixed with an independent `expectedNameFor` lookup + seeded teeth. Seeded-violation tests prove every check can fail; the double-drift case correctly yields exactly the lock violation since #173 gave every row its own root. Contracts 302/302, driver 8/8, adapter 8/8, chat 294/294, typecheck clean, gates + Tests verified on final head c4403e3c6. LAW: a checker that has never failed is a mirror, not a gate — seed its violations; and an invariant worth citing in a comment is worth importing as code. | packages/contracts/src/freebuffOfferInvariants.ts (+test), index.ts, FreebuffDriver.snapshot.test.ts, FreebuffAdapter.subagents.test.ts, PR #177 |
Loading