Skip to content

Commit 404b65c

Browse files
committed
docs(rfc): record the two RFC-compliance gaps this review exposed
Two mechanisms, both found by independent review rather than by a gate. A false RFC claim propagated unchecked because 55 of 162 enrolled RFCs have no full text under rfc/full/ -- including rfc7854 and rfc9069, the two this work was done under. rfc_requirements.py already knows it cannot judge them ("no source text under `rfc/full/` -- cannot judge"), and ze-rfc.md step 2 says to read a file that for a third of enrolled RFCs has never existed. So ze-rfc-check validates those summaries against themselves. That permits a requirement the RFC does not contain, and -- the dangerous one, because the ledger looks complete -- a requirement it does contain that was never extracted. Three tagged-test authorisations remain outstanding and are recorded rather than worked around: the BMP ReplayID stamp (TestStartLocRIBTriggersInitialDump asserts replay.Broadcast specifically, which is over-specified against the obligation), the BMP mixed-family End-of-RIB, and the reactor AIGP case inside orderCases(). The hook blocked each and no agent self-authorised.
1 parent 9c39807 commit 404b65c

8 files changed

Lines changed: 521 additions & 111 deletions

ai/CODE-TO-DOCS.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<!-- GENERATED by scripts/dev/code_to_docs.py -- do not edit -->
44
<!-- Regenerate: make ze-doc-index -->
55

6-
Total: 1515 code paths referenced from docs/
6+
Total: 1516 code paths referenced from docs/
77

88
## `Makefile/`
99

@@ -403,11 +403,12 @@ Files: 15 | Docs: `docs/architecture/api/text-format.md`, `docs/architecture/beh
403403

404404
## `internal/component/bgp/plugins/bmp/`
405405

406-
Files: 9 | Docs: `docs/architecture/api/commands.md`, `docs/features.md`, `docs/features/rfc-status.md`, `docs/functional-tests.md`, `docs/guide/bmp.md`, `docs/guide/command-reference.md`, `docs/guide/configuration.md`, `docs/guide/plugins.md`
406+
Files: 10 | Docs: `docs/architecture/api/commands.md`, `docs/features.md`, `docs/features/rfc-status.md`, `docs/functional-tests.md`, `docs/guide/bmp.md`, `docs/guide/command-reference.md`, `docs/guide/configuration.md`, `docs/guide/plugins.md`
407407

408408
| File | Docs |
409409
|------|------|
410410
| `bmp.go` | `docs/guide/bmp.md` |
411+
| `bmp_events.go` | `docs/guide/bmp.md` |
411412
| `bmp_locrib.go` | `docs/guide/bmp.md` |
412413
| `cmd_show.go` | `docs/architecture/api/commands.md`, `docs/guide/command-reference.md` |
413414
| `fuzz_test.go` | `docs/functional-tests.md` |
@@ -730,7 +731,7 @@ Files: 6 | Docs: `docs/DESIGN.md`, `docs/architecture/meta/role.md`, `docs/archi
730731
| `` | `docs/guide/bgp-role.md` |
731732
| `config.go` | `docs/architecture/meta/role.md` |
732733
| `metrics.go` | `docs/architecture/meta/role.md`, `docs/plugin-development/metrics.md` |
733-
| `otc.go` | `docs/architecture/meta/role.md`, `docs/architecture/route-selection.md`, `docs/plugin-development/metrics.md` |
734+
| `otc.go` | `docs/architecture/meta/role.md`, `docs/architecture/route-selection.md`, `docs/guide/bgp-role.md`, `docs/plugin-development/metrics.md` |
734735
| `register.go` | `docs/DESIGN.md`, `docs/features/plugins.md`, `docs/guide/bgp-role.md`, `docs/guide/plugins.md` |
735736
| `role.go` | `docs/architecture/meta/role.md` |
736737

