2026-09-06 04:20Z (frank-coordinator) — measured, and the blast radius is wider than the gate
Re-measured rather than re-quoted, because the seat that filed this is a reader and a reader has no staleness signal:
git merge-base --is-ancestor 8374118ec 8844c8c42 -> false
8374118ec (frankZ, 2026-09-05 23:15, "export pyvar_is_inttag/pyvar_is_objtag — two lib/rtl units
call them and could not compile") is not an ancestor of pin v404 (8844c8c42, 2026-09-05
20:17). The fix postdates the pin by three hours. Nothing at HEAD is broken and nothing built from
HEAD reproduces it — the pin is behind the source it has to build, and only make pin moves that.
AND IT IS IN THE FULL TIER, NOT JUST THE QUICK GATE. Track T's newest full-tier report,
20260906T040852Z-b77ac29-seven.md, verdict RED, names it:
lib-test#src:tools/crtl_reachability.py
pascal26:159: error: undefined variable (pyvar_is_objtag) lib/rtl/mimic_string.pas
pascal26:483: error: undefined variable (pyvar_is_inttag) lib/rtl/mimic_urllib_request
pascal26:573/666: error: undefined variable (pyvar_is_objtag)
So this is not a local instrument complaining. It is the number the fleet reads.
THIS IS EVIDENCE FOR AN OPEN DECISION: [[decide-pair-the-pin-with-the-lib-rtl-it-is-coherent-with]]
(U, prio 55, owner: user) now carries both casualties. A cadence argument in the abstract is
weak; one carrying two dated casualties in 48 hours is not.
SECOND INSTANCE OF A NAMED CLASS. feature-pascal-corpus-expansion records the first:
property Current: T read GetCurrent; on IEnumerator<T> was omitted from lib/rtl/classes.pas
for a month because the pin rejected a property in an interface, and the parser fix sat in done/
doing nothing for that corpus until pin v404 carried it. "Fixed at HEAD, inert until pinned" —
any compiler fix a $(PXX_STABLE) consumer needs is CLOSED while still unusable there, and the
ticket folder gives the wrong answer while the pin gives the right one. Two instances in two days
makes it a class rather than an anecdote: before closing a compiler fix that a lib/** file
depends on, check whether a pin carries it, and say so in the resolution.
Still not dispatchable. make pin is owner-only, it is on the owner's list, and no agent may run
it or ask a peer to.
The measurement
tools/gate.sh quick, 2026-09-06, on a tree at bb28cd97c and again after the
rebase, reported by frankD while landing an unrelated Track P fix:
gate: RED — 16 rows pass, 1 FAIL
FAIL pinned builds live lib/rtl
lib/rtl/mimic_string.pas undefined variable (pyvar_is_objtag)
lib/rtl/mimic_urllib_request.pas undefined variable (pyvar_is_inttag)
The same canary reports that a compiler built from THIS TREE compiles all 54 units cleanly. So the tree is fine and the pin is behind it.
Why it is nobody's fix but the owner's
pyvar_is_objtag / pyvar_is_inttag are builtins that landed in compiler/**
and were then used from lib/rtl. The pinned binary predates them, so it cannot
see them. lib/rtl is a compiler build input, which is why this row exists
at all.
The canary's own remedy is a pin. make pin is owner-only (CLAUDE.md, Asking
the owner is the expensive path — irreversible or outward-facing acts). No agent
may run it, no agent may ask a peer to run it, and there is no code change that
clears the row.
This is NOT DISPATCHABLE — do not claim it. There is nothing to implement. What it needs is the owner, and what this ticket provides is the record.
What it costs while it stands
- Every
$(PXX_STABLE)consumer. Tracks B and E build with the pinned compiler by rule and never rebuild one, so they are building against a compiler that cannot compile the RTL their programs link. - Every
gate.sh quickrun in every lane is RED, for a reason that has nothing to do with the diff under test. A standing red that everyone learns to step over is a red that will hide the next real one — and the correct reading ("not mine, the canary says so") has to be re-derived by every session that sees it. - CLAUDE.md's precedent: a red is a reason to pin SOONER, not later. The last time reds were treated as a reason to withhold a pin it held for 19 days, and v398 shipped a compiler that could not build C for i386 or arm32.
What it is NOT
Not frankD's bb7b59911 — zero lib/ files in that diff, the pinned binary
predates the edits, and the FPC seed canary passed on the same run. Any session
seeing this row should check its own diff for lib/ and then stop looking.
Resolve this by recording the pin that cleared it, not by a code change. If a later pin lands for other reasons and the row goes green, close it and say which pin did it — the interesting fact is the interval, not the fix.
DATED, 2026-09-06 (frankB found the sha; frank-coordinator verified the interval)
The red has an origin commit and an exact interval, so the owner's decision needs no archaeology:
pin v404 8844c8c42 2026-09-05T20:17:45 binary sha256 fe1e9c37d322
builtins 8374118ec 2026-09-05T23:15:46 fix(N): export pyvar_is_inttag/pyvar_is_objtag
-- "two lib/rtl units call them and could not compile"
8374118ec is NOT an ancestor of the pin (merge-base --is-ancestor, checked) — it
landed 2h58m after it. It adds both builtins to compiler/builtin/pylib.pas, and
its own commit message says why: two lib/rtl units already called them. So the
sequence is the documented one, with names on it — the RTL was written against a
compiler feature, the feature landed, and the pin has never carried it.
83 commits have touched compiler/ since that pin.
SEQUENCING, which is this seat's call — the pin itself is not
- Nothing is blocked right now. Checked at 2026-09-06: no checkout on the box has
uncommitted work under
lib/orexamples/, so no Track B or E session is currently building against the stale pin. The cost today is a standing RED in every lane's gate, not a stopped session. - It is still a reason to pin SOONER. CLAUDE.md's own precedent: a red is a reason to pin sooner, not later — the last time reds were read as grounds to withhold, the pin held 19 days and v398 shipped a compiler that could not build C for i386 or arm32.
- No agent may run
make pin, ask a peer to run it, or ask two peers the same question about it. This ticket is the record; the act is the owner's.
Read for a session seeing this row RED: check your diff for lib/ and for a new
builtin. If it has neither, the row is this ticket and not your change — six sessions
have now confirmed identical output.
2026-09-10 (frankuser) — third instance, and a MOVE is worse than an ADD
Recurred at pin v407 (51901941e, tree 04559b9d, 2026-09-06).
CORRECTED 2026-09-10 — the tier is 7 red rows, 5 of them this cause, 2
unrelated. The first figures here said four rows and one cause; both came from
a [:4] slice in a throwaway print, reported as the tier's state (frank-seven's
own catch). The five that went NEW-RED in a single run at
2026-09-09T16:32:24Z, the first full tier after 0ffe185bb at 15:57Z:
lib-test#src:test/lib_synapse.pas
lib-test#src:test/lib_synapse_ssl.pas
lib-test#src:test/lib_synapse_transitive_unit.pas
lib-test#src:tools/crtl_reachability.py
test-fpjson#src:tools/install_lib_candidates.sh
The two a pin will NOT clear, and they matter because otherwise a post-pin
tier showing red reads as the pin having failed:
size-canary#src:tools/size_canary.py (still-red since 2026-08-30) and
test-sqlite-threads-aarch64#src:tools/compiler_srchash.sh (2026-09-09 11:49Z —
four hours BEFORE 0ffe185bb, so it cannot be this cause).
Attribution strength differs across the five and is stated rather than rounded:
the three synapse rows were reproduced by hand against the pinned binary;
crtl_reachability.py carries the same identifiers as the 2026-09-06 instance;
install_lib_candidates.sh is attributed on co-occurrence in the same run
only, which is weaker.
The box was GREEN on most full tiers through 09-08 and has been continuously RED since 09-09 06:23 — a two-day-old break, not a long degradation, which makes the pin more urgent rather than less.
Cause is 0ffe185bb, and it is the documented shape with one difference that
makes it sharper. Previous instances ADDED a builtin and used it from lib/rtl;
this one MOVED six names out of lib/rtl into compiler/builtin/builtin.pas
— AllocMem, DynArraySize, SetString, sLineBreak, UTF8Decode,
UTF8Encode — deliberately, and for a good reason stated in the commit: "two
homes for one routine is the defect class rather than a mitigation of it", and
FPC's own layout agrees. An ADD leaves the old path working for a stale
compiler. A MOVE removes it, so a $(PXX_STABLE) consumer loses the name
outright.
Isolated from the corpus, so this needs no synapse tree to reproduce — five
lines, no uses clause:
program setstring_probe;
var s: AnsiString; buf: array[0..3] of Char;
begin
buf[0] := 'a'; buf[1] := 'b'; buf[2] := 'c'; buf[3] := #0;
SetString(s, @buf[0], 3);
WriteLn(s);
end.
HEAD-built compiler (61f8a78f8aae, tree 2d35cdbfc): compiles, runs, prints
abc. Pinned v407: error: undefined variable (SetString). A probe whose right
answer differs from its failure answer in both directions, and it does not touch
external/synapse, which is absent on plexus and would have skipped.
The part worth keeping: the author's own correctness check was HEAD-shaped.
0ffe185bb says "any uses clause already pulls the builtin unit, so existing
code is unaffected" — true, and true only of a compiler built from that tree.
The commit was green by every measure its author had. I first wrote that this
was because no per-fix gate compiles anything with $(PXX_STABLE). That is
false, and the truth is more useful — see the section below.
Resolution is unchanged and is still not dispatchable: only a pin clears it,
and the reds clear as a CONSEQUENCE of pinning rather than needing a waiver —
which is why this must not be routed through pin-allowlist.tsv. An allowlist
entry would need a ticket and a lifecycle for a condition that self-resolves the
moment the pin moves. Escalated to the owner 2026-09-10 as one act
(make stabilize-fast && make pin), with the note that the four reds GRADE and
the fixedpoint GATES, so they cannot block the pin that fixes them.
Not promoted to CLAUDE.md. The rule it would strengthen is already there — "a fix is INERT UNTIL PINNED", with two dated casualties — and this is a third instance in the SAME subsystem, so it meets the merit test and not the second-independent-subsystem test. The move-versus-add distinction is banked here instead.
2026-09-10 — the guard EXISTS, and it is aimed one step short of the defect
Correcting my own claim above. tools/gate.sh has pinned_rtl_canary() (:327),
stepped as pinned builds live lib/rtl (:519) — and it is invoked before
the case "$MODE" block, with its own comment saying why: "Before the case, so
it covers quick, lib and full alike from ONE place. The three-branch version of
this was the first draft; a check that has to be remembered in each new mode is
the check that will be missing from the next one." So gate.sh quick runs it,
and the gate even auto-locates this very ticket by grepping for the step's name
(:439-444). The machinery is mature and correctly placed. The earlier sentence
here was wrong and would have sent someone to build a guard that exists.
Why it did not catch 0ffe185bb, measured: the canary's population is
lib/rtl's own units, and no lib/rtl unit USES any of the six moved names.
| name | lib/rtl units referencing it in code |
|---|---|
| SetString | 0 — the only hit, lib/rtl/sysutils.pas:1176, is a COMMENT |
| AllocMem, DynArraySize, sLineBreak, UTF8Decode | 0 in code, same shape |
| UTF8Encode | 0 |
So the pinned compiler still builds lib/rtl cleanly, the canary is honestly
GREEN, and the breakage lands entirely in code outside lib/rtl that
consumes its surface — external/synapse/synautil.pas via
$(PXX_STABLE) --mimic-fpc -Fulib/rtl.
The hole stated once: the guard asks can the pin build lib/rtl? The
defect class is can the pin build code that DEPENDS on lib/rtl's surface?
Removing a name from lib/rtl is structurally invisible to a guard whose
population is lib/rtl itself, because the consumers are elsewhere by
definition. The guard is not weak and it is not misplaced; it is scoped to the
wrong population for this one class, and a MOVE is the only edit that exercises
the difference — which is why two ADDs were caught and this did not need to be.
Fix, and it is small: test/test_uses_sysutils.pas is already the canary's
fixture and is already outside lib/rtl. Extend it to reference the names that
have moved into compiler/builtin, so the canary's population includes a
consumer. The five-line setstring_probe above is the proven shape: no uses
clause needed, no corpus, and its right answer differs from its failure answer.
DELIBERATELY NOT LANDED YET, and the reason is sequencing rather than doubt.
Arming it today makes pinned builds live lib/rtl RED in every session's
gate.sh quick until the pin moves — correct, and badly timed: the owner has
been asked to pin, gate.sh quick is REQUIRED before a pin, and a fresh red
reading "pinned cannot build this" is exactly the shape that makes someone
hesitate at the moment they should not. The fixedpoint gates and this would
grade, but that is a distinction a red does not announce. Land it after the
pin, when it is green, and it then catches the NEXT instance instead of
arguing with the current one.
2026-09-11 (frankS) — still live, new names, and a SECOND shape of the same cliff that this row does NOT catch
The identifiers have turned over twice; the row has not cleared. Today's
instance is mimic_queue :: unknown type: TPyDeque — 1 of 58 root units under
tools/gate.sh quick, and the same failure heads
lib-test#src:tools/crtl_reachability.py in seven's newest full tier
(a45908bcf68e), together with mimic_threading :: __pxxclone (thread creation) requires --threadsafe. TPyDeque landed in compiler/builtin/pylib.pas at
1eb448030, whose own commit message says in as many words that it lands red on
this row, that make pin is owner-only, and that turning it green by putting
mimic_queue on TPyList would be the compiler-appeasement workaround CLAUDE.md
refuses. So that half is knowing, documented and correctly parked here.
Worth adding to this ticket because it is the same cliff and this row cannot
see it: the canary compiles the 58 ROOT UNITS under lib/**. Four tstate rows
were red for two days on the mirror-image shape — a name that LEFT lib/rtl for
compiler/builtin/, which a $(PXX_STABLE) build then cannot find in either
place, because the pin's frozen builtin/ predates the move. Nothing under
lib/** referenced it, so all 58 root units compiled and this row stayed green;
the failures were in external/synapse/synautil.pas (undefined variable (SetString)) and testjsondata.pp (UTF8Encode, sLineBreak) — corpora
outside the canary's population, and absent altogether on plexus, so the local
make lib-test skipped them and went green as well.
Two directions, one cliff: a builtin arriving before a pin (this row, and
the remedy really is a pin) and a lib/rtl name leaving before a pin (mine,
0ffe185bb, and the remedy was to put it back beside the builtin copy — see
task-b-five-system-names-still-in-sysutils-are-waiting-on-a-pin-not-on-a-decision).
Only the first has an instrument. Widening this canary to compile something from
external/ would need the externals fetched, which is one
tools/install_externals.sh and is not done in a fresh clone
(bug-b-lib-test-unrunnable-in-a-fresh-clone-no-synapse-fetch); noting it here
rather than filing, since the cheap half — running that script before trusting a
make lib-test green — is a habit, not a tool.
2026-09-14 (frank-user) — a fourth instance, and this one is a PARSE error
The row was PASS after pin v408, so today's RED is new and it is mine. It is a
harder failure than the previous ones: not undefined variable on a builtin the
pin lacks, but pascal26:218: error: expected 'begin' before 'weakexternal' —
the pinned compiler cannot get through lib/rtl/palthread.pas at all, so no unit
that uses palthread builds under $(PXX_STABLE) either.
Cause, and it is the documented shape rather than an accident: weakexternal
(5484ad6bb) and the __pxxTlsBlockSize / __pxxSigAltStackSize builtins are
what the CLONE_SETTLS fix needed, and lib/rtl/palthread.pas is where they are
used. The tree self-hosts and the fixedpoint is green.
No workaround was written, deliberately. A {$IF PXX_VERSION >= N} guard
around the weak imports would clear this row and would be precisely the
compiler-appeasement workaround CLAUDE.md names — reshaping library code to
please an old compiler, for a condition one pin resolves. This canary's own
failure text agrees: "The change is usually RIGHT and the remedy is a pin, not
a revert."
What is inert until the next pin: lib/rtl/palthread.pas, and therefore
Track B's make lib-test and make demos for anything threaded. make pin is
owner-only; this is reported, not waited on.