Skip to content

refactor!: ADR 0017 — retire the "species"/"reactant" vocabulary for standard Petri-net terms - #25

Open
thevolatilebit wants to merge 8 commits into
mainfrom
rename/petri-vocabulary
Open

refactor!: ADR 0017 — retire the "species"/"reactant" vocabulary for standard Petri-net terms#25
thevolatilebit wants to merge 8 commits into
mainfrom
rename/petri-vocabulary

Conversation

@thevolatilebit

@thevolatilebit thevolatilebit commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Why

A resource pool in this engine was called a species — chemistry's word, in a tool whose own front page opens by denying it is a chemical reaction network — while the newer layers already said marking, arc and MarkingPlot. This PR retires the inherited chemical-reaction-network vocabulary for the standard Petri-net terms. The specification is spec/adr/0017-petri-net-vocabulary.md, which is the first commit on this branch (f623856) — read it for the full argument, the options considered, and the RD→Petri term dictionary in its appendix.

This is a pure rename. No semantics, no numerics, no allocator behaviour, no wire-format meaning beyond key spelling. The one observable-output change is called out under Behavioural notes below.

What changed, per tier

One commit per tier, in migration-plan order, suite green at each:

Commit Tier What
f623856 ADR 0017 itself (the branch base's first commit).
adaa0ed 2 The :S store column symbols spec*place* (specName/specModality/specCost/specValuation/specInitVal/specReward/specStructured/specRole/specInitUncertainty), landed atomically with the two occursin("spec", string(attr)) reflection filters in src/operators/joins.jl and src/operators/equalize.jl that select those columns by name prefix. Splitting those apart would have silently broken @join/equalize!.
ebf648c 1 The six exported names (table below), each behind a one-release shim.
fdbe572, eb8cd05 5 Local identifiers, struct fields (ArcSpec.species.place, ReactionNetwork.reactants.arcs) and code prose. No shims for struct fields, following the ADR 0015 precedent.
aeb7a61 4 spec/CONTRACT_DRAFT.md (amended in place, plus a new Glossary carrying ADR 0017's term dictionary), spec/INVENTORY.md, spec/STATUS.md, docs/src/**, docs/literate/**, demo/**. New docs/src/glossary.md page.
49aa788 ADR 0017 Status:Accepted + Implemented, and its spec/adr/README.md row, in ADR 0015's header style. Corrects three things the draft got wrong (see below).
43384f7 3 The serialized JSON keys — last and on its own commit, so a document written against the previous release is a single isolated diff to bisect against.

SCHEMA's :S object symbol is unchanged (:P is already the parameter object), and the words token and transition are unchanged — they were already canonical Petri vocabulary.

Tier 1 — exported names

Every retired name resolves for one release as a @deprecate/@deprecate_binding shim in the single block at src/ReactiveDynamics.jl:488-524, following the pattern ADR 0015 established.

Retired Use instead
@add_species @add_place
SetSpecies SetMarking
ReactantSpec ArcSpec
reactant_specs arcs
specname placename
register_structured_species! register_token_kind!
get_species / set_species! get_place / set_place!
populate_reactant_specs! populate_arcs!

MarkingPlot keeps its name — it was already correct.

Tier 3 — serialized keys

The writer emits only the new spellings; the loader accepts every retired one for one release with a deprecation warning, and validate reports its diagnostic paths in whichever spelling the document actually uses (so a legacy document's diagnostics still point at real keys). Re-exporting migrates a document.

Retired key Use instead
top-level "species" array "places"
top-level "reactants" array "arcs"
an arc's "species" "place"
a population[] entry's "species" "place"
the action verb "set_species" "set_marking"
a ref node's "kind": "species" "kind": "place"
the export-bundle manifest key "species" "places"
a trajectory record's "species" "place"
the result-frame / ledger DataFrame column :species :place
the @select/@advance field :species :place

The read shim is three helpers in src/serialize.jl (_legacy_key, _legacy_path, _ref_kind), a legacy branch in stmt_from_dict, and _LEGACY_ACTION_VERBS in src/actions.jl.

Two aliases are accepted silently rather than with a warning: the @select/@advance field name (src/predicates.jl) and the SetField target (src/solvers.jl). Both are read inside the step loop, where a depwarn would fire once per token per tick.

On the warning's reach: Base.depwarn is maxlog = 1 per call site and a no-op unless Julia runs with --depwarn=yes. A user therefore sees one warning per session, not one per key — the same contract the Tier-1 name shims already carry. Anyone wanting these to be hard errors can run --depwarn=error.

Behavioural notes

The rename is behaviour-preserving with exactly one observable-output exception, disclosed for completeness:

  • An aliasless @join block now names its generated observable shared_place_N instead of shared_species_N (src/operators/joins.jl). No test, demo, or fixture pins that name, but it is a user-visible string and it can reorder the name-sorted observables list for a model that both uses an aliasless @join and reads observables positionally.

Nothing else changes: no allocator behaviour, no RNG stream, no numeric result. The BD acquisition demo reproduces its previous numbers.

Deliberately NOT in this PR

  • ADRs 0001–0016 keep their original wording. ADRs are append-only. Instead, spec/adr/README.md gains a Vocabulary lineage paragraph naming ADR 0017 as the boundary and pointing at the term dictionary as the translation table.
  • No permanent legacy-key alias. The draft ADR left open whether saved models outside this repository warranted a permanent read alias; that was resolved in the negative, so both shim sets (names and JSON keys) drop together at the next minor release.
  • No fix for two pre-existing inaccuracies found while sweeping, because they are unrelated to the rename: spec/adr/README.md still lists the three CONTRACT §1.4 modality validators as @test_skip placeholders (they landed in 53d1fac), and CONTRACT §6.2/§7.5 cite a live-mutation API add_place!/add_transition!/add_param! that does not exist in src/.
  • No fix for one pre-existing broken demo. demo/core_engine_tour/core_engine_tour.jl §3d deliberately constructs the @rate + cycletime == 0 foot-gun, which 53d1fac turned into a construction-time ArgumentError; the script has been unrunnable past §3d since. Verified identical at this branch's base (base package, base demo file, same transition r0, same error) — it is not caused by this PR, and fixing it would smuggle an unrelated semantic change into a rename.
  • .github/workflows/ untouched.

The two documentation registers

Deliberate, and please do not "fix" it in review:

  • The spec and the API reference say place, marking, arc — those are the names in the code and in the literature.
  • The tutorials and case studies say resource pool for the same thing — the word a portfolio or capacity discussion actually uses — glossed once at first use as "a place, in Petri-net terms".

Both registers describe one object; nothing in the engine distinguishes them. The new docs/src/glossary.md states the rule, carries the term dictionary and the RD→Petri-literature mapping, and disambiguates token (a discrete unit of resource in a place) from the language-model sense.

Verification

All output below is real, captured on 43384f7.

Suitejulia --project=. -e 'using Pkg; Pkg.test()':

Test Summary:  | Pass  Total     Time
Semantic tests |  821    821  3m21.3s
     Testing ReactiveDynamics tests passed

821 pass / 0 fail / 0 broken / 0 skipped, against 801 at the branch base c671875. The +20 are new regression pins, not relaxations: +7 for the _validate_node! place-pool branch the rename exposed as uncovered, and +13 for the new retired-key deprecation testset. No @test_skip and no @test_broken were added.

The deprecation read-shim is observable in that run (Pkg.test() runs with --depwarn=yes):

┌ Warning: the serialized key `"species"` is deprecated (ADR 0017); use `"places"` — re-export the document to update it.
└ @ Core ~/.../src/serialize.jl:229
┌ Warning: the serialized ref kind `"species"` is deprecated (ADR 0017); use `"place"`.
└ @ Core ~/.../src/serialize.jl:97
┌ Warning: the serialized action verb `"set_species"` is deprecated (ADR 0017); use `"set_marking"` — re-export the document to update it.
└ @ Core ~/.../src/serialize.jl:562

Formatterjulia -m Runic --check .: no output, exit 0.

Grep gate. Three families, because _ is a word character (so \b is blind to set_species) and spec matches specified/inspection/the spec/ directory (so the prefix gate must be case-sensitive):

Gate Pattern Residual hits
A \b(species|reactant|reactants|reactantspec)\b, -i 55
B spec[A-Z], case-sensitive 2
C _species|species_|_reactant|reactant_, -i 20

Every residual hit is deliberate and enumerated — the deprecation-shim block in src/ReactiveDynamics.jl, the Tier-3 read shim and _LEGACY_ACTION_VERBS, _AKA_LEGACY_NAMES, the retired-key deprecation test, the two "Renamed in v0.3" tables in docs/src/glossary.md, the retired-keys admonition in docs/src/reference/json_schema.md, the CONTRACT Glossary's two retired-names paragraphs, the spec/STATUS.md and spec/adr/README.md rows describing this rename, and the append-only ADRs 0001–0016 (excluded by construction). Gate A by file:

  18 src/serialize.jl                      3 src/interface/update.jl
  12 test/semantic/serialization_ir.jl      3 spec/CONTRACT_DRAFT.md
   8 docs/src/glossary.md                   2 src/solvers.jl
   4 spec/adr/README.md                     2 src/predicates.jl
   1 src/ReactiveDynamics.jl                1 spec/STATUS.md
   1 docs/src/reference/json_schema.md

Docs. The site builds: CI's Documentation job succeeded and deployed a preview at https://Merck.github.io/ReactiveDynamics.jl/previews/PR25/. Independently, every @docs entry and every code-@ref target was also cross-checked programmatically against the loaded module: 122 unique @docs entries, 0 unresolved bindings, 0 resolved-but-undocumented, 0 code-@ref targets without a @docs entry, and both new relative links (glossary.mdreference/json_schema.md) resolve on disk. All 8 docs/literate/** sources — the three tutorials, both deep dives, all three case studies — execute to completion locally as well.

Demos. agentic_pipeline, introspection_tour, refinement_tour, aa_integration and wires_viz_tour all run clean. The BD acquisition demo runs end-to-end (reduced to 6 seeds — the 160-seed grid was skipped as agreed): the renamed demo/bd_acquisition/model.rdj.json fixture loads, the per-program ledger carries its :place column, and the ledger reconciliation residual is -2.3e-13. core_engine_tour is the one pre-existing failure documented above. All 22 scripts under demo/ and docs/literate/ parse cleanly.

CI on this PR (independent of the local run above) — every required check green:

Check Result
Julia 1.12 — ubuntu / macOS / windows pass
Julia 1 — ubuntu / macOS / windows pass
Downgrade compat pass
Runic formatting pass
Documentation (+ documenter/deploy) pass
Julia nightly — ubuntu informational only (continue-on-error: true in CI.yml); still running when this body was written

Reviewing this

Read spec/adr/0017-petri-net-vocabulary.md first (commit f623856), then the tiers in order. Tier 2 (adaa0ed) is the one commit where the diff is not self-evidently mechanical — check the two reflection filters against their renamed columns. Tier 3 (43384f7) is the compatibility surface; the deprecation test at the end of test/semantic/serialization_ir.jl is the executable statement of what a legacy document is still promised.

🤖 Generated with Claude Code

thevolatilebit and others added 8 commits August 22, 2026 16:16
…lary

Drafts the second half of the ADR 0015 naming debt. 0015 renamed the
ACSets-lineage store surface but deliberately scoped `species`/`reactant`
and the JSON keys out; nothing downstream needs Catalyst-compatible names
anymore (ADR 0003 dropped the dep, its Phase-3 interop view was rejected),
and the newer layers already speak Petri — `marking` in ADR 0007 and
checkpoint.jl, `MarkingPlot` in analysis.jl, `arc` in visualize.jl — so the
engine currently carries two vocabularies for one concept.

Recommends the full rename (place / marking / arc) behind one-release
shims, with the JSON key rename staged separately behind an open question
about saved models in the wild. KEEPS `token`: it is the canonical Petri
word, and the language-model confusion is a one-sentence gloss, not a
rename. Records the load-bearing constraint that joins.jl:33 and
equalize.jl:58 filter columns by the literal substring "spec", so the
column rename must be atomic with them.

The appendix carries the durable artifact: the RD→Petri term dictionary
(place/colour set/arc weight/preset/firing rate/k-server/firing duration/
read arc/self-loop/continuous transition/P-invariant/QPN), which also
tells a reader which literature answers a question about the engine.

Draft only — no code has moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 99c202247689265ba56a05b11b7aae495b31f5cd)
…* -> place*

The nine `:S` (place) column symbols move to Petri vocabulary in one atomic
change, per ADR 0017 Tier 2:

  specName -> placeName            specReward            -> placeReward
  specModality -> placeModality    specStructured        -> placeStructured
  specInitVal -> placeInitVal      specRole              -> placeRole
  specCost -> placeCost            specInitUncertainty   -> placeInitUncertainty
  specValuation -> placeValuation

The `const SCHEMA` object symbol `:S` is unchanged (`:P` is taken by the
parameter object, and `:S` reads as *places* under either vocabulary), and
the declaration ORDER of the columns is preserved, so `ALLATTRS` and every
`propertynames(net.columns)` reflection loop iterate exactly as before.

Atomic with the rename, because they are substring reflection over column
names and would otherwise silently stop matching any place column:

  * `src/operators/joins.jl:33`     occursin("spec", …) -> occursin("place", …)
  * `src/operators/equalize.jl:58`  occursin("spec", …) -> occursin("place", …)
  * `src/interface/update.jl:191`   Symbol(:spec, uppercasefirst(…))
                                    -> Symbol(:place, …)   (the @cost/@reward/
                                    @valuation macros CONSTRUCT their target
                                    column symbol from a `:spec` prefix; not
                                    reachable by grepping for the old names)

`src/compilers.jl:188` filters on the substring "Name", which `placeName`
still satisfies — unchanged deliberately.

No wire-format change: the JSON keys in `src/serialize.jl` / `src/export.jl`
are hardcoded string literals ("name"/"init"/"cost"/… and the top-level
"species"/"reactants" arrays), never derived from a column symbol, so the
serialized bytes are identical. The key rename is ADR 0017 Tier 3 and lands
as its own final commit.

Suite: 801 pass / 0 fail / 0 broken — identical to the pre-change baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…keep one-release shims

The public surface adopts the Petri-net vocabulary. Six exported names move:

  @add_species                 -> @add_place                 (src/interface/update.jl)
  SetSpecies                   -> SetMarking                 (src/actions.jl)
  ReactantSpec                 -> ArcSpec                    (src/ReactiveDynamics.jl)
  reactant_specs               -> arcs                       (src/ReactiveDynamics.jl)
  specname                     -> placename                  (src/ReactiveDynamics.jl)
  register_structured_species! -> register_token_kind!        (src/interface/agents.jl)

`MarkingPlot` keeps its name (already correct) and loses "species" from its docstring.

Every old name survives ONE release as a shim, in a single clearly-delimited block at the
end of `src/ReactiveDynamics.jl` (after the includes, because three of the new names are
defined in included files) — `Base.@deprecate_binding` for the two types, `@deprecate` for
the three functions, and a hand-written `@add_species` macro that depwarns and splices the
call through to `@add_place`. That block is deliberately the only place in `src/` where the
retired spellings survive, so the rename's grep gate can assert exactly that. Following
ADR 0015, the shims are exported (source compatibility preserved); note that on Julia 1.12
`@deprecate_binding` no longer warns at the use site — it only hides the alias from
`names()` — which is pre-existing behaviour shared with the ADR-0015 `ReactionNetworkSchema`
shim, not something introduced here.

Wire format is untouched: `stmt_to_dict(::SetMarking)` still emits `"verb" => "set_species"`
and `ACTION_VERBS` still carries `:set_species`. That mismatch is staged, not an oversight —
serialized keys are Tier 3 and land as their own commit so the file format can be reverted
independently — and both sites now carry an explicit comment saying so.

Call sites swept across src/, test/, ext/, demo/, docs/, spec/ (ADRs 0001-0016 and
spec/history/ left alone: append-only). `populate_reactant_specs!` is internal and moves in
Tier 5; CONTRACT's `add_species!` prose moves in Tier 4.

Suite 806 pass / 0 fail / 0 broken (baseline 801/0/0). The +5 is mechanical: the
`exports_resolve.jl` guard asserts one test per exported symbol, and four of the six shims
are visible to `names()` (the two `@deprecate_binding` consts are flagged deprecated and
hidden), plus one extra assertion in the exported-macro loop. No test added, skipped or
pinned. `julia -m Runic --check .` clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…and code prose

The mechanical body of the Petri-net vocabulary rename: every Julia-level
identifier and every comment/docstring that still said "species" or "reactant"
now says "place" or "arc". No wire key, no serialized verb tag and no
diagnostic path string moves here — those are Tier 3 and land last, as their
own revertible commit.

Struct fields (renamed with no shim, the ADR 0015 precedent for fields):

  ReactionNetwork.reactants  -> .arcs
  ArcSpec.species            -> .place        (the :S foreign key)
  FoldedArc.species          -> .place
  UnfoldedArc.species        -> .place
  BaseStructuredToken.species-> .place        (a token's current place)
  PopulationEntry.species    -> .place
  ProgramLedger.species      -> .place
  NetworkGraph.species       -> .places
  StateDump.tokens NamedTuple key species -> place
  _PROTOCOL_FIELDS :species  -> :place        (must track the token field)

Internal functions: get_species/set_species! -> get_place/set_place!,
populate_reactant_specs! -> populate_arcs!, extract_reactants -> extract_arcs,
recursively_find_reactants! -> recursively_find_arcs!, _static_reactants ->
_static_arcs, _reactant_atom -> _arc_atom, _reactants_to_dict -> _arcs_to_dict,
_reactant_to_dict -> _arc_to_dict, _reactant_term -> _arc_term,
_emit_macro_reactant! -> _emit_macro_arc!, FoldedReactant(s) -> FoldedArc(s),
UnfoldedReactant -> UnfoldedArc, SpeciesNode -> PlaceNode, _species_sym ->
_place_sym, _species_to_dict -> _place_to_dict, available_species ->
available_places, known_species -> known_places, highlight_species ->
highlight_places, boundary_species -> boundary_places, free_blocked_species! ->
free_blocked_places!, rhs_species/lhs_species -> rhs_places/lhs_places,
species_modalities -> place_modalities. The dead `struct Reactant`
(interface/create.jl) became `ArcTerm`, not `Arc`, because visualize.jl already
owns a live `struct Arc`.

Three of these were never exported but are reached by name anyway
(`using ReactiveDynamics: get_species` appears across the demos, and
`populate_reactant_specs!` is named in the ArcSpec docstring), so they get
one-release `@deprecate ... false` shims alongside the Tier 1 shims.

`@aka` now spells the :S object `place` (`alias_default[:S]`), and a three-line
`_AKA_LEGACY_NAMES` selector keeps `@aka net species = resource` targeting :S
for one release — without it that form would have silently produced
`alias_nothing` metadata rather than erroring.

WHAT DELIBERATELY DID NOT MOVE (all Tier 3):

  * the serialized verb tag `:set_species` / `"set_species"` of `SetMarking`;
  * the JSON keys `"species"` and `"reactants"`, and the `NodeRef` kind value
    `:species` in REF_KINDS;
  * `validate`'s diagnostic path strings (`reactants[$i].*`, `species[$i].*`);
  * the `@select`/`@advance` DSL field name `:species`;
  * the `token_trajectory` and `program_ledger` DataFrame column `:species`,
    which is exported to CSV/Arrow;
  * `export_run`'s manifest and token-record `"species"` keys.

Comments in serialize.jl/export.jl that NAME a wire array still say `species[]`
and `reactants[]` so the prose stays true to the bytes until Tier 3 flips both
together.

ONE OBSERVABLE-OUTPUT CHANGE, called out rather than hidden: a `@join`
identification block with no explicit `:alias` now generates the merged place
name `shared_place_N` instead of `shared_species_N` (operators/joins.jl). It is
a generated NAME, not a format key, so previously-serialized documents are
unaffected (place names are data, read back verbatim) and nothing in the suite,
demos or fixtures pins the old spelling. It can reorder `observables(::Ensemble‑
Problem)` for such a model, since that list is name-sorted.

demo/** and docs/literate/** are swept here too, because their code has to
compile against the renamed fields; their PROSE gets the two-register treatment
(tutorials say "resource pool") in Tier 4. spec/** prose is Tier 4.

One LATENT BUG surfaced and is fixed here: `_validate_node!`'s place-pool branch
(`kind === :species ? places : …`) is reached by every document containing a
`ref` node of kind `species`, yet NO test in the suite exercised it — the only
`ref` in the E7 fixture is a param — so a mismatched identifier there threw
`UndefVarError` on real documents while all 806 tests stayed green. Fixed, and
pinned with two new assertions in E7 that validate a place-kind ref both
resolving and dangling.

Suite: 808 pass / 0 fail / 0 broken (806 at Tier 1, +2 for that new pin).
Runic clean. Wire round-trip of demo/bd_acquisition/model.rdj.json still emits
the top-level keys
["meta","params","rd_format","reactants","species","transitions","version"].

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rose

Tier 4 of the ADR 0017 migration plan: the prose layer. The code moved in the
three preceding commits; this one makes every human-readable artifact agree
with it, and adds the two glossaries the ADR mandates.

Spec (living normative documents; ADRs 0001-0016 are append-only and keep
their original wording):

- spec/CONTRACT_DRAFT.md — amended in place through §1-§15: place/marking/arc
  throughout, plus a new top-level **Glossary** carrying ADR 0017's term
  dictionary (which Petri-net family answers which question about the engine),
  the token-vs-language-model disambiguation, the retired-name table, and the
  two-register rule. Also corrects code names the rename moved:
  free_blocked_species! -> free_blocked_places!, extract_reactants ->
  extract_arcs, species_modalities -> place_modalities, add_species! ->
  add_place!, set_species! -> set_place!, get_species -> get_place,
  ArcSpec.species -> ArcSpec.place, and §7.2/J3's now-stale claim that the
  join reflection loop filters the substring "spec" (it filters "place").
- spec/INVENTORY.md — current-source map resynced (store field .arcs, the
  ArcSpec record, populate_arcs!, the ":S places" object, the "place"/"trans"
  reflection loops).
- spec/STATUS.md, spec/DOCS_CHARTER.md, spec/PR_DRAFT.md, spec/adr/README.md,
  CLAUDE.md — vocabulary resynced.

Docs site:

- NEW docs/src/glossary.md, wired into docs/make.jl as a top-level nav page:
  core vocabulary, the literature-mapping table, the renamed-in-v0.3 table
  (every @ref target has a @docs entry), the two-register rule, and the
  sentence disambiguating a Petri-net token from a language-model token.
- docs/src/index.md, readme.md and the reference pages adopt the register the
  ADR asks for: tutorial and case-study prose says "resource pool", glossed
  once at first use as "a place, in Petri-net terms"; the API reference and
  the spec say place/marking/arc.
- docs/src/reference/json_schema.md gains an explicit admonition that the WIRE
  keys still read species[]/reactants[]/set_species, so the page does not look
  like an oversight before the Tier 3 commit lands.

Demos: prose in demo/** resynced (the demo tours are the most-read runnable
code, so the ADR calls their rename the highest-value part of Tier 4).

Also folded in here, as Tier 5 stragglers the earlier word-boundary sweep
could not see (they are underscore-joined or single-letter, so \bspecies\b
never matched): species_ixs -> place_ixs (operators/equalize.jl),
species_from/species_to -> place_from/place_to (solvers.jl @Move), plain_species
-> plain_place, the `sp` locals -> `pl` across serialize.jl / visualize.jl /
ledger.jl / interface/agents.jl / operators/refine.jl, and "Species record" /
"Species trajectories" comment prose. NB ledger.jl's attribute_valuation! and
agents.jl's add_structured_token! were left half-renamed by the Tier 5 commit
(`pl = get_place(...)` then `sp === nothing`) — those would have been
UndefVarErrors on an uncovered path; both are fixed here.

Wire-bound strings are deliberately untouched and still read species[] /
reactants[] / set_species / Ref{species} / population[].species — they move in
the final Tier 3 commit.

Suite: 808 pass / 0 fail / 0 broken (unchanged). Runic clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…t the draft

The ADR was a draft written before any code moved, so implementing it turned
up things it got wrong. Corrected in place, with the wrong figure kept visible
next to the right one where a reader might have quoted it:

- Evidence table: `docs/src/` was never 97 — that is the `docs/src/` +
  `docs/literate/` total, double-counted (31 + 66). `spec/` reads 411 on the
  branch base, not 438, because ADR 0016 and the deck are not on `main`.
  Re-measured with a command that works; the drafted 448/177/154/66 hold.
- Tier 1: `@add_species` lives in `src/interface/update.jl:3,243`, not
  `create.jl`. `ReactantSpec` is at `:135`, `reactant_specs` at `:183`, the
  shim pattern at `:172,445-453`.
- Tier 2: `specCost` is 21 sites, not 20 (the 206 total is right), and the
  draft missed `src/interface/update.jl:191`, where the column symbol is
  CONSTRUCTED — `Symbol(:spec, uppercasefirst(…))` from `@cost`/`@reward`/
  `@valuation`. Missing it breaks those three macros with no compile error.
- Tier 1b, new: the unexported names the grep gate forced (`get_species`,
  `set_species!`, `populate_reactant_specs!`), the six struct fields that moved
  with no shim, and `@aka`'s `:S => :species` legacy alias.
- Tier 3, rewritten: the draft's "16 sites" undercounts the wire surface, and
  its own mandate to emit `arcs[$i].place` diagnostics is incoherent unless the
  array those paths index is renamed too. The full table now names all eleven
  wire-visible items, including the ones it omitted (`"reactants"`, the per-arc
  and population `"species"` field, `REF_KINDS`, both `src/export.jl` keys, the
  two result-frame columns, the `@select`/`@advance` field).
- Tier 5: records the trap that cost the most here — `\bspecies\b` cannot see
  `species_ixs` or `free_blocked_species`, because `_` is a word character.

Per the maintainer's 2026-08-22 resolution (no saved models outside this
repository), Tier 3 drops the permanent read alias the draft proposed: the
writer emits only new keys, the reader takes the legacy spellings for ONE
release with a depwarn, and both shim sets retire together. Consequences drops
the forward/backward-asymmetry worry for the same reason, and gains the one
observable output change of the whole rename — an aliasless `@join` block now
generates `shared_place_N`, not `shared_species_N`.

The README status table gains a vocabulary-lineage note, so a reader of ADRs
0001–0015 knows to read `species` as *place*.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…trings

Three leftovers the word-boundary sweep could not see. `specmap` is the
rename map both composition operators build (`@join`'s `X -> parent__X`,
`equalize!`'s alias collapse) — a lowercase `spec` glued to `map`, so it is
invisible to `\bspecies\b` AND to the case-sensitive `spec[A-Z]` gate. It is
a map of PLACE names, so: `placemap`. Local variables and one function
parameter; no caller outside these two files.

The AA coupling docstrings still shouted SPECIES at the reader in the two
places that describe what `observables`/`getobservable` expose. A place's
exported value is its marking, so that is what it now says.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… the retired ones for one release

The vocabulary rename reaches the wire format last and on its own commit, so a
document written against the previous release is a single, isolated diff to
bisect against. The writer now emits only the Petri-net spellings; the loader
still accepts every retired one for ONE release, with a deprecation warning.

Renamed on the wire (`species` → `place` throughout):

  - the top-level arrays `species[]` → `places[]` and `reactants[]` → `arcs[]`
  - an arc's place key `species` → `place`
  - a `population[]` entry's `species` → `place`
  - a `ref` node's kind value `"species"` → `"place"` (`REF_KINDS`)
  - the marking-write action verb `set_species` → `set_marking`
  - the export-bundle manifest key `species` → `places`, and a per-token
    trajectory record's `species` → `place` (CONTRACT §14.3)
  - the result-frame / ledger `DataFrame` column `:species` → `:place` (§14.1)

The read shim is three small helpers in `src/serialize.jl` — `_legacy_key`,
`_legacy_path` and `_ref_kind` — plus a legacy branch in `stmt_from_dict` and
`_LEGACY_ACTION_VERBS` in `src/actions.jl`. `validate` reports its diagnostic
paths in whichever spelling the document actually uses, so a legacy document's
diagnostics still point at real keys. `Base.depwarn` is `maxlog = 1` per call
site and a no-op unless `--depwarn=yes`, which is the same contract the Tier-1
name shims carry: one warning per session, not one per key.

Two aliases are accepted SILENTLY rather than with a warning, because both are
read inside the step loop and a `depwarn` there would fire once per token per
tick: the `@select`/`@advance` field name `:species` (`src/predicates.jl`) and
the `SetField` target in `src/solvers.jl`.

Also regenerated the one on-disk fixture (`demo/bd_acquisition/model.rdj.json`)
and reframed the former legacy-key test as an explicit deprecation test that
loads a retired-key document, loads its fully-renamed twin, and asserts the two
simulate to an identical `sol` — then that re-exporting the legacy one emits
only the new keys. That test branches on `Base.JLOptions().depwarn` so it is
correct under `--depwarn=no|yes|error`.

Suite 821 pass / 0 fail / 0 broken / 0 skipped (801 at the branch base
`c671875`; +13 here are the new deprecation testset, +7 landed with Tier 5).
Runic clean.

Co-Authored-By: Claude Opus 5 <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.

1 participant