Flow & Cut view (Days 2-3), and the /maxflow trace a bad merge had reverted - #65
Merged
Conversation
#36 shipped `/maxflow?trace=true` in ba6f32d, but #38's ladder work was developed from a pre-#36 base and overwrote api.py wholesale; the merge at 094a1aa then took that side. The whole commit went with it -- the endpoint's `trace` param and its Pydantic models, throughput.TraceStep.flows, the CLI's print-time filters, and three test files' worth of coverage -- leaving a closed ticket with none of its acceptance criteria holding on main. Restored by cherry-picking ba6f32d and reconciling api.py and the two test files against #38, which landed after it. The ladder section and the trace section are independent and both stand; nothing of #38 is given up. The Flow & Cut view (#42) reads every number it draws out of this payload, so it could not be built until the endpoint was back.
Edmonds-Karp stepped one augmentation at a time over the drawn network, with Day 3's min cut arriving as a layer over the completed flow. The two days share a screen because the min cut is a certificate OF the flow just computed, and re-rendering it fresh severs the argument it is making. Beat 0 is the pristine network and each augmentation is two beats, path-and- bottleneck then residual update, so `->` always means "and here is what that does". The coarse unit is the augmentation -- three of them on the textbook set -- because that is what the instructor says out loud; the pristine beat and the cut fold into the first and last units rather than taking units of their own, which keeps the digit keys naming what the board names. The uniform render rule is residuals from step k-1 and path annotation from step k, which is what the server's prepended step 0 exists for. The reverse arc gets its own geometry. An arc traversed against its lane's own direction is promoted in place at offset 22, dash 22 18, stroke 4, hollow head, clipped to the node rims by CIRCLE INTERSECTION rather than the usual trim -- an offset chord is a different line, and trimming it by the centre-line rule leaves one end floating clear of the rim. Promotion REPLACES: two arcs at 11 and 22 merge into one heavy mark that says neither thing. The lane itself is not path-marked while promoted, so "here it uses a residual arc, which is not a lane" is a sentence the screen agrees with. The promotion turned out to need no new dash token. It is the residual arc moved and re-headed, not re-patterned, so it inherits a pattern the stroke floors already certify -- which retires the `24 15 @ 11` the token block was holding a note against, a value the duty rule could not have certified. Three non-colour channels and no hue: colour was never available, path-vs-lane being dE 66 normally and 3.1 under achromatopsia. The cut draws as a partition -- S-side and T-side washes behind the nodes, solid rather than translucent because the scaffold tier owns opacity alone -- with its certifying lanes as `.in-cut` row states on the existing ledger and cut capacity as a headline number. Cut capacity and throughput come off ONE /maxflow response, since two numbers that must be equal by max-flow min-cut and are fetched by two routes are a contradiction waiting for a projector. No solver arithmetic in JavaScript. Per-edge flows and both residual lists are read off the trace and never derived; `cap - flow` is banned by a test rather than by agreement. The whole trace is fetched before the first keypress, so stepping is an index change and never awaits anything. Also here: the beat timeline slot, one clickable row per beat, and the direction glyph on `.on-path` rows -- stylesheet `content` scoped to on-path AND against together, so a forward traversal stays unmarked and no second element has to exist for it. Not done, and deliberately: the ticket's "default dataset: the textbook network" is left to #45, which owns dataset cycling. Coupling the dataset to the view would give a view a code path of its own, which views.js rules out precisely so a view cannot drift from what the same layers show when switched on by hand.
#42) Standards axis: - The hollow head's outline weight was written into the marker's markup, which made it the one appearance value a projector retune could not reach. It is `--arrowhead-hollow-w` now, styled from a rule like every other weight. - `directedLookup`/`laneFor` and `frozen.directed` had been left standing beside the residual lookup that supersedes them, so every dataset built two directed-pair maps and never read one. Folded into a single lookup keyed on every ordered pair a solver can hand back -- which is what the second map was for -- and the dead export is gone. - `render.js` reimplemented its own `shade()` helper for the terminals rather than calling it; the side predicate is a parameter now. - `state.js` documented its per-view memory as `{beat, layers}` where it stores `{beat, overrides}`, contradicting the paragraph underneath it. Also `layers()` shadowed `state.layers` and read as a noun -- `setLayers`. - `SHADE_R` was exported through `geometry` and consumed by nobody. Spec axis: - Promotion suppressed BOTH of a lane's plain residual arcs where the rule suppresses one. The promoted mark is the counter arc moved from 11 to 22, so the counter arc is what it replaces; the along arc is a different arc on the other side, 33 units clear, carrying a residual the layer is on to show. Suppressing it took a real number off the board to fix a collision it was never part of. - The bottleneck was written to state and read by nothing, so beat *a* was "path &" with the "bottleneck" half reaching the screen only as timeline text -- while the path layer's own hint promised it. It is a headline scalar now, beside Flow, and the pair is the sentence: Flow 0, Push 3, then Flow 3. - Dropped the A/B pair this view had claimed. It is one slot for the whole page and its subject is two states never simultaneously visible; an augmentation's "after" arrives one keypress later and is then simply on screen. - Dropped the `used_reverse` loop. Every arc it names is an arc of the same step's path, so it heated nothing already heated -- and what drives the promotion is the drawn lane's direction, a fact about the picture rather than about the solver's bookkeeping. The test's field list says so now. - `bindKeys()` ran before the trace was awaited, leaving the arrows answerable during the fetch and declining on the fallback spine. The keyboard goes live last, once a complete spine is installed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #42.
The Flow & Cut view (#42)
Edmonds-Karp stepped one augmentation at a time over the drawn network, with Day 3's min cut arriving as a layer over the completed flow. The two days share a screen because the min cut is a certificate of the flow just computed, and re-rendering it fresh severs the argument it is making.
Beat 0 is the pristine network and each augmentation is two beats, path-and-bottleneck then residual update. The coarse unit is the augmentation — three of them on the textbook set — because that is what the instructor says out loud; the pristine beat and the cut fold into the first and last units rather than taking units of their own, so the digit keys name what the board names.
The reverse arc gets its own geometry: an arc traversed against its lane's own direction is promoted in place at offset 22, dash
22 18, stroke 4, hollow head, clipped to the node rims by circle intersection rather than the usual trim — an offset chord is a different line, and trimming it by the centre-line rule leaves one end floating clear of the rim. Promotion replaces the same-side arc: the pair at 11 and 22 would merge into one heavy mark that says neither thing. The lane itself is not path-marked while promoted, so "here it uses a residual arc, which is not a lane" is a sentence the screen agrees with.The promotion needed no new dash token. It is the residual arc moved and re-headed, not re-patterned, so it inherits a pattern the stroke floors already certify — which retires the
24 15 @ 11the token block was holding a note against, a value the duty rule could not have certified.While building this I found
/maxflow?trace=truemissing frommain. #36 shipped it asba6f32d, but #38's ladder work was developed from a pre-#36 base and overwroteapi.pywholesale; the merge at094a1aa— labelled "keep main's trace implementation" — then took that side. The whole commit went with it acrossapi.py,cli.py,throughput.py(TraceStep.flows) and three test files, leaving a closed ticket with none of its acceptance criteria holding.The trap worth knowing:
git merge-base --is-ancestor ba6f32d HEADstill answered YES, so ancestry did not reveal it — only reading the file did. Other closed backend tickets from that window may be worth a look.Re-landed here by cherry-pick, reconciled against #38. Nothing of the ladder is given up.
Verification
356 tests pass. The frontend is verified from Python or not at all, so the new rules are pinned as assertions over the asset text in
tests/test_web_assets.py: the two named offsets, the promoted arc spending no hue, the hollow head, circle-intersection clipping, the partition shading, the glyph scoped to on-path and against, and a standing ban oncap - flowanywhere in the JS.Beyond the suite, the real modules were run against the real payload once by hand: 8 beats / 3 units on the textbook set, the promotion firing on the B→A traversal,
cut == throughput == 7, the partition splitting s/A/B/C from D/t, and every beat identical whether stepped to or jumped to cold.Not done, deliberately
The ticket's "default dataset: the textbook network" is left to #45, which owns dataset cycling. Coupling the dataset to the view would give a view a code path of its own, which
views.jsrules out precisely so a view cannot drift from what the same layers show when switched on by hand.