Skip to content

docs(ast): document the role tables and close out the series (#1701) - #1803

Merged
dekobon merged 5 commits into
mainfrom
fix/issue-1701
Nov 4, 2026
Merged

dekobon merged 5 commits into
mainfrom
fix/issue-1701

Conversation

@dekobon

@dekobon dekobon commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Closes #1701. Closes #1690 (umbrella; the completion comment is posted after merge).

R11, the close-out of the role-table series. Five commits:

  1. refactor(roles): the four Elixir bypass-baseline entries reclassified as three documented subset exemptions (the baseline is now empty); Groovy's switch_block recorded under excludes on its Switch row; the switch-body convention named and the Java/Groovy test pins extended. Zero drift: the one non-comment, non-test change is the excludes entry (a const); drift-oracle.py over the Groovy/Java/Elixir corpus and control-flow fixtures, base vs head binaries, 0 of 273 outputs differ. Six perturbations, all caught — the record is on docs(ast): document the role tables and close out #1701.
  2. docs(ast): roles/mod.rs module doc rewritten as the contributor reference, with the one "What the mechanism cannot see" list the other docs link to.
  3. docs(rules): grammar-dispatch.md §1/§4/§5/§7, macro-comments.md row-comment table, AGENTS.md layout bullet and gate paragraph.
  4. docs(book): metrics.md, new-language.md; the series' closing CHANGELOG entry.
  5. docs(lessons): lesson 115.

Seven DSL follow-ups filed outside the series: #1792–#1798.

Gates on c1506be3: make pre-commit BCA_GATE: pass; check-test-lang-gates-compare COMPARE_REF=origin/main OK; role-parity-corpus pass; census 0 pending / coverage 5,998 / bypass 0; self-scan-baseline-fresh clean. Reviews: Astra ×3 on the implementation, a prose review, an architecture audit; details on #1701.

…byte-less space table

The role-bypass baseline held four pre-migration sites: Elixir's
byte-less `is_func_space` and `get_space_kind`, which list `source` and
`anonymous_function`. That list is the ungated subset of the
`FuncOpener` and `Container` rows' owners — the one a spelling with no
bytes can answer, since the rows' third owner is the text-gated `call`
— so the sites take the lint's documented subset exemption rather than
a forward to `opens_space`: the walk's promotion calls the byte-less
checker spelling per node before its single keyword read, and a
forward would scan each `call`'s target twice more for a known `None`
(`promotion_reads_each_calls_keyword_once`). The baseline is now
header-only. What the exemption no longer sees — a kind added to
either span — the derived forward-parity test does, on every corpus
node.

Groovy's `switch_block` moves from coverage debt to `excludes` on the
`Switch` row: the owner's child, where cognitive opens its nesting
level, projected by the named `is_switch_body_kind` beside the table.
Java's twin is claimed by `HoldsDocInBody` since #1699, so the row
comment, the fn doc and the test pin no longer say no row claims it;
the pins now assert the claim (Java) and the exclusion (Groovy).

Zero drift: the only non-comment, non-test change is the `excludes`
entry, which emits a const. `drift-oracle.py` over the Groovy, Java
and Elixir corpus and control-flow fixtures, base vs head binaries:
0 of 273 outputs differ.

Part of #1701 (#1690).
The module doc of `roles/mod.rs` is what the next contributor reads
before touching a dispatch table, and it had grown by accretion across
R01–R10: the row grammar was one 46-line paragraph, the documentation
epic's section sat apart from the roles it describes, the `pending`
example named a package that has landed, and the limits of the
mechanism were a sentence pointing at the umbrella issue.

The rewrite keeps every fact and reorganises it as a reference: the
role set with its long-form entries and the doc-epic recognition set
folded in (R09's item 8); the row grammar split into owner, payer and
row attributes, with the three `pays` modes and the `mode=` canonical
field #1745 landed; what a table emits, now including `TEXT_GATED` and
the test-support `rows()` view (R10); a new "Reading a row from a
consumer" section that states the `_with_code`-first rule, the
known-empty-chain forward (lesson 114), the switch-body convention
(#1701) and the `role-bypass: allow` contract with what an exemption's
span costs; the gates and the derived suite in one place; and "What the
mechanism cannot see" — the one list, a union of the three baseline
headers, the derived suite's doc and the umbrella's four headline
items with nothing dropped, grouped by the kind of mistake and naming
the manual check that covers each. The rules, the book and lesson 115
link here rather than restating it.

Part of #1701 (#1690).
…the role tables

`grammar-dispatch.md` still opened as if every classifier were a
`match` over `kind_id()`; after R01–R10 the membership half lives on a
row and three gates see part of what the checklist asked for by hand.
The rule now says which items the gates catch and which they cannot —
and keeps every item for the second reason. §1 states precisely what
`make role-coverage` buys (a new unaccounted-for alias) and does not
(an alias omitted from its intended row, which is still debt or
another role's), so the alias inventory stays a manual check for rows
as for consumers. §4 adds the switch-body convention as the example of
a structural fact that is not a row. §5 records that declared owners
and payers are disjoint by construction and that a wrapper-and-leaf
count is a visible two-projection choice at the consumer. §7 is kept
and reframed as the manual check for what the gates cannot see, with
Elixir's exempted byte-less pair as the worked example.

`macro-comments.md` gains the row-comment placement table, citing the
module doc's rule rather than restating it. AGENTS.md gains the
`roles/` layout bullet and points the gate paragraph at the module
doc's one "cannot see" list; the bypass baseline is now described as
empty.

Part of #1701 (#1690).
The ABC conditions section and the cognitive lambda paragraph each
describe a rule over a set of constructs without saying where the set
is declared; both now name the rows (`Comparison`, `ShortCircuit`,
`Ternary`, `NullSafe`; `Closure`, `FuncOpener`) and keep the counting
rule as the page's own. The new-language chapter names the module doc
as the reference, drops the suggestion that a new table may leave
rows `pending`, and adds the control-flow fixture and observation
refresh a new language owes the derived parity suite.

The CHANGELOG carries the series' closing entry and the seven DSL
follow-ups filed outside it.

Part of #1701 (#1690).
…g the list leaves the judgement

Lesson 115, the role-table series' supported conclusion. Sharing a
rule (#420's interpolation helper, #1520's `scores_itself` predicate)
left the kind list at every caller, which is where #454, #1526 and
#1529 found their omissions; the #1690 design classified 55 of 72
review entries as the list rather than the rule, with the caveat the
design itself recorded. Sharing the list (R01–R10) made #1567's
repair three entries and made a dropped `Volatile2` fail by name,
and did not prevent #1567's omission, which was a scoping decision
the list would have carried as faithfully. The remainder — a
wrong-role claim, an incomplete entry, a forward whose dropped
context nothing reads yet, a consumer reading the wrong projection
or text-keyed policy, the shapes the bypass lint does not scan — is
named once in the `roles/mod.rs` module doc with the manual check
for each, and this lesson links there rather than carrying a copy.

Part of #1701 (#1690).

This branch was previously deployed

1 inactive deployment
github-pages — c1506be3 Deployed Nov 4, 2026 by dekobon via deploy to GitHub Pages #962
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.

docs(ast): document the role tables and close out refactor(ast): one role declaration per language for Checker, Getter and metric dispatchers

1 participant