DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-162-render-flow-v0_1.md

DUNIN7-M4 — OPERATOR LAYER CHANGE REQUEST

CR-2026-162 — B-28 and B-7: the lost contribution and the Rendering flow — v0.1

Version. v0.1 Date. 2026-08-01 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 at main 7e81e30, and /Users/dunin7/loomworks-engine at main a6a3ed6two repositories, and §2 explains why. Baseline. Neither repository is green. Operator Layer: 610 passed, 0 failed, exit code 1, twelve unhandled fetchPlatformLevel rejections. Engine: 1 failed (test_stele_router_mount), the rest passing. The criterion is no new failure and no new unhandled rejection, against sets recorded at Step 0. Exit 0 is not reachable and is not the criterion. Build-list items. B-28, B-7. CR number. CR-2026-162. Highest confirmed is CR-2026-161. [EXECUTING SESSION: verify; advance if taken.] Grounding. inspection-briefs/loomworks-cr-c-step-0-findings-v0_1every anchor below comes from it. Status. Pre-execution.

What changed from the plan. Change request C was scoped as B-6, B-7 and B-28 together. B-6 is split out — see D-1. This CR is the small, surface-facing half, and it ships a user-visible fix and a user-visible capability without waiting for the larger room.


1. What this fixes

B-28 — contributed Markdown is refused and the work is lost. Two type declarations disagree with what the system actually emits. Two lines.

B-7, first half — there is no way to download a finished render. The engine has served download routes all along. The surface has never called them. A wire, not a build.

B-7, second half — when an older and a newer render sit side by side, nothing says which is current. The surface already receives three fields that bear on it and displays none.


2. Why this touches two repositories

It does not change the engine. The engine is read-only here, for one purpose: confirming the download routes' contracts before the surface calls them. /renders/{id}/download, /content and /files/ are established as existing by the Step 0 read; their exact parameters, response shapes and format-conversion behaviour must be read before an adapter is written against them.

No engine file is edited. No migration. No engine commit. If a route turns out to need changing, halt — that is an engine change request, not this one.


3. Construction decisions

D-1 — B-6, the Shaping room, is split into its own change request.

The Step 0 read establishes that Shaping's engine surface is not what Manifestation's was: it carries a confirm/retire lifecycle, executors, and asynchronous jobs. B-5 needed no engine work, and inheriting that as though the rooms were the same size is the error the findings warn against.

B-6 gets its own scoping and its own Step 0, covering the lifecycle, what the surface must show while a job is running, and whether the shape-type-to-render-type mapping is enforced or merely exposed — the second of the findings' two unread items.

Nothing in this CR is blocked by that split. B-28 and B-7 touch neither Shaping's room nor its adapter.

D-2 — B-28 is fixed at both declarations, and the routing question is opened separately. (Queue entry Q-11, taken.)

text/markdown is registered to the discovery-to-seed skill, which emits source_mode="discovery". The assertion schema declares Literal["text", "voice", "pdf", "image"] at schemas.py:2524 and again at :2806. discovery is emitted and not allowed; text is allowed and emitted by no skill.

Add discovery to both declarations. A fix reaching one leaves the other to fail identically — that is the shape of the defect and the reason it must be stated as two.

Reasoning, and the question deliberately not answered. The findings note that contributed Markdown currently routes through seed extraction, while that registration's own comment says the canonical path is elsewhere. Whether the routing is right is a real question and it is not this one. Making an allowed set match what the system actually emits is not a design commitment — it makes a declaration honest about current behaviour, and if the routing moves later the literal moves with it. The routing question becomes its own build-list item. Deciding it here is the scope absorption C-3 forbids.

What this does not require. Nothing is lost and nothing needs recovering. The assertion is written at contributions.py:268, the response is built after it, and get_db_session rolls back on any exception — so the durable state is as if nothing happened, and the uploaded file row survives, meaning the same file_id re-contributes once the mismatch is fixed. No migration, no backfill, no data repair. The loss is of the person's work, not of their bytes.

D-3 — the currency half is built surface-first, with no engine change. (Queue entry Q-12, taken.)

Renders carry no supersession marker, where Manifestations do. That is a real asymmetry and it is not necessarily this CR's problem. The surface already receives state, created_at and display_number on every render and displays none of them.

Display what is already sent. Then judge whether a marker is still missing.

Reasoning. Building an engine field before establishing that the surface needs it is the wrong order, and the Manifestation precedent is the direct evidence: its staleness marker looked like a build and turned out to be a wire. If, after this CR, the Operator still cannot tell which render is current, the engine marker becomes its own item — informed by what the display revealed rather than by a guess made before it existed.

The honest counter, recorded. Three fields the Operator can interpret is not the same as one field that states the answer. A derived judgement can be wrong in ways a marker cannot. If Step 0 finds that state and display_number cannot distinguish current from superseded — for instance if two renders can hold the same state with no ordering between them — halt and report, because then the surface genuinely cannot say it, and saying it anyway is face 2.

D-4 — the Download control appears only where a download is possible.

RenderingRoom carries a comment recording that no Download button was built because there was no route to call. There is one. The seed's only-show-what-is-available constraint forbids a disabled or greyed control — so the button appears when the render is downloadable and is absent otherwise, never present-and-inert.

And it obeys the room state contract. A download control rendered from a failed read would imply the render exists when no read established that. It appears on populated only.


4. In scope

4.1 B-28 — src/loomworks/api/schemas.py, engine repository. [This is the one exception to §2's read-only engine, and it is two lines.] Add discovery to the source_mode literal at :2524 and at :2806. [EXECUTING SESSION: re-confirm both anchors and sweep for any third declaration of the same literal. A third would fail identically and the findings established two by reading, not by exhaustive sweep.]

> Correction to §2, made explicit. §2 says no engine file is edited. That is wrong as written and this is the exception — B-28's fix is in the engine's schema module. What §2 protects is that no engine behaviour, route or migration changes. Two literals gain a member that the system already emits. A separate engine commit, separate from the surface work.

4.2 The render adapter — a new file in the Operator Layer's lib/api/. The wall does not constrain its name: the Step 0 read establishes that rendering is not a forbidden term and the room may be named for itself throughout. Add it to WIRE_BOUNDARY_FILES per the existing convention.

4.3 RenderingRoom.tsx — the Download control per D-4, and the currency display per D-3.

4.4 The strings for both, asserting the surface where no read stands behind them, per the governing rule.


5. Out of scope


6. Order of operations

Per-step commits. Two repositories, so two commit series — never one commit spanning both. Check the current branch before the first commit in each.

Step 0 — pre-flight and two determinations. Verify both HEADs, both tree states, the CR number. Record both baseline sets — the engine's failure set, and the Operator Layer's failure set and unhandled-rejection set and exit code.

Then settle, by reading:

  1. The download routes' contracts — parameters, response shapes, content types, and the format-conversion behaviour the findings mention. If any route needs changing, halt.
  2. Can state, created_at and display_number distinguish a current render from a superseded one? Per D-3's counter. If they cannot, halt and report rather than displaying a judgement the data does not support.

Report both before proceeding. Commit: CR-2026-162 step 0: pre-flight and two determinations

Step 1 — B-28, engine repository, two literals. Suite against the engine baseline. Commit (engine): CR-2026-162 step 1: allow the discovery source mode

Step 2 — the render adapter, plus its WIRE_BOUNDARY_FILES entry. Commit (OL): CR-2026-162 step 2: render download adapter

Step 3 — the Download control, per D-4. Commit (OL): CR-2026-162 step 3: download control on populated renders

Step 4 — the currency display, per D-3 and Step 0's determination. Commit (OL): CR-2026-162 step 4: render currency display

Step 5 — tests. A test that a discovery contribution is accepted end to end; a test that the Download control is absent on failed and empty and present on populated; a test of the currency display. Assert specific conditions, never a bare Exception. Commits: per repository.

CHECKPOINT A — report, then proceed. Both baseline comparisons; the tests; a live pass on the dev server showing a Markdown contribution accepted, a render downloaded, and two renders side by side with the current one distinguishable. No Operator confirmation. Halt and queue on: any new failure or rejection; either determination coming back ambiguous; any charter §6 anomaly.

Step 6 — implementation notes, in both repositories, recording both determinations.

CHECKPOINT B — merge and tag, separately per repository. --no-ff in each; tag the Operator Layer render-flow-v0_1 and the engine discovery-source-mode-v0_1. Push both. Authorized under R-2 including the pushes. Deployment is never autonomous (F-1).


7. Acceptance gate

  1. No new failure in either repository, and no new unhandled rejection in the Operator Layer, against the Step 0 baseline sets.
  2. Both source_mode literal declarations accept discovery, and a sweep finds no third.
  3. A Markdown contribution completes end to end and is retrievable.
  4. No engine route, migration or behaviour changed — the only engine edit is the two literals.
  5. The Download control appears on populated only, and never as a disabled or inert control.
  6. A download of a finished render succeeds through the engine's existing route.
  7. The currency display uses only fields the engine already sends, and states nothing the data cannot support.
  8. The new adapter is in WIRE_BOUNDARY_FILES; the vocabulary wall passes with no other new exemption.
  9. No string on either room asserts the record without a completed read behind it.
  10. No Shaping room work — B-6 is split.
  11. Implementation notes in both repositories carry both determinations.
  12. The status brief is appended, per charter §7.

8. Claude Code kickoff

This block names the entry point and the fences. It does not restate §6's steps.


CR-2026-162 — B-28 and B-7. Execution session.

CR: loomworks-record/change-requests/cr-2026-162-render-flow-v0_1.md
Confirm it exists, and that it is the highest version present — numeric sort.

Grounding, read before the CR:
  inspection-briefs/loomworks-cr-c-step-0-findings-v0_1.md

Charter dunin7-standing-authorization-charter-v0_1 governs.
Section 6 is the step sequence; follow it there.

TWO REPOSITORIES. /Users/dunin7/loomworks at main 7e81e30 and
/Users/dunin7/loomworks-engine at main a6a3ed6. Separate branches, separate
commit series, separate merges and tags. Never one commit spanning both.

The engine change is TWO LITERALS in schemas.py and nothing else. No route,
no migration, no behaviour. If anything else in the engine needs to change,
halt — that is a different change request.

playground_dev is the live production database and is not touched.

NEITHER BASELINE IS GREEN. The OL exits 1 on twelve fetchPlatformLevel
rejections; the engine has one long-standing failure. Record both baseline sets
at Step 0. "No new failures and no new rejections" is the criterion. Exit 0 is
not reachable.

Step 0 settles two determinations and reports both before any code changes.
The second may legitimately come back as "these fields cannot distinguish
current from superseded" — if so, halt. Do not display a judgement the data
does not support.

Commit to a branch in each repository. Check the current branch before the
first commit in each.

Steps 0 through 5 auto. CHECKPOINT A reports and proceeds on a clean report.
Step 6 auto. CHECKPOINT B merges and tags each repository separately.
Deployment is never yours.

Append the outcome to current-status/dunin7-status-brief at close.

DUNIN7 — Done In Seven LLC — Miami, Florida CR-2026-162 — B-28 and B-7: the lost contribution and the Rendering flow — v0.1 — 2026-08-01 Two literals, one wire, and three fields already arriving unused.