RFC 0040 / 0041 implementation readiness
Decision update: the owner approved signed block-level
_roleon 2026-09-11. RFC 0040 now records the accepted contract; the findings below explain that decision. Its implementation uses Protocol 2.7.0. RFC 0041 was subsequently accepted as the bounded Protocol 2.8.0 implementation on 2026-09-12; the findings below record the original draft review.
Reviewed against 59e8912 (core 2.6.2, Protocol 2.6.0). Documentation cleanup landed in PR #177. The active implementation stage is RFC 0040; RFC 0041 follows. Neither draft has been accepted or implemented by this review.
RFC 0040: role identity and signatures
The draft's _meta.role does not survive reshapeMetaV2toV1. Even if a caller puts role in the flat in-memory meta, computeBlockHash(..., { shape: 'v2' }) normalizes it through reshapeMetaV1toV2, which drops it again. That makes the claim that role changes already change the block hash false at this baseline. The envelope/receipt digest is a separate path; this finding concerns block integrity and the block signatures built on it.
A probe of the built library confirmed:
| Observation | Result |
|---|---|
Nested _meta.role survives the parse-view reshape | No |
Changing flat meta role: senior to role: junior changes the v2 block hash | No |
Changing block-level _role: senior to _role: junior changes the v2 block hash | Yes |
The recommended revision is a block-level _role annotation, alongside _meta and _overrides. Existing v2 canonicalization retains this content, so roles can be signed without redefining the normalization of existing metadata. _meta.role must not become an alias: otherwise two spellings have different integrity behavior. The owner must approve this location change before schema implementation. If _meta.role is retained instead, first design an explicitly versioned normalization/signature compatibility policy; do not silently change the hash of pre-existing role-bearing files.
RFC 0040: decisions the implementation contract must pin
- One scalar role per block.
primaryanddetailcannot coexist in one field. A check with no matching preferred role can still selectprimary. - Filter
componentblocks before every property-level selection step, including named preferences,default,base, and sole-candidate fallback. A standalonecomponentblock must not become a property total merely because no map exists. - Resolve in this order: check-specific key preference, check-specific role,
primary,default,base, sole eligible variant. Never select by insertion order. - A duplicated role is ambiguous when that role's step is consulted; do not silently fall through to
default. Unconsulted roles do not override an earlier successful explicit choice. Repeatedcomponentroles are allowed because those blocks are excluded, not candidates for selection. - Unknown roles and malformed role values are structural errors. An invalid candidate cannot be selected as a fallback. Errors are found in all variants, even when a different candidate resolves successfully.
- Preserve existing results and coverage byte-for-byte for role-free documents. Do not add an unconditional
viafield to every old coverage entry. - Coverage is per rule, but one rule reads several sections. Add an optional
resolutionsmap keyed by section, with the selectedvariantandviafor role-bearing selections; a single top-levelviacannot explain both sides. - Audit CC-03's direct resolver calls and CC-12's direct
getSectionuse, not onlyrequireSections. Keep CC-15's lease-upbaserule unchanged. - Keep every financial comparison and tolerance unchanged. Multiple senior facilities remain ambiguous; this feature does not aggregate them.
- Writers preserve a role during ordinary edits. A trusted explicit role change must be distinct from model-authored section content; raw agent output must not be able to assign or overwrite the host's role annotation.
RFC 0041: findings to resolve next
| Draft claim | Source finding / proposed correction |
|---|---|
Year 3 can compare with 2028-Q1 and an ISO date | Its own equality rule says different kinds never compare. Preserve absolute calendar identities; defer date-to-holding-year conversions until a calendar contract exists. |
| Calendar key is a row's 1-based position | That conflates two unrelated schedules' first months and relies on validation/order. Use an absolute calendar month/quarter key. If ordinal Qn/Mn aliases remain, specify their anchor and gap behavior separately. |
Add @ handling to parser.ts deepGet | Calc AST paths walk getPathSegment in calc/evaluator.ts; they do not call deepGet. Lexer/parser, AST traversal, override keys, dependency extraction, and Excel emission all need explicit treatment. |
deepGet can look up a merged period registry | Its API is deepGet(obj, path) and has no parsed-file or registry context. Preserve the generic helper and add a context-aware period resolver, rather than silently repurposing every data lookup. |
| A first matching row resolves duplicate periods | That is order-dependent despite PS-02. Evaluation must refuse an ambiguous match, even when a caller skips validation. |
| A variant map follows the existing cross-check resolver | Calc uses getSection, which refuses maps; cross-check resolution is private to the validator. Declare a calc-specific section-selection contract, with explicit variant context, instead of importing a check's senior preference. |
| Module registration needs no runtime change unless used | No manifest field or merged-registry API is specified. Define schema/runtime validation, namespace ownership, collision refusal, and context propagation before promising module registration. |
@ can be globally reserved in all existing path strings | Generic deepGet currently traverses arbitrary object keys, while calc grammar is narrower. Limit new selector syntax to declared expression/reference surfaces; preserve literal-key lookup elsewhere. |
| Only PS-01 and PS-02 are introduced | The draft also defines PS-03; tables, summary, exported registries, and fixtures must agree. |
| Both RFCs target Protocol 2.7.0 | Reconcile the actual landed protocol before each stage. If 0040 lands as 2.7.0, a later separate 0041 minor is 2.8.0; package versions remain independently selected at release time. |
Recommended first implementation of 0041: explicit Y<n> selectors on year series and absolute calendar/date selectors on the five standard series, with no implied calendar conversions. Defer relative Q<n>/M<n> aliases, wildcards, and module-defined series until their separate contracts are pinned. This is a scope proposal for the next stage, not approval to alter the current protocol.
Acceptance evidence required
RFC 0040 must prove role-driven selection, duplicate refusal, component exclusion, unchanged role-free behavior, writer preservation/authority, and signed role tamper detection in both metadata formats and supported representations. RFC 0041 must additionally prove row-order independence, duplicate refusal without prevalidation, calendar identity, missing-period behavior, explicit variant scope, safe segment traversal, override precedence, dependency extraction, and an honest Excel emission refusal or supported translation for every new AST shape.