Lift ops-warden's binding rules into owned ADRs
Five rules that governed this repo lived in wiki prose, a workplan, and a comment at the top of catalog.yaml. All were followed; none was addressable. A reader outside ops-warden could not cite one, could not tell whether it was current, and — the point of this change — could not tell whether it was ours to change or someone else's that we merely obey. ADR-0001 The routing catalog is a pointer layer, never a second copy ADR-0002 ops-warden is a transparent conduit, never a secret broker ADR-0003 Cover gaps, but never silently own them ADR-0004 High-risk lanes refuse raw value streaming to agent sessions ADR-0005 Implement one lane narrowly, route everything else Each carries owner: ops-warden, which is the load-bearing field. It says we follow the rule AND we are responsible for changing it — by superseding ADR, never an in-place edit. The failure this prevents runs both ways: a rule we own mistaken for inherited canon never gets fixed, because we wait for an owner who does not exist; inherited canon mistaken for ours gets quietly bent, and the drift is invisible until it breaks a repo that trusted the canonical version. Rules we follow but do not own — NetKingdom canon, the IAM profile, the credential-management standard, the-custodian's ADR-001 workplan convention — are cited, never copied into docs/adr/. Copying them would recreate exactly the second-source-of-truth failure ADR-0001 exists to prevent. architecture.md also now flags the three-digit/four-digit ADR-001 vs ADR-0001 collision, which is itself an ours-versus-inherited confusion waiting to happen. Publication verified rather than assumed: all five render through policy-nexus tools/render.py, and owner reaches the reader in three places — the page eyebrow (render.py:346), the index Owner column (build_site.py:123,137), and the publication manifest. build_site.py:179 makes title/status/owner required, so ownership cannot be dropped on the way out. policy-nexus publishes and never writes back; docs/adr/ stays the source of truth. Documentation adapted: SCOPE.md gains a Governing rules section and an orientation entry; .claude/rules/architecture.md replaced its stub with the ADR index, the owned-versus-inherited rule, and ADR-over-wiki precedence; finding-routing.md's ADR-gap section closed; catalog.yaml's no-double-source header now cites ADR-0001 rather than the originating workplan. uv run pytest -q → 338 passed, 4 deselected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
faa4f2c23e
commit
35aff380a3
10 changed files with 601 additions and 13 deletions
95
docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md
Normal file
95
docs/adr/ADR-0003-cover-gaps-never-silently-own-them.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
id: ops-warden-adr-0003
|
||||
type: adr
|
||||
title: "ADR-0003 — Cover gaps, but never silently own them"
|
||||
domain: infotech
|
||||
repo: ops-warden
|
||||
status: accepted
|
||||
version: "1.0"
|
||||
revision: "1"
|
||||
owner: ops-warden
|
||||
binds: "ops-warden"
|
||||
created: "2026-07-01"
|
||||
updated: "2026-08-18"
|
||||
last_reviewed: "2026-08-18"
|
||||
review_interval: 6m
|
||||
enforced_by: "registry/routing/catalog.yaml delegation:; warden route gaps"
|
||||
supersedes: ""
|
||||
successor: ""
|
||||
---
|
||||
|
||||
# ADR-0003 — Cover gaps, but never silently own them
|
||||
|
||||
## Status
|
||||
|
||||
Accepted. Stated as INTENT §9, made structural by WARDEN-WP-0030 (delegation
|
||||
register).
|
||||
|
||||
## Context
|
||||
|
||||
ops-warden owns exactly one lane: SSH certificate issuance. It nonetheless fronts
|
||||
around eleven credential lanes as a caller-identity proxy, because no other
|
||||
component fronts them yet and a worker blocked on a credential is a worker blocked.
|
||||
|
||||
Covering a gap is legitimate and this repo intends to keep doing it. The failure is
|
||||
subtler: **a cover that is never recorded as a cover becomes ownership by default.**
|
||||
Nobody decides to permanently own another component's lane. It happens because the
|
||||
interim arrangement worked, nobody wrote down that it was interim, and the intended
|
||||
owner never learned they were expected to build a front door.
|
||||
|
||||
By August 2026 the primitive to hand a lane back existed and was proven — `exec_owner`
|
||||
/ `exec_command`, used by secrets-engine for npm publish and by the railiance-platform
|
||||
credential broker for warden-sign — and was used by 2 of 24 lanes. The other
|
||||
twenty-two had no record of who *should* own them.
|
||||
|
||||
## Decision
|
||||
|
||||
**Every catalog entry carries a `delegation:` block**, with a `mode:` of:
|
||||
|
||||
| `mode` | Meaning |
|
||||
| --- | --- |
|
||||
| `permanent` | Ours forever. SSH certificate issuance, and nothing else |
|
||||
| `native` | The owner has a front door; we route to it and execute nothing |
|
||||
| `interim` | We are covering a gap. Requires `intended_owner:` and `blocked_on:` |
|
||||
|
||||
**`interim` without an `intended_owner` is not permitted.** If we cannot name who
|
||||
should own it, we have not understood the lane well enough to be fronting it.
|
||||
|
||||
**`blocked_on:` must name a specific condition, not a mood.** "No front door yet" is
|
||||
not a blocker; "secrets-engine has not confirmed whether `exec --catalog` generalizes
|
||||
over arbitrary OpenBao lanes (asked 2026-08-11, msg 7d55d332)" is. A blocker with a
|
||||
question and a date can be chased. A blocker without one is an excuse with a
|
||||
timestamp.
|
||||
|
||||
**The interim set is queryable**: `warden route gaps` lists it with review dates and
|
||||
staleness. A cover that nobody can enumerate is a cover nobody will retire.
|
||||
|
||||
**A blocker is a claim about the world at a date, and expires.** `reviewed:` is
|
||||
bumped only on a real re-check, never inherited. This was learned the hard way:
|
||||
`RISK-F-0001` invalidated one of our blockers within a day and nothing would have
|
||||
re-checked it.
|
||||
|
||||
## Consequences
|
||||
|
||||
**Retiring a cover is a normal, expected event rather than a renegotiation.** When
|
||||
an owner ships their front door the lane flips `interim` → `native`. This has
|
||||
happened twice and both were routine.
|
||||
|
||||
**Other repos can see what we are holding for them.** The register is why key-cape
|
||||
and user-engine were able to confirm or decline lanes in August 2026 — the question
|
||||
was answerable because it had been written down. One of those answers was "not ours",
|
||||
which is a legitimate and useful outcome.
|
||||
|
||||
**We accept looking worse than we are.** `warden route gaps` publishes a list of
|
||||
things this repo is doing that it would rather not be doing. That is the intent: the
|
||||
alternative is a repo that looks clean because nobody counted.
|
||||
|
||||
**This register is not a risk register.** Interim lanes are tracked work with an
|
||||
owner and a date, not defects. They do not get bulk-filed into `risk-nexus`, which
|
||||
needs to stay small enough to read. Defects go there; gaps stay here.
|
||||
|
||||
## Related
|
||||
|
||||
- `INTENT.md` §9 — the principle this formalizes
|
||||
- `history/2026-08-11-delegation-surface-assessment.md` — the assessment that forced it
|
||||
- `.claude/rules/finding-routing.md` — the register-versus-findings boundary
|
||||
Loading…
Add table
Add a link
Reference in a new issue