Skip to content

mechanisms.md has no entry for "two stages, one question" — the pattern five PRs have now rediscovered #438

Description

@derek73

docs/design/mechanisms.md has no entry for a pattern this codebase has now executed five times and stated in prose twice.

The pattern

Two pipeline stages need the answer to the same question about the same input. Rather than each testing for itself, the site that does not own the decision calls the deciding stage's own predicate — so the two cannot drift.

The instances

PR / issue what was shared what drifting had cost
#319 segment's suffix-comma predicate lifted into _vocab.is_wholly_suffix the comma decision and the honorific peel's segment test disagreed
#424 _is_leading_title / _leading_titles moved down into _group group's title() missed H2's unlisted abbreviations, so Xyz. van Johnson chained where Dr. van Johnson did not
#425 the S2 peel as _peel_trailing over _peel_walk, shared by assign and P5's reserve the reserve re-derived assign's peel by hand and missed the bare-acronym fork
#401 / #421 one reads_as_suffix predicate four inexact-mirror drafts caught in review
#429 _segment_holds_no_name moved down into _group group asked by segment index while assign asked by content

The principle is already written down twice in decisions.md"so the comma decision and the honorific peel's segment test cannot drift apart" (#319) and "the reserve and the join ask the same question, and it is the one assign answers" (#401/#421) — but only ever inside a dated entry about a specific fix, where nobody looks it up.

Why an entry is worth writing

Existing mechanisms.md entries are seated on two or three instances. This one has five and keeps recurring: four consecutive PRs (#424, #425, #401/#421, #429) each rediscovered it. Naming it would make the next one a lookup rather than a discovery.

The Known-limit clause, which is the interesting half

The share is only available in the import direction. _assign imports _group, so a predicate both stages ask must live in _group. Where the earlier stage needs the later stage's answer, sharing a predicate is the only option — recording the decision on the state is structurally unavailable.

ParseState.order is the contrast and states the alternative in its own docstring: "Recorded rather than recomputed downstream, because the two can differ and a post_rules rule keyed on policy.name_order would then disagree with the roles assign already wrote (#395)." But order is recorded by assign for post_rules — a later reader. That option exists only when the reader comes after the decider.

The measured cost of the predicate form, from #429: _segment_holds_no_name now runs twice per family-comma parse, worth 1.2–2.2% of such a parse and 0% of every other. Cheap enough to accept, and worth recording in the entry so the next instance does not re-measure it.

Also worth noting in the entry

_group.py is 1037 lines and now hosts five predicates that belong to neither stage — _is_suffix_piece, _peel_walk/_peel_trailing, _leading_titles, _is_leading_title, _segment_holds_no_name — living there only because of the import direction. That is #439.

Came out of the cleanup review of #435.

Metadata

Metadata

Assignees

Labels

docsDocumentation fixes and updates

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions