OpenWOP openwop.dev
RFC details authors, dates, what it affects, review history
FieldValue
RFC0174
Titlev2 governance: the six RFC states are reachable and used (supersession flips the superseded RFC in the same PR; rejections are filed; a number is never reserved without a file); Active → Accepted is a machine predicate over the bundle and the registers; waiver authority is checked at merge against the RFC's declared class and a rule takes effect on the RFC that introduces it; register rows are typed data everywhere (the 27 stray register pairs move under registers/; a self-referential carried: is refused); document Status: banners are derived from the owning RFC; the RFC 0158 durability ladder is the maturity template; the front door is the core (spec/v2/core/ under 25,000 words, the tail in ext/ with a witness class); "Open spec gaps" tables retire into gaps.json; the host-inventory deprecation rule is restated normatively
StatusAccepted
Author(s)David Tufts (@davidscotttufts)
Created2026-09-03
Updated2026-09-03 (Draft → Active in the filing PR. Comment window waived under GOVERNANCE.md §"Sole-steward operation" and logged in MAINTAINERS.md; RFC 0001 §5 cross-org rule not yet active; RFC 0147 §A.6 overridden and named in the parent, RFC 0167. This RFC changes the decision rule, which GOVERNANCE.md §"Amendments" says needs two maintainer approvals and an RFC: one maintainer exists, so the approval count is waived under the same sole-steward paragraph and recorded — the first rule this RFC introduces (§B.2) is tested against this very waiver. Adversarial review recorded below.) · 2026-09-03 (filed). Evidence tier: corpus gate — no host tier is claimed at the release candidate (GOVERNANCE.md §"Acceptance evidence tiers"; the openwop-app bundle is a Phase 4 entry item). · §E.2a (budget rule made explicit) 2026-09-17 · §B.1 (cross-RFC citation admitted) 2026-09-18 — editorial under §A.4: both gates already resolve a requirement id by string lookup without comparing it to the citing RFC's number, and twelve rows already rely on that, so this states existing behaviour and adds no obligation.
AffectsPart of: RFC 0167 — child C7. v1.x process (this PR, non-wire): RFCS/0001-rfc-process.md §3 (supersession and rejection rules), §5 (Accepted predicate pointer); GOVERNANCE.md §"Sole-steward operation" (approval-count waiver), §"Amendments"; RFCS/README.md §"Companion gap & risk registers" (location rule matches the tree); NEW scripts/check-rfc-status-coherence.mjs (document Status: banners vs owning RFC; self-referential carries; register location); scripts/check-registers.mjs (refuse carried:<self> on a terminal RFC); NEW scripts/check-waiver-authority.mjs; docs/RETROSPECTIVE-QUEUE.md; the 27 stray register pairs moved; 11 stale document banners corrected. v2 (Phase 3): spec/v2/core/ budget gate; ext/ header rule; gaps.json absorbs the 43 prose gap tables
Compatibilitybreaking (v2) as an RFC 0167 child; the v1.x items in this PR are process and corpus-hygiene changes — no wire artifact. Moving register files and correcting stale Status: banners are editorial; the two new checks and the carried:<self> refusal are gates (a gate that bites is a gate, so the first run is recorded, not hidden)
Supersedes— (amends RFC 0001 §3/§5; RFC 0155 §D residue and RFC 0156 G3/R4 are dispositioned)
Superseded by—

Summary

164 RFCs and zero Superseded, Withdrawn, or Rejected; an Active → Accepted rule that lives in a README sentence with no predicate; a waiver ledger that checks presence and not authority; 27 register pairs outside the directory the README says they live in; a gap row that carries itself and passes; eleven Status: Draft documents whose graduation predicate already fired, one of which forbids what its own RFC permits; 43 prose gap tables that the one gap namespace never ingests; a 225,858-word corpus with an 854-word front door that says "read this instead". v2 governance makes each of these a rule with a gate: states are used, Accepted is computed, authority is checked, registers are data in one place, banners are derived, the core is bounded, and the gap tables become rows.

Motivation

  • Terminal states are unreachable in practice. RFC 0001 §3 lists six states; three have never been used (RFCS/*.md status grep: 0). Corrections happen by amendment-in-place, strikethrough, and renumbering (RFC 0158's own header records a 0162 → 0158 renumber).
  • No Accepted predicate. RFC 0001 §6 mentions "a checklist"; RFCS/README.md:24 says "once the implementation lands"; GOVERNANCE.md:70–73 requires a tier and a bundle. Nothing computes it. spec/v1/self-hosted-runner.md:3 still says "RFC 0122 is Active, not Accepted: no host may advertise" while RFCS/README.md:253 records RFC 0122 Accepted with "Hosts MAY now advertise" — the document forbids what the RFC permits. Four of the eleven Status: Draft documents cite a graduation predicate that has already fired (RFCs 0097, 0098, 0117, 0122 are Accepted).
  • Waivers are audited for presence, not authority. check-waiver-ledger.mjs derives the waived set from the literal comment window waived and checks a ledger row exists; RFC 0147 §A.6 forbade waiving high-risk windows and was itself waived, and docs/ASSURANCE-STATUS.md:15 counts 48 waived RFCs with 0 ratified retrospectives.
  • Registers are half data. Tokens and gaps.json landed (RFC 0166), but RFCS/README.md:102 says registers live under registers/ while 27 pairs (0106–0143) sit in RFCS/ root; RFCS/0106-realtime-voice-session-profile.gaps.md:17 reads "CLOSED" in prose and carries carried:openwop.gap.0106.7 — itself — as its token, which check-registers.mjs accepts. 43 spec/v1/*.md "Open spec gaps" tables are a second gap namespace generate-gaps.mjs never reads; several rows there defer to an Active → Accepted whose RFC is already Accepted (agent-deployment.md:94, agent-roster.md:61, agent-org-chart.md:64); artifact-type-packs.md:195 says a test does not exist that does.
  • The front door is a footnote. docs/IMPLEMENT-CORE.md (854 words) says "read this instead of the corpus"; the two largest documents are a test-seam manual (16,021 words) and host-capabilities.md (14,642). /v1/goals, /v1/export, /v1/import are in Accepted RFCs, published schemas, and a security invariant, and absent from api/openapi.yaml.
  • Residue. RFC 0155 §D's budget is repealed (RFC 0166 §C.3) but its UQ1 and an acceptance checkbox still ask whether 12/4 is right. RFC 0156 R4 is open with the queue published and no reviewer.

Proposal

§A. States are used

§A.1 A superseding RFC MUST flip the superseded RFC's Status to Superseded and add the forward pointer in the same PR; check-rfc-status-coherence.mjs fails a tree where an RFC names another in Supersedes while that RFC is not Superseded. §A.2 A declined proposal is filed as Rejected with the reason in Updated (RFC 0166 §D.1); a proposal discussed in an issue or a plan and not filed is not a decision. §A.3 A number is never reserved without a file; a renumber is a Withdrawn file at the old number with a forward pointer, never a hole (RFC 0001 §3 already says numbers are not reused; the hole at 0160–0162 is recorded as G1 and left, because filing three Withdrawn stubs after the fact would be theatre). §A.4 Amendment-in-place remains legal for editorial corrections and for register sweeps; a normative change to an Accepted RFC is a new RFC that Supersedes the section, never an edit.

§B. Accepted is a predicate; authority is checked

§B.1 Active → Accepted is computed by scripts/check-accepted-predicate.mjs (Phase 3 for v2 RFCs; v1.x RFCs are grandfathered as recorded): every acceptance-criteria box ticked or carrying a stated reason (the existing rfc-lifecycle-coherence leg); no open gap row (RFC 0166); a tier named in Updated (GOVERNANCE §"Acceptance evidence tiers"); a cited bundle ACCOUNTING for each requirement id the RFC's falsifiability table names (RFC 0148) — either at least one executed-pass row, or, when the row's verdict cell declares a non-executable witness class (seam-gated, negative-existence, unwitnessable, or witnessable — gated naming the gate), a non-pass row on a certified bundle carrying a reason. The second branch exists because the first alone refuses a corpus that did everything right: RFC 0177 §E.4's form-when-reuses-edge-conditions is recorded inapplicable with the reason “profile not advertised in the captured discovery set”, check-cut-gates.mjs §G.2 accepts exactly that, and a rule demanding executed-pass regardless leaves the honest move and the checkable move in conflict — whose only resolution is to mint a vacuous leg, the very defect this predicate exists to refuse. What the second branch never admits is SILENCE: the suite MUST have reached the leg and recorded why. A falsifiability row MAY name a requirement id minted under another RFC's number — twelve rows already do, this RFC's own included — when the row asserts the SAME requirement that id already witnesses, and the Observable cell names the owning RFC or the gate they share. Ownership does not move: the id stays minted and maintained under the RFC whose number it carries, and a citing RFC MUST NOT restate or narrow its meaning. What a cross-citation never admits is a NEW normative requirement borrowing an old witness — if the citing row states a rule the cited id's assertion does not exercise, the row is unwitnessed and needs its own id, however much gate machinery the two share. Nor may a row cite an id whose owning RFC is not Active or Accepted, or one carrying no executed-pass row of its own: a citation is never stronger than the evidence it points at, and a chain of citations to nothing is the vacuous pass with extra steps. For a program child, the parent Active — and the parent flips LAST, since a parent that is Accepted is no longer Active and would otherwise strand every child still awaiting its own flip. A flip that fails the predicate fails the gate.

§B.2 Waiver authority is checked at merge: scripts/check-waiver-authority.mjs reads each waived RFC's Compatibility class and the rules it names as overridden, and fails when (a) a safety-fix RFC waives the §3 90-day window without an embargo citation, (b) a high-risk RFC (RFC 0147 §A.6 surfaces) waives its window without naming §A.6 as overridden in the header, or (c) an RFC amends the decision rule without naming the approval-count waiver. A rule takes effect on the RFC that introduces it: this RFC is the first tested.

§B.3 GOVERNANCE.md §"Sole-steward operation" gains one sentence: the two-approval requirement in §"Amendments" is waived and recorded while one maintainer exists, with the same retirement condition as the window waiver.

§B.4 The host-inventory deprecation rule (COMPATIBILITY.md §5, 2026-09-02) is restated normatively: v1 support ends at the later of every INTEROP-MATRIX host's non-vacuous v2 bundle plus 90 days, and 18 months from the v2 release if and only if an independent host is in the matrix at release. Phase 5 computes the date from the matrix; nothing else may.

§C. Registers are data, in one place

§C.1 Every register pair lives under RFCS/registers/; the 27 stray pairs move in this PR (git mv, no content change); check-rfc-status-coherence.mjs fails a register outside the directory. §C.2 A carried:<own id> on a terminal-status RFC is the RFC 0166 backfill's default and 388 rows carried it at filing; refusing them outright would re-adjudicate 36 registers in one PR. Instead: the count is a ratchet (docs/witness-baseline.json selfCarried, may only fall; check-rfc-status-coherence.mjs), a self-carry whose own prose says CLOSED is refused (machine and prose must agree — 45 such rows, RFC 0106 G7 among them, are re-tokened closed in this PR), and in v2 a carry MUST name a different open row or a tracked surface (gaps.schema.json's "the gap's own id" target is withdrawn for v2 RFCs). §C.3 RFC 0155 UQ1 and the unticked budget checkbox are closed with a pointer to RFC 0166 §C.3. §C.4 RFC 0156 R4 stays open with the honest note that the queue is published and no second organization exists; this RFC does not pretend otherwise.

§D. Banners are derived

§D.1 A spec/v1/*.md Status: banner MUST agree with its owning RFC: a document whose graduation predicate names an RFC that is Accepted MUST NOT read Draft on that ground. check-rfc-status-coherence.mjs parses the banner's RFC NNNN references and fails on Draft … graduates when RFC NNNN reaches Accepted where NNNN is Accepted. The eleven banners are corrected in this PR (four graduate to Stable; self-hosted-runner.md:3 and :117 are corrected to permit what RFC 0122 permits; the rest keep Draft with a predicate that has not fired). §D.2 A gap-table row that defers to Active → Accepted MUST name the RFC; the check fails a deferral whose RFC is Accepted. The stale rows at agent-deployment.md:94, agent-roster.md:61, agent-org-chart.md:64, artifact-type-packs.md:195, and host-capabilities.md:2110 are corrected here.

§E. Maturity template, front door, gap tables

§E.1 RFC 0158's ladder (durable-single-instance → multi-instance → multi-region-qualified, each rung named with its evidence, no discovery field) is the template for every per-host maturity claim in v2: a rung is evidence, never an advertisement. §E.2 spec/v2/core/ is under 25,000 words (check-core-budget.mjs, Phase 3). §E.2a (2026-09-17). The ceiling stays at 25,000 and the rule already practised is made explicit: core prose carries the MUSTs; rationale, measurement and history live in schema description / $comment fields, which the budget does not count. Three core MUSTs landed in one week at 24,999 of 25,000 by moving their arguments into ids.schema.json, run-event-payloads.schema.json and suspend-request.schema.json; that is the forcing function working, and raising the ceiling would remove it. Two guards follow from the near-miss it also produced — one compression briefly changed MUST NOT rewrite era-2 rows in place to MUST NOT rewrite era-2 rows before review caught it: (i) a budget trim MUST NOT alter a normative clause — diff the RFC 2119 sentences of the edited section before and after, and pay from connectives, parentheticals and restatements only; (ii) a paragraph that duplicates a schema description is deleted in favour of the schema, not compressed. Raising the ceiling was considered and rejected: 16 core RFCs have landed against it, and the near-miss came from hurry, not from the number. Every spec/v2/ext/<name>/ document declares witness and both maturity axes in its header; a MUST with witness: unwitnessable may not appear in core/; a document whose only witness is "deferred to Active → Accepted" enters ext/ or is deleted. /v1/goals, /v1/export, /v1/import enter api/v2/openapi.yaml or their RFCs' MUSTs are demoted — decided in C.1 with the seams profile. §E.3 The 43 "Open spec gaps" tables are absorbed into gaps.json in Phase 3 (generate-gaps.mjs gains spec/v2/** as a source with a 2-column adapter) and deleted from prose; a gap citing an artifact that contradicts it fails (C.11).

Migration table

RowKindv1v2CodemodPersisted data
openwop.migration.C7.1behaviorprose "Open spec gaps" tables in 43 documentsrows in gaps.json— (a generator adapter, not a consumer artifact)not-persisted
openwop.migration.C7.2behaviorActive → Accepted by README sentencecheck-accepted-predicate.mjs—not-persisted
openwop.migration.C7.3behaviorregisters in two directories; carried:<self> unboundedone directory; self-carry ratcheted and refused where the prose says CLOSED—not-persisted
openwop.migration.C7.4behaviorhand-written document Status: bannersderived from the owning RFC—not-persisted

Persisted-data disposition

No persisted host data. Corpus files only: 54 register files move (history preserved by git mv); eleven banners and five gap rows are corrected.

Compatibility

breaking (v2) as a child; the v1.x items are process and hygiene: no schema, OpenAPI, AsyncAPI, or prose MUST on the wire changes. Correcting self-hosted-runner.md:3 relaxes a prose prohibition ("no host may advertise") — that prohibition contradicted its own Accepted RFC and RFCS/README.md:253; the correction restores the RFC's decision and is recorded as a Class-3 editorial correction under COMPATIBILITY §3's precedent list, not as a relaxed MUST.

Conformance

Corpus gates, not host scenarios: check-rfc-status-coherence.mjs (supersession pairs, register location, banner-vs-RFC, stale deferrals), check-registers.mjs (self-carry refusal), check-waiver-authority.mjs. Phase 3: check-accepted-predicate.mjs, check-core-budget.mjs, the gaps.json prose adapter.

Falsifiability — one row per normative requirement

RequirementObservableWho can cause the conditionVerdict
§A.1 supersession flips in the same PRcheck-rfc-status-coherence.mjsthe corpus gatewitnessable — unaided (corpus)
§B.1 Accepted predicatecheck-accepted-predicate.mjs (Phase 3)the corpus gatewitnessable — unaided (corpus)
§B.2 waiver authoritycheck-waiver-authority.mjsthe corpus gate; this RFC is the first subjectwitnessable — unaided (corpus)
§C.1 register locationcheck-rfc-status-coherence.mjsthe corpus gatewitnessable — unaided (corpus)
§C.2 self-carry ratchet; CLOSED-prose self-carry refusedcheck-rfc-status-coherence.mjsthe corpus gatewitnessable — unaided (corpus)
§D.1 banner agrees with the RFCcheck-rfc-status-coherence.mjs — openwop.requirement.0172.release-identitythe corpus gatewitnessable — unaided (corpus)
§E.2 core budget; ext/ headerscheck-core-budget.mjs (Phase 3)the corpus gatewitnessable — unaided (corpus)
§B.4 the v1 end date is computed from the matrixPhase 5 scriptthe stewardwitnessable — gated on the matrix

Adversarial review

  1. This RFC changes the decision rule and GOVERNANCE §"Amendments" demands two approvals an RFC cannot supply with one maintainer. Disposition: named in the header; §B.3 records the approval-count waiver under the sole-steward paragraph with the same retirement condition; §B.2's checker treats an unrecorded approval waiver as a failure, so the rule bites its own introducer first.
  2. Refusing carried:<self> on terminal RFCs would fail 388 existing rows — the RFC 0166 backfill's own default. Disposition: a ratchet plus a contradiction rule, not a blanket refusal; the 35 rows whose prose already said CLOSED are re-tokened here; the remaining 343 fall as registers are swept (G2 records the baseline).
  3. Moving 54 register files breaks sources[].file in gaps.json. Disposition: generate-gaps.mjs --write regenerates the source paths; spec/v1/gaps.json ids are (rfc, local)-derived and do not change.
  4. Relaxing "no host may advertise selfHostedRunner" is a prose MUST relaxed (§2.2). Disposition: the prohibition was a document contradicting its own Accepted RFC; restoring the RFC's decision is a correction, recorded under the §3 precedent list, and the register row says so.
  5. Deriving banners from RFC status hides a document that is genuinely still draft after its RFC is Accepted. Disposition: the check only fails a banner whose stated predicate has fired; a document may stay Draft with a different, unfired predicate.
  6. The Accepted predicate needs bundle ledger rows per requirement id, and no gaps.json row has a requirementId today (558 of 558 null). Disposition: that is C.11's row-binding work; §B.1 is a Phase 3 gate for v2 RFCs and does not re-adjudicate the 158 Accepted v1 RFCs.
  7. The hole at 0160–0162. Disposition: left as a recorded gap; filing Withdrawn stubs for numbers never authored would be a paper trail for nothing.

Alternatives considered

  1. Keep Accepted as a maintainer judgement with a checklist. Rejected: the checklist is unenforced today and the stale banners are the result.
  2. Add a Status: field to every document that the generator writes. Rejected for v1 (a rewrite of 60 headers); adopted for v2 in ext/ headers (§E.2).
  3. Leave the prose gap tables and index them. Rejected: two namespaces for one thing is the RFC 0166 defect one layer up.
  4. Do nothing. Rejected: 0 terminal states in 164 RFCs is the measurement.

Unresolved questions

  1. Whether check-accepted-predicate.mjs should also re-run over the 158 v1 Accepted RFCs as an advisory report (not a gate). Recommended: yes, advisory, so the Phase 3 retrospective has a number.

Implementation notes (non-normative)

This PR: RFC 0001 §3/§5 text; GOVERNANCE sentence; README register-location sentence; git mv of 54 files; RFC 0106 G7 re-token; RFC 0155 residue closed; eleven banners and five gap rows corrected; check-rfc-status-coherence.mjs, check-waiver-authority.mjs, the check-registers.mjs self-carry refusal, wired into openwop:check step 4. Phase 3: check-accepted-predicate.mjs, check-core-budget.mjs, the gaps.json prose adapter.

Acceptance criteria

  • [x] Draft → Active: RFC text; RFC 0001 and GOVERNANCE amendments; register relocation; self-carry refusal with the sweep; the two new checks green; banners and stale deferrals corrected; ledger row; adversarial review. (This PR.)
  • [ ] Active → Accepted (Phase 3): check-accepted-predicate.mjs and check-core-budget.mjs in the gate; the 43 prose gap tables absorbed and deleted; spec/v2/core/ under budget with every ext/ header declared; §B.4 restated in spec/v2/core/. — met at the RC: both checks run in scripts/openwop-check.sh stage 10; the 43 prose gap tables are absorbed into spec/v1/spec-gaps.json (196 rows across 43 documents, both row shapes mapped, ids (doc, row-order)-derived) and deleted from prose, each document left carrying its absorption pointer, which generate-gaps.mjs --check verifies; spec/v2/core/ measures 20,899 of 25,000 words across 20 documents and every one of the 13 ext/ families declares witness and both maturity axes in its README header (check-declaration.mjs leg 5); §B.4 is restated at spec/v2/core/overview.md §"v1 end-of-support". deferred: nothing.

References

  • RFC 0167 §A (Axiom 4), §B.7, §C, G1; RFC 0001 §3–§6; RFC 0166 §A/§C.3/§D; RFC 0155 §D; RFC 0156 §B, G3, R4; RFC 0158 §D–§E; RFC 0147 §A.6; RFC 0122; GOVERNANCE.md §"Acceptance evidence tiers", §"Sole-steward operation", §"Amendments"; COMPATIBILITY.md §3, §5; RFCS/README.md §"Process", §"Companion gap & risk registers"; docs/IMPLEMENT-CORE.md; docs/RETROSPECTIVE-QUEUE.md; docs/WAIVER-RETROSPECTIVE-REGISTER.md; scripts/check-waiver-ledger.mjs, check-waiver-retrospective.mjs, check-registers.mjs, generate-gaps.mjs.

More proposals.

Edit this page on GitHub ↗