Skip to content

Keep bibliography out of the shared-vocabulary edge weight - #105

Merged
realmarcin merged 1 commit into
mainfrom
fix/citation-prefixes-out-of-edge-weight
Sep 22, 2026
Merged

realmarcin merged 1 commit into
mainfrom
fix/citation-prefixes-out-of-edge-weight

Conversation

@realmarcin

Copy link
Copy Markdown
Contributor

Closes #62.

shared={t for t in shared if t.split(":")[0]!="DOI" or True}

The or True made the condition unconditionally true, so the comprehension rebuilt the set unchanged and no citation prefix was ever dropped.

Why implement it rather than delete it as dead code

roots.CITATION already writes down the principle:

Prefixes that identify a piece of literature rather than a concept. Every Mech cites papers, so counting them alongside the ontologies would say only that.

build_data.py keeps them out of the heatmap's ordering; the cell indexes skip them. The edge weight was the one place the code contradicted its own rule.

And the contradiction was visible on the page: three lines down, the examples attached to each edge already excluded DOI by hand. So a chord's weight was partly bibliography while the evidence offered for that weight deliberately was not. Both now read not in CITATION, so they agree by construction — and PMID is covered too, which today contributes nothing by accident of the data rather than by design.

Measured before deciding, because it moves published numbers

citation share of total edge weight 379 of 11,350 — 3%
edges affected 9 of 36
edges that disappear 0
largest single shift CellStructureMech–ProteinTraitsMech, −15%

The graph's shape does not change and no chord vanishes.

No data churn in this PR

assets/fleet/ and fleet_data.json are snapshots of the last full pipeline run, so nothing committed changes here and assemble_page.py --check still passes. The new numbers appear when the pipeline is next rerun as a whole — a partial rerun would date the heatmap and the edges to different scans, which is the trap that bit this repo before. That refresh is owed anyway for #84, #85 and #87, and belongs in that PR where the 40-file diff is the point rather than noise.

A test asserting no CITATION prefix reaches an edge needs build_subsets.py to be importable — that is #97, which is next.

27 tests and assemble_page.py --check pass; the only changed file is scripts/fleet/build_subsets.py.

🤖 Generated with Claude Code

`build_subsets.py` filtered DOI out of an edge with

    shared={t for t in shared if t.split(":")[0]!="DOI" or True}

where the `or True` made the condition unconditionally true, so the set was
rebuilt unchanged and no citation prefix was ever dropped (#62).

`roots.CITATION` already states the principle the filter was reaching for:
every Mech cites papers, so counting those alongside the ontologies "would say
only that". `build_data.py` keeps them out of the heatmap's ordering and the
cell indexes below skip them; the edge weight was the one place that still
counted them, which is the single spot where the code contradicted its own
written rule.

The inconsistency was visible on the page. Three lines down, the examples
attached to each edge already excluded DOI by hand, so a chord's weight was
partly bibliography while the evidence offered for that weight deliberately
was not. Both now read `not in CITATION`, so the two agree by construction and
PMID is covered as well — it contributes nothing today, but by accident of the
data rather than by design.

Measured on the pre-change data before deciding, since this moves published
numbers: citation prefixes are 379 of 11,350 total edge weight (3%), touching
9 of 36 edges, and no edge disappears. The largest single shift is
CellStructureMech-ProteinTraitsMech, down 15%.

No committed data changes here. `assets/fleet/` and `fleet_data.json` are
snapshots of the last full pipeline run, so the new numbers appear when the
pipeline is next rerun as a whole — a partial rerun would date the heatmap and
the edges to different scans. That refresh is owed anyway for #84, #85 and #87.

A test asserting no CITATION prefix reaches an edge needs build_subsets.py to
be importable, which is #97.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
realmarcin added a commit that referenced this pull request Sep 22, 2026
Reviewed adversarially; findings filed as issues #112 (#103) and #119 (#105), neither blocking.
@realmarcin
realmarcin merged commit c722e9b into main Sep 22, 2026
1 check passed
@realmarcin
realmarcin deleted the fix/citation-prefixes-out-of-edge-weight branch September 22, 2026 07:25
realmarcin added a commit that referenced this pull request Sep 22, 2026
Reviewed adversarially; findings filed as issues #112 (#103) and #119 (#105), neither blocking.
realmarcin added a commit that referenced this pull request Sep 22, 2026
`build_subsets.py` resolved every Mech checkout at import — `MECHS` called
`mech_root`, which exits when one is missing — and then scanned. `build_data.py`
read both JSON inputs at import and rewrote fleet_data.json. Neither could be
imported to look at a constant (#97).

Both now keep only the pure constants at module level. `build_subsets` grew a
`prepare()` that resolves the checkouts and indexes HabitatMech's pages, and
everything from the scan onward moved into `main()`; the function bodies are
untouched and still refer to the same module-level names, so `MECHS`,
`hab_pages` and the rest are declared empty and filled by `prepare()` rather
than moved. `build_data` grew `build()` and `main()`, with the same treatment.
Importing either is now free and writes nothing.

That lets the tests read the lists the pipeline actually uses. They had been
recovered from the source text with `ast`, which cannot see a list rebuilt
after its literal — the hole #99 closed partly and #101 recorded the rest of.
Reading the objects retires the class: all five shapes now fail, including the
three the parser could not see.

    VOC, _X = [...], 1     parser: passed   import: FAILED
    del VOC[0]             parser: passed   import: FAILED
    VOC[0] += "X"          parser: passed   import: FAILED
    VOC += [...]           parser: FAILED   import: FAILED
    VOC.append(...)        parser: FAILED   import: FAILED

`literal()` and its mutator allowlist are gone with the parsing they served.

Verified behaviour-preserving by running both scripts before and after the
refactor over the same checkouts and comparing every output file. `assets/fleet`
and `subsets_summary.json` match semantically: identical per-Mech scan counts,
identical edge weights and `by` breakdowns, identical term lists in identical
order, every term entry equal.

One edge file differs by bytes, and it is not this refactor. `CAS` and `ENVO`
are tied at 1 in that edge's `by`, and `Counter.most_common()` breaks ties by
insertion order, which comes from iterating a set of strings under per-process
hash randomization. Filed as #107, since it means a refresh churns committed
assets for no reason and the pipeline cannot be used to verify itself.

No committed data changes here; the runs' output was restored.

Rebased over #105, carrying its CITATION filter into main(). From the review:
main() now calls prepare() itself, so importing the module and calling main()
resolves the checkouts instead of failing with a KeyError inside scan()
(closes #117); the unused `import ast` left behind by literal()'s removal is
gone (closes #116).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
realmarcin added a commit that referenced this pull request Sep 22, 2026
`build_subsets.py` resolved every Mech checkout at import — `MECHS` called
`mech_root`, which exits when one is missing — and then scanned. `build_data.py`
read both JSON inputs at import and rewrote fleet_data.json. Neither could be
imported to look at a constant (#97).

Both now keep only the pure constants at module level. `build_subsets` grew a
`prepare()` that resolves the checkouts and indexes HabitatMech's pages, and
everything from the scan onward moved into `main()`; the function bodies are
untouched and still refer to the same module-level names, so `MECHS`,
`hab_pages` and the rest are declared empty and filled by `prepare()` rather
than moved. `build_data` grew `build()` and `main()`, with the same treatment.
Importing either is now free and writes nothing.

That lets the tests read the lists the pipeline actually uses. They had been
recovered from the source text with `ast`, which cannot see a list rebuilt
after its literal — the hole #99 closed partly and #101 recorded the rest of.
Reading the objects retires the class: all five shapes now fail, including the
three the parser could not see.

    VOC, _X = [...], 1     parser: passed   import: FAILED
    del VOC[0]             parser: passed   import: FAILED
    VOC[0] += "X"          parser: passed   import: FAILED
    VOC += [...]           parser: FAILED   import: FAILED
    VOC.append(...)        parser: FAILED   import: FAILED

`literal()` and its mutator allowlist are gone with the parsing they served.

Verified behaviour-preserving by running both scripts before and after the
refactor over the same checkouts and comparing every output file. `assets/fleet`
and `subsets_summary.json` match semantically: identical per-Mech scan counts,
identical edge weights and `by` breakdowns, identical term lists in identical
order, every term entry equal.

One edge file differs by bytes, and it is not this refactor. `CAS` and `ENVO`
are tied at 1 in that edge's `by`, and `Counter.most_common()` breaks ties by
insertion order, which comes from iterating a set of strings under per-process
hash randomization. Filed as #107, since it means a refresh churns committed
assets for no reason and the pipeline cannot be used to verify itself.

No committed data changes here; the runs' output was restored.

Rebased over #105, carrying its CITATION filter into main(). From the review:
main() now calls prepare() itself, so importing the module and calling main()
resolves the checkouts instead of failing with a KeyError inside scan()
(closes #117); the unused `import ast` left behind by literal()'s removal is
gone (closes #116).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
realmarcin added a commit that referenced this pull request Sep 25, 2026
…kill that does it (#120)

* Add a skill for refreshing the X-Mech page from the Mechs as they are now

There was no procedure for this, only the pipeline reference in
_fleet/README.md, and the last refreshes show what that costs: a census taken
from one checkout and card stats from another, a census that read a shared
checkout's uncommitted files, and figures on five other pages left behind the
cards.

The skill pins every Mech and CLAW once, builds a sparse shared snapshot at
those commits instead of pulling the shared checkouts, runs the pipeline
canary-first with the side effects checked after each stage, re-checks every
hand-curated claim against the live sites and the pinned repos, propagates the
figures to every page that repeats them, rewrites the provenance record, and
stops at a reviewed PR. It keeps the methodology fixed; census scope changes
(#84, #87) stay out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Record the revision each count was read at, and make subset output stable

The census and the card stats are written by different scripts, and nothing
said which checkout either had read. The last refresh put 364 CommunityMech
records in the census and 396 in the stats, and its ProteinTraitsMech census
counted 770,276 UniProt references where the commit it claimed holds 657,598,
because it read a shared checkout's uncommitted files. roots.revision() now
names the commit, marked +dirty for a checkout with local changes;
prefix_census.py writes it per Mech under _revisions and mech_stats.py writes
it as source_revision. The latter field was already in the committed file,
added by hand, so rerunning the script would have dropped it.

build_subsets.py wrote two orders that came from iterating a set of strings,
which changes with each process's hash seed: tied prefixes in each edge's "by"
breakdown (#107) and, not in that issue, the order of the per-cell summary,
which reaches subsets_summary.json and the page's embedded data. Both now sort.
Two runs over the same snapshot under different seeds are byte-identical.

Closes #107.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Fold the first run's lessons into the update skill

What the 2026-09-24 run hit that the procedure did not yet say: scanning with
resource warnings as errors floods the log because the census never closes its
files (#118); git grep -E has no \b; three cross-references pointed the wrong
way, so direction needs checking as well as existence; HUB text renders after a
prefix; and two sites published new records mid-run. For the last, the skill now
says to keep the page a consistent snapshot at the pins and record the live
figure in the audit, rather than re-pinning one Mech, which would be a partial
rerun.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Refresh the X-Mech page from the Mechs as they are on 2026-09-24

Every number was re-derived from one set of revisions: each Mech's GitHub main
and CLAW's, pinned once and read from sparse clones of the shared checkouts,
never their working trees. Several of those lagged their remotes by up to 121
commits, and ProteinTraitsMech's carried 45,049 uncommitted files.

Derived layer. The census, subsets, fleet data and card stats were regenerated
at the pins, and the manifest snapshot advanced to CLAW's pin (semantically
unchanged). The census now names its revisions, and RefreshProvenanceTests pins
the census, the stats and the site audit to the same commits and record counts.
On the old data that test fails for exactly the known partial rerun, 364 vs 396
CommunityMech records; each of its four invariants was mutation-tested. The #105
citation filter takes effect: 379 citation matches leave the edge weights and
all 36 edges survive. The previous census's ProteinTraitsMech counts had been
read from a dirty checkout (770,276 UniProt references where the revision the last audit pinned holds
657,598), so the lower figures are a correction.

Hand-curated layer. Twelve read-only auditors checked 533 claims against the
live sites and the pinned repositories; an independent skeptic re-derived each
proposed change, and a second round verified what the first round had missed.
Beyond the moved figures (CommunityMech 422 and 15 categories, TraitMech 763,
CellStructureMech 542, MediaIngredientMech 2,953 / 2,611 mapped, CultureMech
6,288, ProteinTraitsMech 429,293):
- three cross-references pointed away from the consumer and now point at it;
  three new ones are implemented upstream (TraitMech into ProteinTraitsMech,
  TraitMech and HabitatMech into TaxonMech), and the list now matches XREFS;
- the kg-microbe ties claimed exports that do not exist: only
  MediaIngredientMech feeds the graph, CultureMech and CommunityMech have
  exporters but no release, and TraitMech receives embeddings rather than
  exporting SSSOM;
- "ENVO-grounded", "a superset of TraitMech's node vocabulary", "one node
  vocabulary", "content-hashed CURIE" and "evidence with PMIDs or DOIs" were
  fleet-wide generalizations the sources contradict, and now say what holds;
- CultureMech's pages/ index is live again, so its Browse link and card cite it.
The figures are propagated to every page that repeats them, and the three
dedicated pages' permalinks move to the pins (all 33 anchors checked).

CellStructureMech (544) and TraitMech (764) published after the pins; their
cards keep the pinned figures and site_audit.json records what the sites showed.

Closes #85, #113.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Address the adversarial review of the refresh

Provenance that can actually tell. roots.revision() judged a checkout clean
from git status, which never lists ignored files, hides untracked ones under
status.showUntrackedFiles=no, and says nothing about paths a sparse checkout
leaves out, while record_paths() globs the disk. It now compares the counted
records with the files git tracks under the same globs, scopes status to the
record directories and src/ so an unrelated scratch file is not "dirty" (#121),
runs git without optional locks in the shared checkouts (#123), and returns None
for a directory inside another repository instead of that repository's HEAD
(#124). Each scan takes the revision before reading and stops if HEAD moves
(#122), reads records in a with block (#135), and stops on an unreadable record
instead of skipping it and reporting the full count (#127).

Coverage. build_subsets.py records its revisions too; the tests now require them
to equal the census's, fleet_data.json to equal build_data.build() over the
committed inputs (#126), and the audit's CLAW pin, card figures, merged PRs and
repository set to match (#128). RevisionTests covers clean, modified, hidden
untracked, ignored, sparse-excluded and nested cases; SubsetDeterminismTests
runs build_subsets.py under four hash seeds on a fixture with tied prefixes
(#129). Each was mutation-tested: reverting the #107 sort, the tracked-set
comparison or the top-level check turns its test red.

Data. CommunityMech's four data/isolates records have no published page, so
they now get no record link instead of a 404 (#139). Rerun at the same pins:
only CommunityMech's record lists changed; the census, stats, manifest and audit
are byte-identical.

Content. The hub panel names the four Mechs that share only the namespace
(#140); the CommunityMech to CultureMech entry cites CultureMech's import and
backfill scripts (#141); the resources.md diagram shows only MediaIngredientMech
feeding kg-microbe (#142); CommunityMech's 16-category landing tile is explained
(#143); the arrow legend covers scope hand-offs (#145).

Skill. CLAW is cloned from GitHub at its pin and the manifest refreshed before
anything reads it (#130, #138); the shared checkouts get their own variable
(#133); the audit fan-out has a canary (#136); audit SHAs come from the pins
(#125); the gate wording allows documented post-pin drift (#134).

Closes #121. Closes #122. Closes #123. Closes #124. Closes #125. Closes #126.
Closes #127. Closes #128. Closes #129. Closes #130. Closes #133. Closes #134.
Closes #135. Closes #136. Closes #138. Closes #139. Closes #140. Closes #141.
Closes #142. Closes #143. Closes #145.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Hash each audited page at the pin, and derive the byte-identity note

site_audit.json's notes said the live page was byte-identical to the one at the
pin for NaturalProductMech, CellStructureMech and TraitMech, while the hash
beside each note was of a page published after the pin (#155). The notes were
typed; the hashes were fetched. The audit now records the committed copy's hash
at the pin next to the live one and generates the sentence from the comparison.
That also shows MediaIngredientMech's data file changed after the pin, with the
same figures. The skill says never to type an identity claim into the audit.

Closes #155.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Complete the cross-reference arrows, and measure TaxonMech in the census

The graph drew only two arrows at TaxonMech and none leaving it, although taxa
tie it to most of the fleet. Two separate gaps.

Direct references. The earlier audit searched records, schemas, docs and
READMEs; this sweep also read ingest scripts, vendored snapshots, source
catalogues, CI workflows and site generators in all ten repositories at the
pins, and an independent verifier checked each of 32 candidate pairs. Both
lists now hold the 29 confirmed arrows, one per ordered pair:
- 16 new, including TaxonMech's scope hand-offs to TraitMech, HabitatMech and
  CultureMech (its curation rules leave traits, habitats and media to them) and
  the write gate it ported from CellStructureMech;
- the reverse directions that also exist: CommunityMech records cite CultureMech
  media and MediaIngredientMech ingredients, and NaturalProductMech defers
  antimicrobial mechanism to AntibioticMech;
- adopted practices, e.g. ProteinTraitsMech's merge rule from
  MediaIngredientMech and AntibioticMech's site generator from HabitatMech;
- the 13 kept arrows carry the verifiers' wording, and HabitatMech to
  CultureMech is removed: HabitatMech grounds that concept itself and hands
  nothing over.

Taxon identity. Most species and strains the other Mechs cite are TaxonMech
records, but that is a shared identifier, which the page draws as vocabulary
chords, and TaxonMech had never been scanned (#87). It now is: a census row,
eight NCBITaxon overlaps (ProteinTraitsMech 7,444 shared taxa, HabitatMech
6,423, NaturalProductMech 1,708, CommunityMech 1,167 with GTDB, TraitMech 274,
CellStructureMech 124, AntibioticMech 109, CultureMech 54), and record links to
taxon.html?id=. build_subsets.py scans ProteinTraitsMech and TaxonMech last,
keeping only terms another Mech cites; the proteins keep every taxon, so their
overlap with TaxonMech is exact. The census-coverage wording is one token that
reads "all ten Mechs" now. Other rows are unchanged; TaxonMech's labels improve
taxon labels across the overlaps. Record-subset assets grow to about 10.5 MB;
the two largest overlap files (2.3 and 1.8 MB) load only on click.

Rendered in a browser: 29 arrows, 8 TaxonMech chords, no console errors.

Closes #87.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Route the arrows so they stay legible, and fix what the review of 102f461 found

Geometry. Arcs bowed away from the chord midpoint, which for opposite nodes is
the hub: TraitMech's and MediaIngredientMech's arrows collapsed into one straight
line through it, and five arcs passed under other nodes (#166). An arc now runs
outside the ring, its control point on the bisector of the two node angles and
pushed out only as far as the canvas allows until it clears every node; if it
cannot, it bows through the interior around the hub. A reverse pair takes a
wider lane, or the other side inside. Measured in a browser: every arc clears
other nodes by 19 px and the hub by 74 px, none leaves the canvas, and reverse
pairs sit 21 px or more apart. Node labels get a halo where arcs pass behind
them. The legend now separates dashed record-data arrows from dotted schema and
practice ones, and the overview says so (#167).

Arrows, now 30. CellStructureMech's scope rule leaves phenotypes to TraitMech,
a missing arrow (#158). The helpers AntibioticMech and NaturalProductMech credit
to TraitMech match HabitatMech's and AntibioticMech's copies line for line, so
the credit goes on those arrows rather than new ones (#159). Fixed: an evidence
path that was never committed (#156), "about two-thirds" of recipes, which is
5,696 of 6,288 canonical media (#160), and three skill names that do not exist
(#162). _fleet/README.md now states what counts as an arrow.

Taxon labels. TaxonMech is now the first NCBITaxon label authority and a taxon it
keys a record by takes that record's label, where its one extra vote had split
other Mechs' spellings and erased labels (#163): unlabelled overlap terms fall
from 3,301 to 1,714. Its chords show species it has records for rather than
Bacteria or a genus (#164), and folded YAML labels are read whole (#165). Edge
weights and cells are unchanged.

Prose. The taxon note says eight of the other nine Mechs (#168); the heat note
says species-level and infraspecific and puts taxa in the organism arm (#169);
resources.md's Related lines follow the arrows (#170); the partial-coverage
wording has a test again and a stale comment is gone (#171).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Cite CultureMech's committed README now that its pages/ index is gone again

#120 had pointed CultureMech's Browse link and its card check at
culturebotai.github.io/CultureMech/pages/, which was live on 2026-09-24 and
stated the canonical 6,288 merged records. It returns 404 again on 2026-09-25
(#175): CultureMech publishes only committed files from main, pages/index.html
has been untracked since CultureMech #320, and the copy that was served was a
leftover the 16:22Z Pages rebuild removed. It had also been 404 on 2026-09-20.

The Browse link goes back to the site root, which serves the app/ browser. The
card note cites the README's corpus snapshot at the pin. check_cards.py reads the
committed README (it now accepts an absolute URL), and the audit records it as
CultureMech's source; the live README is byte-identical to the pinned one.
culturemech.md and _fleet/README.md stop saying the index is live.

Closes #175.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* State why CultureMech's pages/ comes and goes, and test the README source

Review of d0bbb19. The stated cause was wrong (#180): pages/index.html was never
committed and is gitignored since April; CultureMech's generate-pages workflow
builds pages/ and deploys it through GitHub Actions, and the branch-based Pages
build replaces that deployment on other pushes to main. So the index appears and
disappears, and the card intro now says it is intermittently published rather
than "not currently" (#181). The README's description of the audit names
CultureMech's committed README as the one non-Pages source (#179), and the
audit's local_date is derived from its own timestamp (#178).

check_cards.py gains source_url() for the absolute-URL case, and its text parser
ignores Markdown emphasis, so a README that bolds the number alone still reads;
tests cover both and the wrapped-line case (#177). Whether a 404 or reworded
source should fail rather than warn is #176, handled with #148.

Closes #177. Closes #178. Closes #179. Closes #180. Closes #181.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Say that CultureMech's browser data comes and goes too, and pin the tests to SOURCES

Round-2 review of the #175 fix. The branch-based Pages build removes CultureMech's
app/data.js along with pages/ and the dashboard, so the app/ browser loaded with
no records on September 25; _fleet/README.md now says so and names the upstream
fix, setting CultureMech's Pages source to GitHub Actions (#182, recorded on
#172). The README and the audit scope describe CultureMech's source as its
committed README, the one non-Pages source (#185). The card intro drops a bare
"(#175)" that read as CultureMech's own issue (#184). The CultureMech test reads
the SOURCES entry, so a mistyped URL or selector now fails it; both mutations
were checked (#183).

Closes #182. Closes #183. Closes #184. Closes #185.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Stop saying CultureMech's published browser serves its records

Round-3 review of the #175 fix. CultureMech's recipe browser loads app/data.js,
which only its Actions deployment carries, so it opens empty whenever a branch
build has replaced that deployment, as on September 25. The graph panel no longer
says the published browser serves the 15,878 records, culturemech.md says the
browser can open empty, and the review skill's worked example says the same
(#188). The embedding maps are unaffected: their data is inline and committed.
The CultureMech source test now pins the whole README URL, so a typo in the
branch or file name fails too (#189).

Closes #188. Closes #189.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Describe what CultureMech's recipe browser shows without its data

Round-4 review of the #175 fix: without app/data.js the browser never leaves
"Loading recipes...", it does not open empty (#193).

Closes #193.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Say CultureMech's browser never finished loading, as culturemech.md does

Round-5 review of the #175 fix: the fleet README still described September 25 as
the day the browser "loaded with no records"; without app/data.js it never leaves
"Loading recipes..." (#204).

Closes #204.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Let the README parser survive a wrap inside the label, and name the README exception everywhere

From the Codex review of the #175 fix. The text parser collapsed no whitespace,
so a formatting-only wrap between "merged" and "records" in CultureMech's README
returned None; whitespace now collapses before matching, with a test for a wrap
inside the label (#207). _fleet/README.md and the review skill's general
descriptions of the card sources now name CultureMech's README exception (#208).

Closes #207. Closes #208.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Name CultureMech's README exception in the remaining general statements

Verification review of e115229: the fleet README's census paragraph, the
check_cards.py docstring and assemble_page.py's fleet_records docstring still
said every card follows a published site; the review skill now says the README
figure is on the card and in MECHS (#211).

Closes #211.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A DOI filter in build_subsets.py is a no-op because of an 'or True'

1 participant