ID |
|
|---|---|
Status |
Spec |
Bucket |
architecture |
Priority |
2 |
Theme |
model-cleanup |
Created |
2026-08-27 |
Updated |
2026-08-27 |
Measure re-evaluation rather than naming, so a materialization cut set can be chosen on evidence
Every materialization registration in the fact store is argued from a measurement of one relation against the tree as it stood. There is no instrument that says what the register does as a set, so there is no way to propose a different set and no way to tell whether a registration still earns its place. R848 states that problem. This item builds the instrument it needs.
Two instruments already count namings: how many times H2 instantiates a rule when a reader is evaluated, H2 inlining a view wherever it is named and eliminating no common subexpression. That is one of the three mechanisms the register’s own reasons cite, and both instruments are blind to the other two. Naming them apart is the first thing this plan owes an implementer, because they are not the same quantity and only one of them is in the tree.
The two naming metrics, and which one this item is about
The shipped one is no.sikt.graphitron.roadmap.InlineMultiplicityCheck in roadmap-tool main
scope, tested by InlineMultiplicityCheckTest and bound by roadmap-tool/pom.xml to verify as the
report-inline-multiplicity execution, so it runs in every full build. It scans the authored DDL
textually, counts each relation’s references per CREATE VIEW body, multiplies down the tree, and
prints the fifteen heaviest relations. A materialized relation drops out by construction, its
canonical name being a table. It reports and does not gate, deliberately, because it
over-approximates: it counts textual references without knowing which arms a predicate prunes.
The uncommitted one is a probe written while filing R848 and never committed. It reads H2’s
normalized stored definitions out of a booted store rather than the authored text, sums over the 48
root readers rather than reporting per view, and can redirect each registered target to its _live
view to produce a no-materialization counterfactual, which is where 470 and 913,978 below come from.
They disagree, and the mechanism matters because it lands inside slice 1.
InlineMultiplicityCheck counts what an author typed;
MaterializeDependencies.relationsReadBy collects off the normalized definition and returns a
Set, discarding multiplicity along with position. So the multiplicities available to the new
instrument are H2’s after normalization, not the authored ones, and slice 1 has to recover
multiplicity as well as position. Neither basis is wrong. They answer different questions, and no
arithmetic reconciles their totals.
This item is about neither of them. It is about the weighted instrument that replaces the
uncommitted probe. InlineMultiplicityCheck is not touched by this item at any outcome, which the
acceptance section below states explicitly.
Vocabulary
A derived relation is a view in the fact store: a rule stated once in SQL, evaluated whenever a reader names it. A registration moves the canonical name onto a table refilled from the rule once per capture, so readers stop evaluating it. A root reader is a view no other view names, which is where a real read enters the derivation. Re-evaluation is the thing being counted throughout: how many times the engine actually executes a rule’s body during one read, which is not the same as how many times an author wrote its name.
What the naming metric says
Computed by the uncommitted probe over 107 views, 168 base tables, 20 registrations and 48 root readers. Both figures are total rule instantiations summed across every root reader, counting H2’s normalized references with multiplicity. They are not comparable with `InlineMultiplicityCheck’s per-view ranking, whose heaviest single view already exceeds the first figure here.
These figures are pinned to the tree they were taken on and are not restated per run. That tree declared 107 views; trunk declared 109 within a day, two having landed while this item sat in review. Swapping the census while leaving the totals would misattribute them, and re-taking the totals on every census move is work with no reader. Nothing downstream depends on their being current: the acceptance gate compares against magnitudes recorded in the DDL rather than against anything measured per run. The drift is also the item’s own thesis happening again while the item waits for it.
| Rule instantiations | |
|---|---|
As evaluated today, 20 targets standing as tables |
470 |
With no materialization at all |
913,978 |
The distribution is the finding rather than the ratio. A single family dominates: the five worst root readers are all in the write-payload chain and account for about 98% of the total. A greedy search, adding whichever registration cuts total expansion most, reaches 595 with twelve registrations against the 470 that twenty buy.
The finding that justifies building the real thing
The same probe was run two days apart, across a tree that gained exactly one registration in between, and the pair says something no per-relation measurement could.
intent_field_scope_table was registered by an increment whose subject was not materialization. On
the tree before it, intent_argument_scope_table was the single largest lever in the whole register:
greedy step one, taking total instantiations from 910,497 to 78,457. On the tree after, that role
belongs to intent_field_scope_table (913,978 to 77,209), and intent_argument_scope_table has
fallen to a marginal value of +17 and last place in the greedy order.
Registering the lower relation absorbed almost all of the upper one’s value. The two are near substitutes and the register holds both, each with its own reason arguing its own case, neither mentioning the other. Nobody did anything wrong and nobody could have noticed: the value of a registration is a function of which other registrations exist, and no per-relation measurement can see that.
This also shows why the marginal column is the wrong summary to act on. intent_field_scope_table
scores +9 marginally and is the largest lever in the register. Registrations are substitutes, so
dropping-one-at-a-time systematically understates every relation that has a near-twin.
Where a naming metric is blind, and why that disqualifies both of them
Three mechanisms put registrations in the register. A naming metric, on either parsing basis, models one. Nothing below turns on the difference between the two, which is why they are separated once above and treated together here.
| Mechanism | What re-evaluates the rule | Naming metric |
|---|---|---|
Breadth |
a rule named N times is expanded N times |
counted correctly |
Per-row |
a derived relation on the inner side of a join, or a correlated probe, is evaluated once per driving row |
counted as 1 |
Recursive |
a view named in a recursive term or its anchor is re-expanded per iteration |
counted as 1 |
Check the blindness against the register’s own timings. intent_mutation_write_destination scores
10 and its reason records 12983 milliseconds falling to 5.4. `+intent_field_reference_step_hop`
scores 36 and its reason records `+intent_node_id_decode` falling from about fifty seconds to about
thirteen. Those are the two registrations bought for per-row and recursive re-evaluation, and the
metric ranks them near the bottom.
So a naming metric is a map of one mechanism, useful for locating where breadth concentrates and
unusable for choosing a cut set. Committing the probe as-is, or reading a cut-set decision off the
shipped report, would repeat the error the store-performance skill already records twice: a count
that is real, and a reading of it as cost that is wrong. InlineMultiplicityCheck does not make that
error itself, its javadoc saying outright that it reports rather than gates because the metric
over-approximates; the error would be a reader’s.
What to build
A metric that weights each naming by its position, because position is what decides how many times the engine runs the body.
Parse each stored view definition with jOOQ’s parser and classify every relation reference as one of:
plain (evaluated once per naming), inner-side-of-join (once per driving row), correlated (once per
outer row), or recursive-term (once per iteration). MaterializeDependencies already parses stored
definitions this way and walks them, collecting table references off the query object model rather
than off text, so the walk and its normalization rules are established; what is new is retaining each
reference’s position instead of discarding it.
Weight the three re-evaluating positions by the driving side’s cardinality, which the store can count because it holds the rows. That makes the metric population-dependent, and that is correct rather than regrettable: the register’s own reasons record that a synthetic fixture twelve clusters wide understates a per-row probe by as much as it takes to turn four seconds into no termination at all. A metric that ignores population would inherit exactly that error.
Home: graphitron-model, test scope, beside the instruments that already live in
no.sikt.graphitron.model.test (UnregisteredRelation, RunawayRelation, FactStores). It is a
research instrument and nothing at runtime needs it. Promoting it to main scope, or to an MCP
surface, is a separate question and should not be settled here.
Why not extend InlineMultiplicityCheck instead, which is the module that already holds a static
metric over this DDL.
Not for want of dependencies, which is what an earlier draft of this plan claimed and got wrong.
roadmap-tool depends on graphitron-model at compile scope, so jOOQ is on its classpath; it
carries org.duckdb:duckdb_jdbc (named as evidence about that module’s dependencies and nothing
more: this instrument runs on H2 through jOOQ like everything else in graphitron-model, and nothing
in this item uses DuckDB or proposes a change of engine, the fact store’s own being out of scope
however much the register’s reasons blame its inlining); and it already opens a populated store on
every full build,
SchemaIdentifierDriftCheck and SchemaReferencePages both calling GraphitronModelStore.open()
with check-schema-identifiers bound to verify beside report-inline-multiplicity. Both inputs
this instrument needs are present there today. The "needing no database and no profiler" premise
belongs to InlineMultiplicityCheck and describes that check, not the module around it.
The grounds are the instrument’s collaborators and its cadence.
The walk being extended is MaterializeDependencies.relationsReadBy, graphitron-model main
scope. Extending it from another module means duplicating the walk or widening its API for a single
caller, and the H2 normalization rules it encodes are exactly the part that must not be re-derived
independently, the two existing metrics already disagreeing because their bases differ.
The counterfactual’s collaborators are test scope in that module. Scoring a candidate cut set
means evaluating the registered and unregistered shapes of a relation, which is what
UnregisteredRelation does, over a store from FactStores; both are no.sikt.graphitron.model.test,
republished as a test-jar so downstream modules build on that floor rather than opening a store of
their own. report-inline-multiplicity runs from roadmap-tool main scope through exec:java,
and main cannot reach test-scope instruments. Hosting the weighted metric beside it therefore means
reimplementing the swap in main scope, or putting it in roadmap-tool test scope where it is neither
a build step nor near anything it collaborates with.
Cadence decides what is left. Every roadmap-tool execution is bound to verify, so the module’s
shape is build steps. This instrument answers a question somebody is asking at the time they ask it,
and its store boot and cardinality counts are not costs to add to every build. A class can sit
unbound in that module, which is why this is the third ground rather than the first.
So the two coexist on purpose, with different jobs and different cadences: a DDL-only report that runs every build and catches an authored-multiplicity regression, and a store-dependent instrument run on demand when a cut set is in question. What would make the shipped one redundant is a weighted metric that runs without a store, which this plan does not propose and does not believe in, the population being the reason the register’s own reasons record a fixture understating a per-row probe by orders of magnitude.
Acceptance: the metric must reproduce a known ranking
This is the gate, and the item fails honestly rather than shipping a plausible number that nobody can check.
Most rows of meta_materialize carry a measured before-and-after in wall clock. Those timings were
taken on different trees and different schemas, so they are not comparable as figures. What survives
that incomparability is a coarse class, because the classes below sit orders of magnitude apart
and no cross-tree noise flips one into another.
Classify all twenty first, then score. The classes are fixed and written into this item before the metric is run, so the gate cannot be tuned to the answer it produces. Class on the absolute saving a reason records rather than on the ratio: a cut set is chosen to reduce total time, and a large ratio on a small base is not evidence of value.
| Class | What the reason records |
|---|---|
A |
a read or capture that did not terminate, or timed out |
B |
a saving of a second or more |
C |
a saving under a second |
U |
no timing at all, the reason arguing breadth only |
The ship condition is over all twenty, not over a triple: no registration may outrank one in a higher class. Class U is excluded from the comparison, having no evidence to compare against, and must be listed rather than quietly dropped.
This discriminates, and the earlier version did not. The probe fails it, on the same marginals this plan already reports:
-
intent_input_field_resolving_table(18, class C, its reason recording a walk from 39 milliseconds to 1) outranks `+intent_mutation_write_destination` (+10, class B, 12983 milliseconds to 5.4). A 38-millisecond saving placed above a thirteen-second one. -
intent_mutation_payload_refusal(+39, class A, its reason recording a capture that did not finish at all, twenty-three minutes of CPU with no output) ranks ninth of twenty, below three class-B registrations and below two class-U ones.
What the triple was, and what it now is. An earlier draft made the ship condition an ordering
over intent_mutation_write_destination, intent_field_reference_step_hop and
intent_argument_column_match, and claimed the probe got it backwards at 10 and +36 against +4.
That claim was simply wrong: those three marginals are in the demanded relative order, and the probe
passes that test. So does `+InlineMultiplicityCheck` on the obvious per-registration derivation, its
source-view subtree counts putting them at 28, 20 and 6. A ship condition passed by both metrics this
item exists to replace is not a gate. The triple survives below as an illustration of what the
blindness looks like, which is the job it can actually do: intent_field_reference_step_hop and
intent_mutation_write_destination, the two bought for recursive and per-row re-evaluation, rank
tenth and fifteenth of twenty by marginal (+36 and +10) despite their reasons recording among the
largest measured wins in the register. That is a statement about where they sit in the whole ranking,
which is the claim the gate now tests, rather than about their order among themselves, which was
never wrong.
What the negative branch deletes, stated exactly because the first draft of this plan left it to
implication. If the gate fails, the new weighted instrument and its tests are deleted, and the
uncommitted probe stays uncommitted. InlineMultiplicityCheck, its test, and the
report-inline-multiplicity build step are untouched: they are a build-bound reporting surface whose
retirement is a separate decision with a separate blast radius, and nothing this item measures bears
on whether that report is worth printing.
Where a negative result is recorded. Not in this file, which is deleted at Done. It goes in
roadmap/changelog.md, one of the three permanent roadmap artifacts, naming the item and what the
gate refused and why. A finding that a static count cannot rank these registrations is worth as much
to the next author as a working metric would have been, and it is the kind of result this tree loses
by default.
Slices
-
Position-and-multiplicity parse. Extend the definition walk to retain each reference’s position and its multiplicity,
MaterializeDependencies.relationsReadByreturning aSettoday and so discarding both. Pin it with cases over hand-written view bodies of each shape, so the classifier is tested against known answers before it is pointed at the schema. -
Weighting and the whole-register score. Cardinality from the store, a total per root reader, and a cut-set score for an arbitrary candidate set.
The regression test is *self-contained, not cross-tool*. On the same hand-written fixtures slice 1 pins, with every weight forced to one, the instrument must produce the instantiation counts derivable by hand from those bodies. This deliberately replaces the first draft's "reproduces the naming metric's numbers", which named nothing runnable: the shipped tool computes per-view subtree counts off authored text, the uncommitted probe computes root-reader totals off normalized definitions, and no arithmetic takes either to the other. Agreement with a second implementation on a different basis is not a property worth asserting; agreement with a hand-derived answer is. . *The validation gate.* Rank the twenty registrations, compare against the reasons' recorded magnitudes, and record the outcome either way, in `+roadmap/changelog.md+` when it is negative.
Slices 1 and 2 are worth nothing without 3 and should not land separately from it.
Risks
The classifier is the risk. H2’s stored definition is already normalized and may not preserve the
distinction between a join whose inner side is derived and one whose inner side is a base relation in
a way the parser exposes. If it does not, slice 1 stops and the item needs a different reading of
position, possibly from EXPLAIN plan shape rather than from the definition. Establish this in slice
1 before building anything on top of it.
Cardinality weighting can also mislead in the other direction, inflating a relation whose driving side is large but whose body is trivial. The gate is what catches that, which is another reason it cannot be deferred.
Relationship to R848
R848 asks which cut set the store should have. This item builds the only instrument that could
answer it on evidence, so R848 should not reach Spec before this one has produced a result. Stated
here rather than as a depends-on edge on R848, whose body is being actively worked by another
session.
Reviewer findings
Round 1 (2026-08-27, Spec → Ready, reviewer session session_014R3TSfjFfZQzoms4otDrVn)
Verdict: withhold, on one finding. The design holds up and the goal is well communicated: nothing
changes at the consumer surface when this lands, and the plan says so, but what it unblocks does,
every capture and every language-server or MCP store open paying the register’s evaluations, so an
instrument that can score a set rather than a relation is the thing standing between R848 and an
answer. The three-mechanism diagnosis is the right diagnosis, and the decision to weight a static
count rather than to reach for EXPLAIN ANALYZE scan counts is better founded than the plan claims:
the store-performance skill records that a scan count stops tracking cost exactly when a change
moves rows between a view and a table, "which is what every registration in the register does", so
the shipped scan-count instruments are the wrong primary here for a reason the tree already states.
Round 2 (2026-08-27, Spec → Ready, reviewer session session_014R3TSfjFfZQzoms4otDrVn)
Verdict: withhold, on one finding, in the one section Round 1’s asks produced.
Three of the four asks landed and landed well. The referent section separates the two metrics
correctly on every detail I checked: InlineMultiplicityCheck does print the fifteen heaviest, does
count authored text, does report rather than gate on an over-approximation premise its own javadoc
states, and MaterializeDependencies.relationsReadBy does return a Set that discards multiplicity
along with position, which slice 1 now requires be recovered. Slice 2’s regression test is now
runnable by the implementer who writes it, and the trade it names is the right one: agreement with a
hand-derived count over agreement with a second implementation on an incommensurable basis. The
negative branch says exactly what is deleted and what is not. Sending a negative result to
roadmap/changelog.md rather than to a file that Done deletes is right, and the changelog is one of
the three permanent roadmap artifacts, so it is a durable home rather than another transient one.
1. Every stated ground of the home argument is false about roadmap-tool. "Why not extend
InlineMultiplicityCheck instead" rests on two inputs being unavailable in that module: jOOQ’s
parser for position, and a populated store for cardinality. Both are there, and both are already
used at verify.
-
roadmap-tool/pom.xmldepends ongraphitron-modelat compile scope, so jOOQ is on the classpath. The dependency’s own comment says what it is for: "The fact store: render-schema-reference boots it from the DDL and reads the relation census, comments and meta rows back through the shared catalog reader". The module also depends onorg.duckdb:duckdb_jdbc. -
SchemaIdentifierDriftCheckandSchemaReferencePagesboth callGraphitronModelStore.open(). -
check-schema-identifiersis bound toverifybesidereport-inline-multiplicity. Runningmvnd -pl roadmap-tool verify -Plocal-dbprints "check-schema-identifiers: 51 pages and 4402 store prose values resolve against 275 relations in 13 families", so a populated store is opened in that module on every full build.
So nothing would be "added" to host a store-dependent instrument there, and the property the plan
says adding it would destroy is one the module does not have. The quoted premise "needing no database
and no profiler" belongs to InlineMultiplicityCheck, describing that check, and the plan reads it
as a property of the module.
This is a finding about the argument and not a request to move the instrument. I expect
graphitron-model is still right, on grounds the plan has available and does not use: the walk being
extended and the sibling test instruments both live there, this is on-demand research code that
should not sit on any verify path, and `roadmap-tool’s store boot serves build-time documentation
rendering and gating, which is a different job at a different cadence from scoring a candidate cut
set. But that is the author’s argument to write, and it lands somewhere the current one does not:
it argues from what the instrument is and where its collaborators live, rather than from a
dependency that is missing.
What would satisfy this: restate the home decision on grounds that survive the pom. If the true grounds change the answer, say that instead.
Non-blocking
"which both naming metrics get backwards", in the acceptance section, claims more than the tree
supports. The first draft said "the naming metric", meaning the probe, and the 10 and +36 marginals
support it. The shipped tool produces no per-registration ranking at all, and on the obvious way to
derive one, each registration's source view subtree count, it gets the demanded order right rather
than backwards: `+intent_mutation_write_destination_live` 28,
intent_field_reference_step_hop_live 20, intent_argument_column_match_live 6. Nothing in the gate
turns on the sentence.
Everything else checkable checked out. The census is exact: the DDL declares 107 CREATE VIEW and
168 CREATE TABLE statements, meta_materialize holds 20 rows with intent_field_scope_table the
twentieth, and 48 views are named by no other view. intent_field_scope_table was indeed registered
by an increment whose subject was not materialization (02ec43c, the condition membership fold).
Both cited timings are verbatim in the register: intent_mutation_write_destination records 12983
milliseconds falling to 5.4 and names its per-row re-evaluation, intent_field_reference_step_hop
records intent_node_id_decode falling from about fifty seconds to about thirteen and names its
recursive term, and intent_argument_column_match records the fifteen-to-six the plan contrasts
them against. Every symbol exists as named: MaterializeDependencies in graphitron-model main
scope parsing stored definitions with jOOQ’s parser and collecting off the query object model,
UnregisteredRelation, RunawayRelation and FactStores all in no.sikt.graphitron.model.test,
and every relation the plan names as a view or a table in the fact schema.
1. "The naming metric" has no runnable referent, and the one in the tree is not throwaway. The
plan opens by reporting the naming metric as "throwaway code". A metric answering to that
description is committed and build-bound:
no.sikt.graphitron.roadmap.InlineMultiplicityCheck in roadmap-tool main scope, tested by
InlineMultiplicityCheckTest, bound by roadmap-tool/pom.xml to the verify phase as the
report-inline-multiplicity execution, so it runs in every full build. Its javadoc states the same
mechanism in nearly the same words, "H2 inlines a view wherever it is named and eliminates no common
subexpression", it computes relation instantiations per read from the DDL alone with a materialized
relation exempt by construction, and its report line prints the census this plan quotes: "107 views
over 168 tables".
Three places in the plan turn on which metric is meant, and each resolves differently.
Slice 2’s regression test, "reproduces the naming metric’s numbers when every weight is forced to
one", names nothing an implementer can run. The figures behind 470 and 913,978 came from code that
is not in the tree, and the shipped tool computes a different quantity: per-view subtree
instantiations, printed as a top-15 ranking, with no root-reader total and no
no-materialization counterfactual. Summing its per-view counts over the 48 root readers does not
land on either figure under any reading I could construct. Per-naming counts give 3219 as evaluated
today and 5,359,571 with each registered target redirected to its _live view; restricting the sum
to the 28 root readers that are not themselves registered source views gives 2397 and 3,043,899;
deduplicating references within a body gives 1208 and 350,295, or 766 and 142,629 restricted the
same way. The shipped tool’s own heaviest single view is 899, already above the plan’s 470 total, so
the two are not the same quantity whatever the counterfactual. This is not a claim that the plan’s
figures are wrong. It is that slice 2 has to say which naming metric the extension must agree with,
and under what definition of the two totals, or the implementer picks one and the regression test
means whatever they picked.
The mechanism for why the two bases can legitimately disagree is worth stating, because it lands
inside slice 1. InlineMultiplicityCheck counts textual references in the authored DDL;
MaterializeDependencies.relationsReadBy collects off H2’s normalized stored definition and returns
a Set, so it discards multiplicity as well as position. Slice 1 must recover both, and the
multiplicities it recovers are H2’s after normalization rather than the ones an author typed.
The acceptance gate’s negative branch, "the naming metric is deleted rather than kept as a
nearly-right one", reads as discarding a scratch file. If it means the report-inline-multiplicity
step, that deletion removes a build-bound reporting surface and its test, which is a different
decision with a different blast radius and one the plan should take deliberately rather than by
implication.
And the home. The plan puts the new metric in graphitron-model test scope beside the existing
instruments and argues that placement against runtime scope and an MCP surface, but not against the
module that already holds a static metric over this same DDL. The case for graphitron-model looks
strong to me, since the store is where cardinality lives and MaterializeDependencies is the
established walk, and neither is available in roadmap-tool. But two static metrics over one DDL on
two parsing bases is the shape worth arguing on the record rather than arriving at silently, along
with what becomes of the shipped one once the weighted metric exists.
What would satisfy this: name the referent. Say whether the naming metric under discussion is
InlineMultiplicityCheck, the uncommitted probe, or both; say what slice 2’s regression test
compares against and how, given that the shipped tool’s basis and totals differ; say what the
negative branch deletes; and say why the new instrument sits in graphitron-model rather than
extending the tool that is already there.
Non-blocking
The plan’s Slices heading numbers three slices and the closing line says 1 and 2 are worth nothing
without 3, which is clear as written. Worth deciding at the same time as the finding above whether
slice 3’s negative outcome is recorded in this item’s body, in roadmap/changelog.md, or in the
register’s own prose, since a negative result that lands only in a roadmap item disappears when the
item does.
Round 3 (2026-08-27, Spec → Ready, reviewer session session_014R3TSfjFfZQzoms4otDrVn)
Verdict: withhold, on one finding in the acceptance gate. It is the only thing I have left, and the home argument Round 2 asked for is settled.
First, a correction I owe the record: the false premise Round 2 found was mine before it was the plan’s. Round 1’s closing aside said the store and the walk were "neither available in `roadmap-tool`", and the plan picked that up. Round 2’s finding was against my own error restated.
The rewritten home argument checks out on every claim. graphitron-model’s pom does republish the
compiled tests as a test-jar, its own comment naming the store harness under
`no/sikt/graphitron/model/test as what consumers want from it and "every module downstream builds
its own fixtures over that floor" as the reason. UnregisteredRelation does exactly what the plan
says it does: its javadoc opens "Reverses one materialization registration inside a live store" and
names its purpose as "What a case needs to ask what a registration costs: the two shapes of one
relation, in one process, with no DDL edit and no model rebuild", taking a store from FactStores.
roadmap-tool sets no classpathScope on any exec:java execution, so the default applies and its
main-scope steps cannot reach those instruments, and it declares no test-jar dependency that would
put them on any classpath of its own. The primary ground is the right one: the walk being extended is
main scope in graphitron-model, and its H2 normalization rules are the part that must not be
re-derived in a second place.
1. The gate’s ship condition is passed by the metric this item exists to replace, on the plan’s own
figures. The section states the discriminating test in its first paragraph, score every
registration and check the ranking against the magnitude ordering the reasons record, and then
narrows what "ships only if" attaches to: "Concretely it must rank
intent_mutation_write_destination and intent_field_reference_step_hop well above
intent_argument_column_match, which the probe’s marginals get backwards at +10 and +36 against +4."
Read as figures, +10 and +36 both sit above +4, which is the demanded order rather than backwards. The sign convention that makes them backwards is not stated anywhere in the plan, and the reading that keeps the rest of the plan consistent is the opposite one: a register whose leading lever takes 913,978 to 77,209 has marginals in the tens of thousands, which is what makes +10 and +36 "near the bottom" as the blindness section says. Under that convention the probe orders this triple correctly, and the concrete test is one the probe passes.
The paragraph immediately after says why that matters, and says it correctly against the other metric: "A metric blind to per-row and recursive re-evaluation can still order three particular relations correctly, and an ordering that survives by luck on the one triple anybody checked is not evidence the mechanism is modelled." That argument applies to the ship condition stated one paragraph earlier, because the ship condition is an ordering over that one triple. So the gate as written can be passed by a metric that models one of three mechanisms, which leaves the item’s stated purpose, failing honestly rather than shipping a plausible number nobody can check, resting on a check that does not discriminate.
I cannot resolve the sign question from the tree, the probe not being in it, and neither can the implementer.
What would satisfy this: make the all-twenty comparison the ship condition rather than the triple, and say what agreement counts, for instance that no registration whose reason records a small win may outrank one whose reason records an order-of-magnitude win. Keep the triple as the illustration it is good at being. The "backwards" claim then has no work left to do and can go, or stay with its convention stated.
Non-blocking
The census has already moved under the plan. Trunk now declares 109 views, not the 107 both figure sections state, base tables and registrations unchanged at 168 and 20, an increment having landed between revision 2 and this review. I have not corrected the number, because the probe’s 470 and 913,978 were computed against the 107-view tree and swapping the census while leaving the totals would misattribute them. Say the figures are pinned to the tree they were taken on and the drift stops mattering; the gate itself is unaffected, since the magnitudes it compares against are recorded in the DDL rather than measured per run. Two views in one day is also the item’s own thesis happening again while it waits.
The two prior rounds' non-blocking notes were both taken.