ai/INDEX.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ artifact type. Check them whenever your work touches the described concern.
193193
| `learned_numbers.py` | `scripts/dev/` | Keep `plan/learned/NNN-*.md` numbering sound: no two summaries share a number, and each H1 number matches its filename. `learned-next` allocates max(existing prefixes)+1 against the local tree only, so parallel branches collide and only a merge or rebase reveals it. `--check` (gate: `make ze-learned-numbers-check`, folded into `make ze-doc-test` and `ze-regen-check`); `--fix` (`make ze-learned-numbers-fix`) keeps the most-referenced summary at the contested number, renumbers the rest above the highest, and rewrites references. Run after any merge/rebase touching `plan/learned/`. |
194194
| `digest_check.py` | `scripts/dev/` | Validate the `file:line` anchors in `ai/digests/*.md`: each resolves to a real file (subsystem-relative via the digest's `<!-- digest-base: -->` header) and an in-range line. Keeps the hand-maintained flow digests honest as code moves. Gate: `make ze-digest-check`, folded into `make ze-doc-test`. |
195195
| `spec-closure-check.py` | `scripts/dev/` | Detect specs implemented but never closed. `--list` shows the backlog in two tiers (high-confidence vs NEEDS VERIFICATION); `--spec <s>` exits 3 only for high-confidence (committed `plan/learned/NNN-<slug>.md` whose slug exactly equals the spec stem, spec `in-progress`, not an umbrella). Backs the Stop-hook closure gate. See `ai/rules/planning.md` "Closure Enforcement". |
196+
| `ci_observer_recover_check.py` | `scripts/dev/` | Guard the `.ci` observer fail-closed property: no engine-touching call may sit inside a **recovering** `except` handler, because an exception unwinding through the observer's `finally` shutdown lands its sentinel on a stderr nothing relays -- the ordering defect that let real RPC errors pass as green in 332 of 346 observer files. The flagged call set is DERIVED (transitive closure over `ze_api.py` to `_call_engine`/`wait_for_shutdown`), so a new engine-touching helper is covered the day it is written. Its Go test runs the real scan and asserts zero, so `make ze-unit-test` enforces it with no make target to forget. See `ai/rules/testing.md` "Observer-Exit Antipattern". |
196197
| `go_extract.go` | `scripts/dev/` | Move Go symbols between files |
197198
| `replace.py` | `scripts/dev/` | Bulk find-and-replace with diff preview (run without `--apply` to review, then `--apply` to write). Supports `--regex` and `--all`. |
198199
| `yang_move.py` | `scripts/dev/` | Format-aware YANG path refactoring. When YANG nodes move, updates slash paths, set commands, brace blocks, and GetContainer chains across the codebase. `remove <seg> --under <path>`, `rename <old> <new> --under <path>`, `move <src> <dst>`. Preview by default, `--apply` to write. Run `--test` for self-tests. |

ai/RFC-REQUIREMENTS.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3656,8 +3656,8 @@ An RFC is **enrolled** (`rfc/enrolled.txt`) when every MUST-level requirement it
36563656
| `RFC7854-x-13` | SHOULD | x | -- | -- | |
36573657
| `RFC7854-x-14` | SHOULD | x | -- | -- | |
36583658
| `RFC7854-x-15` | SHOULD | x | -- | -- | |
3659-
| `RFC7854-x-16` | SHOULD | x | `internal/component/bgp/plugins/bmp/bmp_reconnect_test.go:404` | -- | |
3660-
| `RFC7854-x-17` | SHOULD | x | `internal/component/bgp/plugins/bmp/bmp_reconnect_test.go:407` | -- | |
3659+
| `RFC7854-x-16` | SHOULD | x | `internal/component/bgp/plugins/bmp/bmp_reconnect_test.go:432` | -- | |
3660+
| `RFC7854-x-17` | SHOULD | x | `internal/component/bgp/plugins/bmp/bmp_reconnect_test.go:435` | -- | |
36613661
| `RFC7854-x-18` | MAY | x | -- | -- | |
36623662
| `RFC7854-x-19` | MAY | x | -- | -- | |
36633663
| `RFC7854-x-2` | MUST | x | `internal/component/bgp/plugins/bmp/header_test.go:83` | `internal/component/bgp/plugins/bmp/header_test.go:30` | |
@@ -4719,7 +4719,7 @@ An RFC is **enrolled** (`rfc/enrolled.txt`) when every MUST-level requirement it
47194719

47204720
| Requirement | Level | § | Positive test | Negative test | Note |
47214721
|---|---|---|---|---|---|
4722-
| `RFC9234-3.1-1` | MUST NOT | 3.1 | `internal/component/bgp/plugins/role/otc_test.go:664`, `internal/component/bgp/plugins/role/otc_test.go:1080` | `internal/component/bgp/plugins/role/otc_test.go:674` | |
4722+
| `RFC9234-3.1-1` | MUST NOT | 3.1 | `internal/component/bgp/plugins/role/otc_test.go:665`, `internal/component/bgp/plugins/role/otc_test.go:1082` | `internal/component/bgp/plugins/role/otc_test.go:675` | |
47234723
| `RFC9234-4-1` | SHOULD | 4 | -- | -- | |
47244724
| `RFC9234-4.1-1` | MUST | 4.1 | `internal/component/bgp/plugins/role/config_test.go:288` | `internal/component/bgp/plugins/role/config_test.go:289` | |
47254725
| `RFC9234-4.1-2` | MUST NOT | 4.1 | `internal/component/bgp/plugins/role/config_test.go:290` | -- | {single-polarity} parseRoleContainer (internal/component/bgp/plugins/role/config.go:66) reads a single import role per peer and extractRoleCapabilities (config.go:213) emits exactly one CapabilityDecl per peer, so no code path can advertise multiple Role capabilities and only the exactly-one assertion is constructible |
@@ -4729,18 +4729,18 @@ An RFC is **enrolled** (`rfc/enrolled.txt`) when every MUST-level requirement it
47294729
| `RFC9234-4.2-4` | SHOULD | 4.2 | -- | -- | |
47304730
| `RFC9234-4.2-5` | MAY | 4.2 | -- | -- | |
47314731
| `RFC9234-5-1` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:335` | `internal/component/bgp/plugins/role/otc_test.go:345` | |
4732-
| `RFC9234-5-10` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1357` | `internal/component/bgp/plugins/role/otc_test.go:967` | |
4733-
| `RFC9234-5-11` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1652` | -- | {single-polarity} checkOTCIngress (internal/component/bgp/plugins/role/otc.go:164) and OTCEgressFilter (otc.go:384) take no operator override, peerRoleConfig (config.go:17) exposes no disable flag, and the wire-bytes OTC suppression at otc.go:384 runs before and independent of the export policy, so the procedures cannot be switched off by configuration and a modifiable negative is not constructible |
4734-
| `RFC9234-5-12` | SHALL | 5 | `internal/component/bgp/plugins/role/otc_test.go:1700` | `internal/component/bgp/plugins/role/otc_test.go:346` | |
4732+
| `RFC9234-5-10` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1368`, `internal/component/bgp/plugins/role/otc_test.go:1960`, `internal/component/bgp/plugins/role/otc_test.go:2003`, `internal/component/bgp/plugins/role/otc_test.go:2043` | `internal/component/bgp/plugins/role/otc_test.go:969` | |
4733+
| `RFC9234-5-11` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1665` | -- | {single-polarity} checkOTCIngress (internal/component/bgp/plugins/role/otc.go:164) and OTCEgressFilter (otc.go:384) take no operator override, peerRoleConfig (config.go:17) exposes no disable flag, and the wire-bytes OTC suppression at otc.go:384 runs before and independent of the export policy, so the procedures cannot be switched off by configuration and a modifiable negative is not constructible |
4734+
| `RFC9234-5-12` | SHALL | 5 | `internal/component/bgp/plugins/role/otc_test.go:1713` | `internal/component/bgp/plugins/role/otc_test.go:346` | |
47354735
| `RFC9234-5-13` | NOT RECOMMENDED | 5 | -- | -- | |
4736-
| `RFC9234-5-2` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:504` | `internal/component/bgp/plugins/role/otc_test.go:496` | |
4737-
| `RFC9234-5-3` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:480` | `internal/component/bgp/plugins/role/otc_test.go:330` | |
4738-
| `RFC9234-5-4` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:966` | `internal/component/bgp/plugins/role/otc_test.go:1005` | |
4739-
| `RFC9234-5-5` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1650` | `internal/component/bgp/plugins/role/otc_test.go:1651` | |
4740-
| `RFC9234-5-6` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:1450` | `internal/component/bgp/plugins/role/otc_test.go:1430` | |
4736+
| `RFC9234-5-2` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:505` | `internal/component/bgp/plugins/role/otc_test.go:497` | |
4737+
| `RFC9234-5-3` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:481` | `internal/component/bgp/plugins/role/otc_test.go:330`, `internal/component/bgp/plugins/role/otc_test.go:1901`, `internal/component/bgp/plugins/role/otc_test.go:1929` | |
4738+
| `RFC9234-5-4` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:968`, `internal/component/bgp/plugins/role/otc_test.go:1124`, `internal/component/bgp/plugins/role/otc_test.go:1868` | `internal/component/bgp/plugins/role/otc_test.go:1007`, `internal/component/bgp/plugins/role/otc_test.go:1806`, `internal/component/bgp/plugins/role/otc_test.go:1827` | |
4739+
| `RFC9234-5-5` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1663` | `internal/component/bgp/plugins/role/otc_test.go:1664` | |
4740+
| `RFC9234-5-6` | MUST | 5 | `internal/component/bgp/plugins/role/otc_test.go:1461` | `internal/component/bgp/plugins/role/otc_test.go:1441` | |
47414741
| `RFC9234-5-7` | MUST | 5 | -- | -- | {not-applicable} ze does not operate as an AS Confederation (no confederation-identifier or member-AS config exists anywhere in config or the role plugin); the egress OTC stamp at internal/component/bgp/plugins/role/otc.go:432 uses only dest.LocalAS, so there is no confederation egress boundary at which a confederation identifier could be stamped |
47424742
| `RFC9234-5-8` | MUST NOT | 5 | -- | -- | {not-applicable} ze has no AS Confederation membership (no member-AS or confederation-identifier config); the egress OTC stamp at internal/component/bgp/plugins/role/otc.go:432 uses only dest.LocalAS, so an UPDATE never carries a member-AS OTC value at a confederation boundary |
4743-
| `RFC9234-5-9` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1324` | -- | {single-polarity} the egress OTC stamp at internal/component/bgp/plugins/role/otc.go:432 uses dest.LocalAS, the effective per-peer internet-facing local AS supplied by the reactor at internal/component/bgp/reactor/peer_forward_facts.go:133, and no code path stamps any other value, so a wrong-ASN negative is not constructible |
4743+
| `RFC9234-5-9` | MUST NOT | 5 | `internal/component/bgp/plugins/role/otc_test.go:1335` | -- | {single-polarity} the egress OTC stamp at internal/component/bgp/plugins/role/otc.go:432 uses dest.LocalAS, the effective per-peer internet-facing local AS supplied by the reactor at internal/component/bgp/reactor/peer_forward_facts.go:133, and no code path stamps any other value, so a wrong-ASN negative is not constructible |
47444744
| `RFC9234-6-1` | MUST NOT | 6 | -- | -- | {not-applicable} ze has no representation of a Complex peering relationship; parseRoleContainer (internal/component/bgp/plugins/role/config.go:66) parses one role per peer with no relationship-complexity classification, so there is no ze code path that could place a role on a complex session to guard against |
47454745
| `RFC9234-6-2` | SHOULD | 6 | -- | -- | |
47464746

ai/rules/testing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ the two wired conventions, never a bare test file plus hope:
342342

343343
| Your tool | Convention | Runs because |
344344
|-----------|-----------|--------------|
345-
| Has its own unit tests | Name them `scripts/dev/<tool>_test.py` (unittest, with `unittest.main()`) | `TestPythonUnitTests` (`scripts/dev/python_tests_test.go`) globs `*_test.py` and runs each. A new file is picked up automatically |
345+
| Has its own unit tests | Name them `<tool>_test.py` (unittest, with `unittest.main()`) and put them BESIDE the tool -- `scripts/dev/` or `test/scripts/` | `TestPythonUnitTests` (`scripts/dev/python_tests_test.go`) globs `*_test.py` under BOTH roots and runs each. A new file is picked up automatically, and each root carries its own non-empty assertion so a root that stops contributing fails loudly rather than silently covering nothing |
346346
| Wants fixture tests inside the script | Add a `--selftest` flag, then a small Go test that shells out to it | The pattern of `dep_audit.py`, `migrate_module.py`, `qemu-run.py`. See `scripts/dev/migrate_module_test.go` |
347347

348348
Both land inside `go test`, so `make ze-unit-test` covers them via `go list ./...`

0 commit comments

Comments
 (0)