docs(decisions): ADR-018 propose central agent asset registry (#246)#548
docs(decisions): ADR-018 propose central agent asset registry (#246)#548Kalindi-Dev wants to merge 3 commits into
Conversation
…mples#246) Draft ADR proposing a versioned, immutable-per-version asset registry for MCP servers, Cedar policy modules, and skills. Fixes the contract (semver grammar, immutability, resolve-at-create-task, descriptor validation, governance workflow) and defers substrate selection to the design PR. Ranks AWS Agent Registry as preferred with DynamoDB+S3 as fallback; surveys mcp-gateway-registry, agentregistry.ai, and Entra Agent Registry+AGT as considered alternatives. Status: proposed. Requires the aws-samples#246 `approved` label before opening a follow-up implementation PR (per ADR-003). Includes regenerated Starlight mirror.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #548 +/- ##
=======================================
Coverage ? 89.43%
=======================================
Files ? 228
Lines ? 55899
Branches ? 5923
=======================================
Hits ? 49991
Misses ? 5908
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
How will the lookup "Registry of registries" be maintained? For example:
Is there an abstraction from the Cloud Computing Native Foundation (CCNF) that can be utilized versus rolling a bespoke system? |
scottschreckengaust
left a comment
There was a problem hiding this comment.
First off — this is genuinely one of the most thorough, well-reasoned ADRs I've read in this repo, and it was a pleasure to review. 🙌 You did the hard things right: it's template-compliant, it's honest about its own risks (the (!) section is excellent), and it correctly reaches for the repo's twice-learned parity lesson instead of re-discovering it. The contracts/registry-resolution/ proposal mirroring contracts/cedar-parity/ and contracts/workflow-validation/ is exactly the instinct we want to see — I verified both of those directories exist and this slots right in beside them. The four substrate flip-conditions turn "preferred with a fallback" into something falsifiable rather than hand-wavy, and resolving at the create-task boundary keeps you consistent with ADR-014's shipped workflow_ref model. Really strong work, and the effort clearly shows.
Everything below is in service of making an already-good document airtight before it graduates from proposed to accepted — I'm leaving this as a comment, not a change request, since it's a draft awaiting the approved label on #246. Please read the tone as "help me help this land," not "stop." 💛
Two things I'd fix before acceptance (both quick)
1. The "grammar already committed" claim doesn't hold against the shipped regex (inline on the sub-decision). I ran the ADR's own example refs against _REGISTRY_REF at validator.py:50, and the committed regex has no @ in its character class (so @<constraint> can't be parsed) and only allows hyphens in the kind segment (so mcp_server / cedar_policy_module / prompt_fragment don't match — only mcp-server does). This is totally fixable and doesn't weaken the design — it just means the framing "closing the loop rather than opening it" slightly overstates what's shipped. A one-line reframe ("this ADR extends the committed shape") plus a note on where the regex change lands fully resolves it. Details inline.
2. Small off-by-one in the References section (inline). It credits "sub-decision 12" for the #381 split, but 12 is the workflows-don't-migrate decision — the #381 split is 13 (your PR body has it right!). Tiny, but the test-plan box "sub-decisions referenced by number are internally consistent (1–13, no gaps)" is checked, so worth tidying so that claim stays true. Appears in both the source file and the generated mirror, so the fix lands in docs/decisions/ and re-syncs.
A few design-level thoughts (take or leave — you're the author)
These aren't blockers, just things I'd love your read on:
- Altitude. The doc is part ADR, part design spec, part market survey — and it's great diligence — but the ~15-vendor enumeration and the pinned implementation details (error-code strings, Cognito group names) are the most perishable content in an artifact whose whole job is to be durable. Totally your call, but you might keep the invariants + preferred/fallback ranking + flip-conditions here, and let the vendor deep-dive live in the design PR or a dated appendix. The decision survives; the survey ages.
- Governance-in-MVP sizing (inline). Sub-decision 10 is a strong principle ("governance is what separates a registry from a directory" — love it), but a full lifecycle + audit + event integration + auto-approve, on top of a two-language semver resolver and descriptor validation, is a big MVP for a reference project whose AC4 goal is one E2E kind. Might be worth an explicit sentence acknowledging governance may need staging if the estimate tightens.
- AgentCore timing. The ADR is dated 2026-07-08 and the preferred substrate hard-migrates namespaces on 2026-08-06 — likely mid-build. You flag it as a risk and lean on the
RegistryClientseam, which is right for code, but the seam doesn't confine data migration / IAM action renames as cleanly as the prose implies. Maybe promote "don't take a production dependency until post-cutover GA" from a risk bullet into the decision framework. - One status, two names (inline).
approved"(also calledactive)" then both get used interchangeably — in a byte-for-byte two-language contract that's a latent divergence, and it's exactly the parity hazard your own(−)bullet warns about. Pick one canonical token.
Nits
- Two reference URLs look off by host convention (didn't fetch):
www.docs.microsoft.com/...(usuallylearn.microsoft.com) anddocs.cloud.google.com/...(usuallycloud.google.com/...). Worth a spot-check since the test plan mentions link verification. - Reserving a
capabilitykind for workflows while sub-decision 12 says workflows won't migrate reads slightly oddly — fine as "declared but not loaded," just a one-clause note would smooth it.
Thank you for the care you put into this — the bones are excellent and none of the above is hard to address. Happy to pair on the grammar reconciliation (#1) if useful, since that's the one place code and contract need to meet. Excited to see this one land. 🚀
|
|
||
| ### Sub-decisions | ||
|
|
||
| 1. **URI grammar and kinds.** `registry://<kind>/<namespace>/<name>@<constraint>`. MVP kinds: `mcp_server`, `cedar_policy_module`, `skill`. Schema declares — but does not yet load — `plugin`, `subagent`, `prompt_fragment`, `capability` (`capability` = workflow, ADR-014 vocabulary). URI regex matches the shape already committed at [`agent/src/workflow/validator.py:50`](../../agent/src/workflow/validator.py). |
There was a problem hiding this comment.
Small but load-bearing accuracy fix 🙂 — this says the URI regex "matches the shape already committed at validator.py:50," but I tested the ADR's own example refs against the actual committed regex:
committed: ^registry://[a-z][a-z0-9-]*/[a-z0-9][a-z0-9./-]*$
MATCH registry://mcp-server/acme/pdf-tools
NO registry://mcp_server/acme/pdf-tools@^1.4.1 ← this ADR's grammar
NO registry://mcp_server/acme/pdf-tools ← underscore kind
NO registry://skill/acme/refactor@~2.0.0
Two gaps:
- No
@<constraint>support — there's no@in the character class, so the semver constraints from sub-decision 2 have nowhere to live in the shipped grammar. - No underscores in the kind segment — it's
[a-z][a-z0-9-]*(hyphen only), but every MVP kind here is snake_case (mcp_server,cedar_policy_module,prompt_fragment). Only a hyphenatedmcp-servermatches today.
None of this hurts the design — it just means _REGISTRY_REF has to be extended to carry this contract. Suggested tweak: reframe as "extends the committed shape (adding @constraint and reconciling kind naming)" and note whether that regex change rides in this ADR's companion or the design PR. Same reasoning applies to the "the code already parses and validates registry:// refs" line down in Consequences. Happy to pair on this bit! 💪
| ## References | ||
|
|
||
| - Issue [#246](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/246) — this ADR's tracking issue. | ||
| - Issue [#381](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/381) — ADR↔Persona↔Skill graph (overlap resolved by sub-decision 12). |
There was a problem hiding this comment.
Tiny off-by-one: this credits sub-decision 12 for the #381 split, but 12 is "Workflows do not migrate to the registry" — the clean #381 split is sub-decision 13 (and your PR description has it right ✅). Worth fixing because the test-plan checkbox "sub-decisions referenced by number are internally consistent (1–13, no gaps)" is ticked, so this keeps that honest. It's in the generated mirror too, so the fix lands in this source file and re-syncs via mise //docs:sync.
|
|
||
| 9. **MVP E2E path is one asset kind: MCP server.** Per issue AC4, one end-to-end path is sufficient for MVP. MCP server is chosen because it is the most heavily used asset kind (already vendored built-in in the container) and because AgentCore Registry has native, protocol-validated support for it. Cedar policy modules and skills follow in child issues ([#478](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/478)/[#479](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/479)/[#480](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/480)/[#481](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/481)). | ||
|
|
||
| 10. **Governance workflow ships in MVP.** Publishing follows a lifecycle: **draft → submitted → approved | rejected → deprecated → removed**. The `active` status on a record means "approved" — resolvers only match approved records; submitted/rejected/draft records exist but do not resolve. Governance is what separates a registry from a directory; deferring it entirely to Phase 3 would drop ABCA into the "partial-match" tier that community catalogs occupy. MVP requirements: (a) publish creates a record in `submitted` state (or `active` if the operator has approver rights); (b) an approver can transition submitted → approved or submitted → rejected with an audit trail; (c) an event fires on state transitions so external review pipelines (ticketing, security scan, human approval) can integrate; (d) auto-approve mode is available for dev environments. Rich audit metadata (approver identity, timestamp, rationale) is a MUST on every state transition. |
There was a problem hiding this comment.
Really like the principle here — "governance is what separates a registry from a directory" is the right north star, and putting it up front is a good instinct. My only nudge: this is a lot of MVP (full lifecycle + audit trail + event-driven review integration + auto-approve mode) layered on top of a two-language semver resolver, descriptor validation, and a parity corpus — for a reference project whose stated E2E goal (AC4) is a single asset kind. Not asking you to cut it — just maybe one explicit sentence that governance can be staged if the estimate tightens, so a future implementer has permission to sequence it rather than treating all of (a)–(d) as an atomic MVP gate. Your call entirely. 🙏
|
|
||
| 3. **Immutable per version.** `(kind, namespace, name, version)` is immutable once published. Republish attempts fail 409 `REGISTRY_VERSION_EXISTS`. Content changes require a new version. Mutable metadata is confined to a lifecycle status field. | ||
|
|
||
| 4. **Lifecycle status.** Full set: `draft` → `submitted` → (`approved`/`rejected`) → `deprecated` → `removed`. `approved` (also called `active`) resolves silently; `deprecated` resolves with a warning event on the task record; `submitted`, `draft`, `rejected`, and `removed` all fail resolution. See sub-decision 10 for the governance transitions between these states. Immutability of the artifact bytes is orthogonal — status transitions do not rewrite content. |
There was a problem hiding this comment.
One canonical-name nudge: approved "(also called active)" introduces two tokens for one state, and sub-decisions 10 and 11 then use both interchangeably (e.g. "in submitted state (or active…)"). Since the resolver contract has to agree byte-for-byte across the TypeScript orchestrator and the Python agent — the exact parity hazard your own (−) bullet flags so well — I'd pick one canonical status string and mention the other only as "(formerly/aka)" prose. Cheap insurance against a sneaky divergence bug. 🙂
scottschreckengaust
left a comment
There was a problem hiding this comment.
Verdict
Comment (not a change request). This is an exceptionally thorough, template-compliant ADR and the design is sound: contract-first, substrate-deferred with falsifiable flip-conditions, fail-closed resolution, and it correctly reaches for the repo's twice-learned parity lesson (contracts/registry-resolution/ mirroring the existing contracts/cedar-parity/ and contracts/workflow-validation/, both of which I verified exist). It should not merge yet only because it's a self-declared draft awaiting the approved label on #246 — governance-wise that gate is now satisfied per the review brief. Before it graduates proposed → accepted, two factual defects should be fixed (grammar-parity claim + a cross-reference off-by-one). I independently re-verified the prior maintainer review (@scottschreckengaust) against the current worktree at f3e8972; every claim it makes is still accurate — none are stale.
Vision alignment
Strong fit with VISION.md:
- Fire-and-forget default (tenet 1): assets resolve at the create-task boundary (sub-decision 5) — no live intervention introduced; the submitter path stays asynchronous.
- Bounded blast radius & cost (tenet 2): fail-closed on unresolvable refs (sub-decision 6), semver-only with
*/latest/>=rejected (sub-decision 2), immutability per version (sub-decision 3), governance lifecycle + coarse Cognito ACL (sub-decisions 10/11). Widening surface is documented, not silent. - Reviewable outcomes (tenet 8):
resolved_assetsstamped on every task record for audit + reproducibility; state transitions carry rich audit metadata. - Tenet trade-offs documented: the
(−)/(!)sections are exactly the explicit rationale the tenets require of an architectural decision. No undocumented tenet trade.
Blocking issues
None that block this draft PR. The two items below are fix-before-accepted correctness defects in the design-of-record (I'd re-raise them as blocking if this PR tried to move status to accepted without addressing them):
-
docs/decisions/ADR-018-agent-asset-registry.md:40/:134/:162— the "grammar already committed" claim is factually false against the shipped regex._REGISTRY_REFatagent/src/workflow/validator.py:50is^registry://[a-z][a-z0-9-]*/[a-z0-9][a-z0-9./-]*$. I ran the ADR's own example refs against it:registry://mcp_server/acme/foo@^1.4.1→ no match (no@in any char class; underscore not allowed in the kind segment)registry://cedar_policy_module/acme/foo→ no match (underscore)registry://prompt_fragment/acme/foo→ no match (underscore)- only
registry://mcp-server/acme/foo(hyphen, no constraint) matches.
So the ADR's MVP kinds (mcp_server,cedar_policy_module) and the@<constraint>grammar (line 34) are not parseable by the committed regex. Lines 134/162 ("the code already parses and validatesregistry://refs", "URI grammar already shipped") overstate what exists and would mislead #632+ implementers into thinking no parser change is needed. Fix: reframe as "this ADR extends the committed shape" and add a one-line note that the regex must widen (add@<constraint>, allow_in the kind segment) in the implementation PR. Risk if unfixed: the design-of-record is inaccurate about its own foundation.
-
docs/decisions/ADR-018-agent-asset-registry.md:152— off-by-one cross-reference. References credits "sub-decision 12" for the #381 split, but sub-decision 12 (line 62) is workflows-don't-migrate and the #381 split is sub-decision 13 (line 64; the PR body has it right). The test-plan box "sub-decisions referenced by number are internally consistent (1–13, no gaps)" is checked, so this contradicts a checked claim. One-character fix, lands in source + re-syncs to the mirror.
Non-blocking suggestions / nits
- Dual status token (
approved"also calledactive"). Lines 46/58/60 use both interchangeably. In a byte-for-byte two-language contract this is the exact parity hazard the ADR's own(−)bullet warns about — pick one canonical token now while it's cheap. - Reference host conventions.
docs.cloud.google.com/...(line 109) is normallycloud.google.com/...;www.docs.microsoft.com/...(line 171) is normallylearn.microsoft.com/.... Worth a spot-check since the test plan claims link verification. (Not fetched.) - Altitude / perishability. The ~15-vendor survey and pinned implementation strings (error codes, Cognito group names) are the most perishable content in an artifact whose job is durability. Consider keeping invariants + preferred/fallback + flip-conditions here and moving the vendor deep-dive to a dated appendix or the design PR. Author's call.
- Governance-in-MVP sizing. Sub-decision 10 (full lifecycle + audit + event integration + auto-approve) on top of a two-language semver resolver + descriptor validation is a large MVP for a reference project whose AC4 is one E2E kind. A sentence acknowledging governance may need staging if the estimate tightens would be honest.
- AgentCore 2026-08-06 rename. The
RegistryClientseam confines code but not data migration / IAM-action renames as cleanly as the prose implies. Consider promoting "no production dependency until post-cutover GA" from a risk bullet into the decision framework. - Reserved
capabilitykind vs sub-decision 12. Reservingcapability(= workflow) while sub-decision 12 says workflows won't migrate reads slightly oddly; a one-clause "declared but not loaded" note smooths it.
Documentation
- ADR follows the
docs/decisions/template (Status/Date/Context/Decision/Sub-decisions/Consequences/References), matching ADR-016/ADR-017 shape. ADR-018 number is free (no collision). Linked ADR-014 and ADR-003 files both exist. - Starlight mirror is correctly regenerated, not hand-edited.
docs/src/content/docs/decisions/Adr-018-agent-asset-registry.mdhas the generated frontmatter and site-rewritten internal links; after stripping frontmatter and normalizing link targets, the mirror body is identical to the source. CI "Fail build on mutation" will pass. This satisfies themise //docs:syncrequirement.
Tests & CI
- CI is green (Codecov note is a config warning about a missing BASE report, not a failure).
- No unit tests apply — the PR is strictly two docs files; no executable behavior added.
- Bootstrap synth-coverage: N/A for this PR. The review brief's CDK bootstrap-policy check (DynamoDB table, S3 bucket, new Lambda handlers,
BOOTSTRAP_VERSIONbump,resource-action-map.ts,DEPLOYMENT_ROLES.mdgolden baseline) applies to the implementation PRs (#632/#633/#634), not to this ADR. I confirmed the diff touches zero CDK/handler/CLI/agent code (git diff --name-only= the ADR source + its mirror only). No constructs changed here, so there is nothing to gate. When #632 lands, that PR MUST carry the full bootstrap bundle update — I'll enforce it there. - Shared-type sync: N/A.
cdk/src/handlers/shared/types.tsis untouched, so nocli/src/types.tssync is owed by this PR.
Review agents run
- Hand review by principal-architect judgment (primary): appropriate here because the diff is prose — the ADR itself is the reviewable artifact. I empirically validated the two load-bearing factual claims (regex parity via a Python harness against the actual
_REGISTRY_REF; sub-decision numbering) rather than trusting prose. - Omitted —
code-reviewer,silent-failure-hunter,type-design-analyzer,pr-test-analyzer: no executable code, no error-handling paths, no new types, no tests in the diff; nothing in their scope to analyze. - Omitted —
/security-review: no IAM, Cedar policy, network, secret, DynamoDB, S3, API-handler, or input-validation code in this diff. Those resources are proposed in the ADR and will be security-reviewed when their code lands in #632+. The security-relevant design assertions (fail-closed resolution, coarse-ACL trust decision, publish-endpoint exposure) are called out in the ADR's own(!)risks and I reviewed them by hand under Vision alignment. comment-analyzersensibility applied by hand: verified the ADR's cross-references and its claims about existing code/contracts dirs match reality — that's how I found blocking-issue #1 and #2.
Human heuristics
- Proportionality — concern (minor): governance-in-MVP + full vendor survey is heavy relative to AC4's one-E2E-kind goal (see nits). Not a blocker for an ADR; flagged for the design PR to right-size.
- Coherence — concern (minor): dual
approved/activestatus token (:46/:58/:60) risks the same-concept-two-terms hazard the ADR itself warns about; and the grammar claim (#1) conflicts with the shipped code. Both fixable. - Clarity — pass: names communicate intent; error/failure modes surface explicitly (fail-closed, specific reason codes); no defaults hiding failures.
- Appropriateness — pass: maintainable by this team; correctly slots beside existing parity-corpus mechanisms; integration claims mostly verified against real repo state (the one exception is blocking issue #1, where the claim was checked against the real regex and found overstated).
Stack / merge-order note
This ADR is the independent design-of-record (ADR-018) that the implementation PRs #632/#633/#634 build on. Recommended order: land this ADR first (after the two fixes and the approved label lands per the PR's own draft gate), so #632+ have a stable, accurate contract to cite. Critically, fix blocking issue #1 before #632 — otherwise #632's implementers may skip the required _REGISTRY_REF regex widening (add @<constraint>, allow _ in the kind segment) because the ADR told them the grammar was already shipped. The bootstrap-policy, shared-type-sync, and CDK synth-coverage gates all attach to #632, not here.
|
|
||
| ### Sub-decisions | ||
|
|
||
| 1. **URI grammar and kinds.** `registry://<kind>/<namespace>/<name>@<constraint>`. MVP kinds: `mcp_server`, `cedar_policy_module`, `skill`. Schema declares — but does not yet load — `plugin`, `subagent`, `prompt_fragment`, `capability` (`capability` = workflow, ADR-014 vocabulary). URI regex matches the shape already committed at [`agent/src/workflow/validator.py:50`](../../agent/src/workflow/validator.py). |
There was a problem hiding this comment.
Verified against the shipped regex and this claim is overstated. _REGISTRY_REF at agent/src/workflow/validator.py:50 is ^registry://[a-z][a-z0-9-]*/[a-z0-9][a-z0-9./-]*$. Running the ADR's own examples: registry://mcp_server/acme/foo@^1.4.1 does NOT match — there is no @ in any character class (so @<constraint> can't parse) and no _ in the kind segment (so mcp_server and cedar_policy_module fail; only mcp-server matches). So the committed regex does not yet match this ADR's grammar or MVP kinds. Suggest reframing to "this ADR extends the committed shape" and noting the regex must widen (@<constraint>, underscore in the kind segment) in the implementation PR. Same overstatement recurs at line 134 ("the code already parses and validates registry:// refs") and line 162 ("URI grammar already shipped").
| ## References | ||
|
|
||
| - Issue [#246](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/246) — this ADR's tracking issue. | ||
| - Issue [#381](https://github.com/aws-samples/sample-autonomous-cloud-coding-agents/issues/381) — ADR↔Persona↔Skill graph (overlap resolved by sub-decision 12). |
There was a problem hiding this comment.
Off-by-one: the #381 split is sub-decision 13 (line 64), not 12 — sub-decision 12 (line 62) is "workflows do not migrate." Your PR body has it right. The test-plan box "sub-decisions referenced by number are internally consistent (1–13, no gaps)" is checked, so this needs the one-char fix to keep that claim true. Fix lands in source and re-syncs to the mirror.
|
|
||
| 3. **Immutable per version.** `(kind, namespace, name, version)` is immutable once published. Republish attempts fail 409 `REGISTRY_VERSION_EXISTS`. Content changes require a new version. Mutable metadata is confined to a lifecycle status field. | ||
|
|
||
| 4. **Lifecycle status.** Full set: `draft` → `submitted` → (`approved`/`rejected`) → `deprecated` → `removed`. `approved` (also called `active`) resolves silently; `deprecated` resolves with a warning event on the task record; `submitted`, `draft`, `rejected`, and `removed` all fail resolution. See sub-decision 10 for the governance transitions between these states. Immutability of the artifact bytes is orthogonal — status transitions do not rewrite content. |
There was a problem hiding this comment.
Nit: approved "(also called active)" then both tokens get used interchangeably here and in sub-decisions 10/11. In a byte-for-byte two-language contract that's the exact parity hazard your own (−) bullet warns about. Pick one canonical status token now while it's cheap.
Status
Draft — pending
approvedlabel on #246 per ADR-003. Opening as a concrete artifact for maintainer review while approval is being decided; not requesting merge until governance approval lands on the parent issue.Summary
Proposes ADR-018, which fixes the contract for a central agent asset registry closing #246:
registry://<kind>/<namespace>/<name>@<constraint>(already declared atagent/src/workflow/validator.py:50).mcp_server,cedar_policy_module,skill. Others declared but not loaded.^/~only. Rejects*,latest,>=.(kind, namespace, name, version)immutable once published; republish returns 409.draft → submitted → approved | rejected → deprecated → removed.workflow_refresolves today;resolved_assetsstamped on the task record.RegistryClientabstraction; substrate changes and any AgentCore Aug 6 2026 rename are confined to one implementation file per language.docs/+ plugin markdown.Substrate
The ADR does not pick the substrate. It ranks candidates by fit for ABCA and defers the pick to the design PR:
Design PR decision framework names four concrete conditions under which the design PR flips from preferred to fallback (region GA, immutability guards, semver-on-top complexity, Aug 2026 rename cost).
Addressing #381 (per @scottschreckengaust's comment on #246)
Sub-decision 13 splits the two concerns cleanly:
docs/and plugin markdown with frontmatter edges + parity linter.If a skill record's descriptor eventually cites an ADR, that's metadata on the record — not a graph edge #381 owns. Happy to iterate on the boundary if you see it differently.
Test plan
docs/decisions/ADR-018-agent-asset-registry.mddocs/src/content/docs/decisions/Adr-018-agent-asset-registry.mdapprovedlabel on feat(registry): central agent asset registry for capabilities, skills, plugins, and MCP servers #246proposedstatusRelated
Progresses #246. See also #381 (documentation graph — split resolved in sub-decision 13), ADR-014 (workflow-driven tasks — resolver-interface precedent), and the ADR-003 contribution governance model.