Version. v0.1
Date. 2026-08-04
Author. Claude.ai (drafting session). Approving: Marvin Percival.
Charter. standing-notes/dunin7-standing-authorization-charter-v0_1. No session executes a change request it drafted (§1).
Target. /Users/dunin7/loomworks-engine at main 3b10ff1; /Users/dunin7/loomworks at main cb8b940. Two repositories, separate branches, separate merges and tags.
Baselines. Neither repository is green, and **the criterion is no new failure, against sets recorded at Step 0** — not a clean exit, which this change request does not achieve. See §1.
Build-list items. B-33, B-34, B-42.
CR number. CR-2026-166. Highest confirmed is CR-2026-165. [EXECUTING SESSION: verify; advance if taken.]
Grounding. inspection-briefs/loomworks-clean-report-cluster-step-0-findings-v0_1 — every anchor and every mechanism below was read or run there.
Status. Pre-execution.
It makes three specific reports readable: the engine's test suite, the surface's test exit code, and the surface's lint errors.
It does not make either repository report clean, and the build list said it would. The Step 0 read established three further gates no prior document had named: the engine's ruff backlog, the engine's strict mypy backlog, and a surface tsc --noEmit gap that next build structurally cannot see. None is in scope here, all three are real, and the engine's only CI job checks OpenAPI-doc freshness — not tests, not lint, not types.
The premise is corrected rather than quietly abandoned. Build list v0.20 recommended this cluster first on the ground that it "makes every future build's verification simpler and its gate stronger." It makes three of six gates readable. That is worth doing and it is not what was claimed.
Established, not assumed. The test mounts Stele's own mountable router and calls a route depending on require_fresh_session — a separate, stricter step-up slot the test never overrides. Its default token source reads only an Authorization: Bearer header; the test sets a cookie. Stele's own docstring says the slot deliberately does not depend on resolve_current_principal "so overriding one slot never silently changes the other's behavior" — which is Stele telling a host that stubbing auth means stubbing both.
It is a regression, not an always-broken test. The test was last touched 2026-06-20; Stele's step-up work landed 2026-07-07, seventeen days later, and changed that route's dependency.
And the production path is untouched by any of it. The engine never mounts Stele's router anywhere outside this one test. The live route is a separate native implementation depending on the engine's own cookie resolver.
The fix: override require_fresh_session in the test's own fixture, mirroring the existing resolve_current_principal override, confined entirely to that one test file.
> And a comment naming the failure mode, because this will happen again. A host stubbing Stele's auth must stub every auth slot the mounted routes use, and Stele adds slots on its own release cycle. The comment is the only thing standing between the next slot and a second silent regression.
Rejected: delivering a Bearer header instead. It would exercise the real slot, which sounds better and is not what this test is for — its own docstring says it verifies the router is mountable and consumable, not that authentication works.
The twelve unhandled rejections are test-double artifacts — a relative URL hit through a non-browser fetch, a mocked response body read twice, an incomplete mock. None is reachable in a browser under ordinary use.
The gap underneath them is real. fetchPlatformLevel catches ApiError and re-throws everything else; its sole caller fires an async function with no await and no .catch(). A failure fetch throws before any response exists — offline, DNS, CORS, abort — is a plain TypeError, not an ApiError, and goes unhandled in a browser exactly as it does under test.
The fix goes in usePlatformLevel's effect: catch the rejection. That closes the real gap and removes all twelve test rejections in one change, because a caught rejection is not an unhandled one whatever produced it.
> The constraint, and it decides the shape. The current code accidentally satisfies the room-state contract: an uncaught rejection crashes before resolved is ever set, so resolved stays false — which reads as we don't know, not as we checked and there is none. Any remedy that catches the failure and then sets resolved = true with level = null collapses those two into one and breaks the contract three rooms hold explicitly. The catch must preserve the unknown.
>
> And it must stop being silent. Today a real failure produces a spinner or an absent entry with nothing reported. The rejection is caught, the unknown is preserved, and the failure is surfaced — to the error path the surface already has, not swallowed.
Rejected: mocking the call in shared test setup. It silences twelve symptoms and leaves the browser gap exactly where it is.
Rejected: widening fetchPlatformLevel's catch to return null on anything. That is the collapse the contract forbids, at the one function every caller of that module trusts.
The rule's own named hazard is a cascade — state set in an effect that feeds back into that effect's dependencies. At the two sites read, it does not apply: ManifestationRoom and usePagedList set loading state synchronously at the top of an effect, and the state they set is not in the dependency array of the effect setting it. The result is one bounded extra render, not a cascade. They do it deliberately, and both say so in their own comments — it is the room-state contract's unloaded → loading transition.
The remedy: an inline exception at each deliberate site, carrying its reasoning. Not a global disable, and not eighteen rewrites.
> The verification that must come first. The Step 0 read confirmed the deliberate pattern at two sites. Eighteen files break the rule. The other sixteen are unverified, and some may be ordinary violations that should be fixed rather than excepted. > > [EXECUTING SESSION: for each of the eighteen, establish whether the state being set appears in the dependency array of the effect setting it. Where it does not, the site is the contract pattern and takes an exception. Where it does, it is a genuine cascade risk and is fixed. Report the split before writing either.] Blanket-excepting all eighteen because two were checked is the failure this project keeps correcting.
Lint reports seven distinct rules in two unrelated families. The hooks family is D-3's subject; no-unused-vars across ten files is dead imports and unused bindings, mostly in tests. A routine sweep, and omitting it means lint still does not pass — which is the entire point of the cluster.
The remaining singleton rules — react-hooks/refs, static-components, rules-of-hooks, globals, and the unused-disable directives — are read and reported at Step 0 with a fix or an exception each, per D-3's method. [EXECUTING SESSION: rules-of-hooks in particular is not a style rule. If it flags something real, halt and surface rather than excepting it.]
ruff, mypy and the tsc gap are out of scope and become their own items.
Not deferred out of caution — they are a different kind of work. Thousands of findings across dozens of rules, in a repository whose CI has never run either tool, is not a cleanup. It is a decision about what a gate should check, and it wants scoping before anyone fixes anything. Same for the surface's direct-tsc gap.
3.1 Engine — tests/test_stele_router_mount.py. D-1. One file. Nothing in src/.
3.2 Surface — src/hooks/usePlatformLevel.ts. D-2.
3.3 Surface — the eighteen hooks-rule sites, per D-3's verification-then-split.
3.4 Surface — the unused-variable sweep and the remaining singleton rules, per D-4.
ruff, mypy, the surface's direct-tsc errors. D-5.src/ change in the engine. D-1 is a test file.Per-step commits. Two repositories, two branches, two commit series. Never one commit spanning both. Check the current branch before the first commit in each.
Step 0 — pre-flight, baselines, and one split.
Verify both HEADs, both trees, the CR number. Record every baseline by name and count: the engine's failure set; the surface's failure set, unhandled-rejection set, exit code, lint error and warning counts by rule, and tsc --noEmit count. Re-confirm the findings' anchors.
Then settle: the eighteen-site split at D-3, and the singleton rules at D-4. Report both before writing anything.
Step 1 — engine, D-1. Verify the test passes and the engine's failure set is now empty.
Step 2 — surface, D-2. Verify the twelve rejections are gone and the exit code is zero.
Step 3 — surface, D-3, per Step 0's split.
Step 4 — surface, D-4.
Step 5 — tests. A test that fails if usePlatformLevel ever reports a failed read as a confirmed absence — the D-2 constraint has nothing else protecting it, and it is the same shape as the render current-marker and the shaping progress language. Assert specific conditions, never a bare exception.
CHECKPOINT A — report, then proceed. Every baseline set compared; the D-3 split with its reasoning; and an explicit statement of which commands now exit clean and which do not, so the change request's own claim in §1 is verified rather than asserted. No Operator confirmation. Halt and queue on: any new failure; rules-of-hooks flagging something real; any site in the eighteen that is a genuine cascade and cannot be fixed without changing the contract; any charter §6 anomaly.
Step 6 — implementation notes in each repository, carrying the split and the baseline comparison.
CHECKPOINT B — merge and tag, separately per repository. --no-ff in each; tag the engine stele-mount-test-fix-v0_1 and the surface clean-report-surface-v0_1. Push both. Authorized under R-2 including the pushes. Deployment is never autonomous (F-1).
pytest exits clean — the failure set is empty, not merely unchanged.npm run test exits zero, with no unhandled rejections.npm run lint reports zero errors and zero warnings.usePlatformLevel catches the rejection, **preserves unknown rather than reporting *none***, and surfaces the failure. A test enforces it.src/ change. D-1 is confined to one test file.ruff, mypy and tsc --noEmit are untouched and their counts are reported unchanged — this change request claims nothing about them.
CR-2026-166 — the clean-report cluster. Execution session.
CR: loomworks-record/change-requests/cr-2026-166-clean-report-cluster-v0_1.md
Confirm it exists, and that it is the highest version present — numeric sort.
Grounding, read before the CR:
inspection-briefs/loomworks-clean-report-cluster-step-0-findings-v0_1.md
Charter dunin7-standing-authorization-charter-v0_1 governs.
Read the CR in full. It is the authority on what changes, where and why; this
block repeats none of it. Where this block and the CR appear to differ, the CR
is right and you halt rather than choosing.
Section 2 — the construction decisions and their reasoning
Section 3 — what changes
Section 4 — what is out of scope
Section 5 — the step sequence and both checkpoints
Section 6 — the acceptance gate
TWO REPOSITORIES — the CR's header names both targets and both baselines.
Separate branches, separate commit series, separate merges and tags. Never one
commit spanning both.
Section 2's D-3 requires verifying each site before excepting it. Excepting all
of them because a sample was checked is the failure this project keeps
correcting. Report the split before writing either half.
Standing fences, true regardless of this CR:
playground_dev and playground_test are live databases and are not touched.
If the engine's suite needs a database, use a throwaway, dropped after.
Commit to a branch; check the current branch before the first commit in each.
Deployment is never yours.
Append the outcome to current-status/dunin7-status-brief at close.
DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-166 — B-33, B-34, B-42: the clean-report cluster — v0.1 — 2026-08-04 Three reports made readable, and three more gates named that nobody knew were there.