v1.108.299 — A name the file never spells
A name the file never spells
(struct posn (x y)) binds posn?, posn-x and posn-y as well as posn, and those are the names Racket callers actually write. None of them occur anywhere in the file, so they exist in an index only if synthesised. They now are — sharing the struct form's byte range, so get_symbol_source("posn-x") returns the form that generates it, which is the answer DrRacket gives for "where does this come from".
#549 by @otherjoel, a follow-up to his own #548. On the 211-file fidelity corpus: 3,060 → 3,438 symbols, extra and wrong_span both still 0, coverage 86.2% → 86.5%. On a real Racket project, 1,754 → 1,935.
The PR's headline is the fabrication its own harness caught
Emitting make-<name> for every define-struct invented make-base-object/c for (define-struct base-object/c (...) #:constructor-name NEVER_CALL_THIS) in racket/private/object-c.rkt. #:constructor-name replaces the default constructor where #:extra-constructor-name adds one. One fabricated name across 211 files, caught by the extra bucket that must stay at zero — on the author of the harness that caught it. Reading the code would not have found it; every rule was subsequently checked against expand output for that exact variant.
Two rules that are easy to get backwards
Own fields only. (struct derived base (c)) binds derived-c and not derived-a — inherited fields keep the supertype's accessors. The supertype occupies the slot before the field list, so the fields are the first list child after the name, never a fixed index and never the last list. That single rule is also what stops #:guard (lambda (a b n) ...) and #:property prop:procedure (lambda (s) 1) being read as fields, and what resolves (serializable-struct/versions posn 1 (x y) ()) to (x y).
serializable-struct and serializable-struct/versions were absent from the form table entirely and produced no symbol at all — not even the struct name. struct:<name> is deliberately not emitted (ranking noise). #:name / #:extra-name are emitted as type, because a struct-type transformer is not callable.
PARSER_GENERATION 2 → 3 — your next index call re-parses once
Struct bindings change which symbols exist for a file whose content never changes, so an index already holding .rkt files would never re-read them. Expect one full re-parse per repo on the next index_folder / index_repo, reported as rebuild_reason="parser_generation_upgrade"; jcodemunch-mcp refresh does it in bounded, resumable slices.
⚠ #548 shipped Racket itself without a bump and that was correct. .rkt was wrong_extension in every index that existed, so the language arrived through discovery — a file nobody parsed cannot hold a stale parse. But 1.108.297 and 1.108.298 both parse Racket, so an index built by either holds .rkt at generation 2 with the old symbol set. Coverage does not need a bump; extraction does, and the two arriving one release apart is the clearest statement of that line we are likely to get.
Scope is narrow — three extensions, and the window of affected indexes opened 2026-08-24. It is bumped anyway because a stamp equal to the constant is unrepairable later: the decision is cheap today and impossible next week.
The coverage figure had three mirrors and the PR regenerated one
results.json moved to 86.5 / 475 while LANGUAGE_SUPPORT.md and the fidelity harness's own README kept the older run, with a green suite either side — our missing ratchet, not the contributor's omission. tests/test_racket_fidelity_artifacts.py now derives six figures from the artifact and checks the rows, not the headline; clean_files and the 10-worst-file total come from per_file, because no summary field carries them. Run against the stale tree first: five assertions fail, three pass, and the three that pass are the figures this change genuinely did not move.