← board

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

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

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.pasAllocMem, 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.