feat(handoffs): publish owner task interfaces
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
parent
a3952113d5
commit
890f3b05b5
12 changed files with 754 additions and 38 deletions
|
|
@ -17,7 +17,7 @@
|
||||||
| workplan | RMGR-WP-0007 | finished | — | workplans/RMGR-WP-0007-greenfield-rapp-wrap-efficiency.md |
|
| workplan | RMGR-WP-0007 | finished | — | workplans/RMGR-WP-0007-greenfield-rapp-wrap-efficiency.md |
|
||||||
| workplan | RMGR-WP-0008 | finished | — | workplans/RMGR-WP-0008-work-record-and-register-receiving-surface.md |
|
| workplan | RMGR-WP-0008 | finished | — | workplans/RMGR-WP-0008-work-record-and-register-receiving-surface.md |
|
||||||
| workplan | RMGR-WP-0009 | finished | — | workplans/RMGR-WP-0009-coding-assistant-commit-provenance.md |
|
| workplan | RMGR-WP-0009 | finished | — | workplans/RMGR-WP-0009-coding-assistant-commit-provenance.md |
|
||||||
| workplan | RMGR-WP-0010 | active | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| workplan | RMGR-WP-0010 | finished | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| task | RMGR-WP-0001-T01 | done | — | workplans/RMGR-WP-0001-foundation.md |
|
| task | RMGR-WP-0001-T01 | done | — | workplans/RMGR-WP-0001-foundation.md |
|
||||||
| task | RMGR-WP-0001-T02 | done | — | workplans/RMGR-WP-0001-foundation.md |
|
| task | RMGR-WP-0001-T02 | done | — | workplans/RMGR-WP-0001-foundation.md |
|
||||||
| task | RMGR-WP-0001-T03 | done | — | workplans/RMGR-WP-0001-foundation.md |
|
| task | RMGR-WP-0001-T03 | done | — | workplans/RMGR-WP-0001-foundation.md |
|
||||||
|
|
@ -83,9 +83,9 @@
|
||||||
| task | RMGR-WP-0010-T01 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| task | RMGR-WP-0010-T01 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| task | RMGR-WP-0010-T02 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| task | RMGR-WP-0010-T02 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| task | RMGR-WP-0010-T03 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| task | RMGR-WP-0010-T03 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| task | RMGR-WP-0010-T04 | wait | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| task | RMGR-WP-0010-T04 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| task | RMGR-WP-0010-T05 | wait | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| task | RMGR-WP-0010-T05 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| task | RMGR-WP-0010-T06 | wait | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| task | RMGR-WP-0010-T06 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| task | RMGR-WP-0010-T07 | wait | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
| task | RMGR-WP-0010-T07 | done | — | workplans/RMGR-WP-0010-authoritative-workload-references.md |
|
||||||
| intake | RMGR-IN-0001 | open | — | intakes/intakes.md |
|
| intake | RMGR-IN-0001 | open | — | intakes/intakes.md |
|
||||||
| intake | RMGR-IN-0002 | open | — | intakes/intakes.md |
|
| intake | RMGR-IN-0002 | open | — | intakes/intakes.md |
|
||||||
|
|
|
||||||
70
docs/adr-005-owner-task-interfaces.md
Normal file
70
docs/adr-005-owner-task-interfaces.md
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
---
|
||||||
|
id: RMGR-ADR-005
|
||||||
|
type: architecture-decision-record
|
||||||
|
title: "Owner-task interfaces for cross-repository adoption"
|
||||||
|
status: accepted
|
||||||
|
decided: "2026-08-22"
|
||||||
|
deciders: ["Bernd Worsch", "repo-manager"]
|
||||||
|
related:
|
||||||
|
- RMGR-WP-0010
|
||||||
|
- docs/owner-task-interface-contract_v1.md
|
||||||
|
- canon-work-record-types
|
||||||
|
---
|
||||||
|
|
||||||
|
# ADR-005: Owner-task interfaces for cross-repository adoption
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
**Accepted** (2026-08-22).
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
A producer can identify work that belongs in another repository before the
|
||||||
|
owner has created a task for it. Sending only a prose request makes the next
|
||||||
|
coding agent reconstruct the objective, inputs, constraints, and acceptance
|
||||||
|
criteria. Keeping a duplicate waiting task in the producer workplan then makes
|
||||||
|
two agents carry one todo.
|
||||||
|
|
||||||
|
The fleet work-record registry is deliberately closed. An interface offer must
|
||||||
|
therefore not become a new task-like lifecycle or a second backlog.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
1. A producer may publish an `OwnerTaskInterface`: a versioned, immutable
|
||||||
|
proposal containing the complete owner-ready objective, authoritative
|
||||||
|
inputs, deliverables, constraints, acceptance criteria, and verification.
|
||||||
|
2. The interface is not a work record. It has no assignee or lifecycle status
|
||||||
|
and does not replace the target owner's workplan/task/decision.
|
||||||
|
3. The target owner records `approved`, `amended`, or `rejected` in its own
|
||||||
|
authoritative work record. Approval cites the interface id and exact Git
|
||||||
|
revision. An amendment records the delta rather than silently changing the
|
||||||
|
imported meaning.
|
||||||
|
4. Once the producer publishes and routes a complete interface, its handoff
|
||||||
|
task is done. Adoption and implementation are tracked only in the target
|
||||||
|
owner's work record; the producer does not retain a duplicate todo.
|
||||||
|
5. Dependencies that still block producer-owned implementation remain normal
|
||||||
|
producer tasks. The interface pattern is only for work whose implementation
|
||||||
|
authority has crossed the repository boundary.
|
||||||
|
6. Interfaces contain no credentials, secret values, mutable environment
|
||||||
|
state, or copied authority. They reference authoritative contracts and
|
||||||
|
evidence by stable path/id and revision.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Owners can approve and use the supplied task specification directly.
|
||||||
|
- Later agents do not need to recover intent from messages or re-plan the work.
|
||||||
|
- Owner amendments stay explicit and auditable.
|
||||||
|
- Work queues contain one implementation todo, in the repository that can
|
||||||
|
actually complete it.
|
||||||
|
- Messages become routing notifications; they are not the only copy of the
|
||||||
|
implementation contract.
|
||||||
|
|
||||||
|
## Rejected alternatives
|
||||||
|
|
||||||
|
- **Duplicate tasks in producer and consumer workplans.** They drift and need
|
||||||
|
manual closure coordination.
|
||||||
|
- **Prose-only messages.** They are useful routing evidence but too weak to be
|
||||||
|
an executable interface.
|
||||||
|
- **A new `handoff-task` work-record kind.** The existing task, intake, and
|
||||||
|
decision kinds already own lifecycle; adding another would create a parallel
|
||||||
|
backlog ontology.
|
||||||
92
docs/owner-task-interface-contract_v1.md
Normal file
92
docs/owner-task-interface-contract_v1.md
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
---
|
||||||
|
id: RMGR-CONTRACT-OWNER-TASK-INTERFACE-0001
|
||||||
|
type: contract
|
||||||
|
title: "Owner-task interface contract"
|
||||||
|
version: "1"
|
||||||
|
status: active
|
||||||
|
created: "2026-08-22"
|
||||||
|
updated: "2026-08-22"
|
||||||
|
related:
|
||||||
|
- RMGR-ADR-005
|
||||||
|
- RMGR-WP-0010
|
||||||
|
---
|
||||||
|
|
||||||
|
# Owner-task interface contract v1
|
||||||
|
|
||||||
|
An owner-task interface is a complete proposal that another repository can
|
||||||
|
approve and adopt by reference. It transports implementation intent, not task
|
||||||
|
lifecycle.
|
||||||
|
|
||||||
|
## Required shape
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: helixforge.repo-manager/v1
|
||||||
|
kind: OwnerTaskInterface
|
||||||
|
metadata:
|
||||||
|
id: helixforge.example.consumer-port.v1
|
||||||
|
title: Adopt the example consumer port
|
||||||
|
source:
|
||||||
|
repo: producing-repo
|
||||||
|
workplan_task: PRODUCER-WP-0001-T04
|
||||||
|
target:
|
||||||
|
repo: consuming-repo
|
||||||
|
owner_agent: consuming-repo
|
||||||
|
approval:
|
||||||
|
dispositions: [approved, amended, rejected]
|
||||||
|
record_in: target owner work record
|
||||||
|
task:
|
||||||
|
title: Adopt the example consumer port
|
||||||
|
priority: medium
|
||||||
|
objective: Consume the producer contract without copying its authority.
|
||||||
|
inputs:
|
||||||
|
- ref: producing-repo/docs/example-contract_v1.md
|
||||||
|
authority: producing-repo
|
||||||
|
deliverables:
|
||||||
|
- Implement the consumer adapter.
|
||||||
|
constraints:
|
||||||
|
- Do not infer missing identity.
|
||||||
|
acceptance:
|
||||||
|
- A contract fixture passes through the consumer boundary.
|
||||||
|
verification:
|
||||||
|
- make test
|
||||||
|
```
|
||||||
|
|
||||||
|
`inputs`, `constraints`, and `verification` are optional. The objective,
|
||||||
|
deliverables, and acceptance criteria are mandatory and owner-ready.
|
||||||
|
|
||||||
|
## Approval and adoption
|
||||||
|
|
||||||
|
The target owner records one disposition in its existing workplan task or
|
||||||
|
decision:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
interface_id: helixforge.example.consumer-port.v1
|
||||||
|
source_revision: <Git commit containing the interface>
|
||||||
|
disposition: approved # approved | amended | rejected
|
||||||
|
```
|
||||||
|
|
||||||
|
This may be prose or structured metadata according to the target repository's
|
||||||
|
existing convention. `amended` must cite the delta. The target task remains the
|
||||||
|
only lifecycle-bearing implementation record.
|
||||||
|
|
||||||
|
The source task is complete after the validated interface is committed and its
|
||||||
|
owner is notified. A later producer task is warranted only if an approved
|
||||||
|
amendment requires producer-side changes.
|
||||||
|
|
||||||
|
## Discovery
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rmgr owner-interface validate --path interfaces
|
||||||
|
rmgr owner-interface validate --path interfaces --owner ops-warden
|
||||||
|
```
|
||||||
|
|
||||||
|
The command validates all interface files before returning their content. An
|
||||||
|
owner filter matches either `target.repo` or `target.owner_agent`.
|
||||||
|
|
||||||
|
## Authority and safety
|
||||||
|
|
||||||
|
- The Git revision plus `metadata.id` pins the offered meaning.
|
||||||
|
- Referenced records remain authoritative in their source repositories.
|
||||||
|
- Interface files never contain secrets or mutable runtime observations.
|
||||||
|
- An interface must not contain `status`, State Hub ids, or an assignee: those
|
||||||
|
would turn it into an unregistered work-record lifecycle.
|
||||||
49
interfaces/ops-warden-workload-reference-v1.yaml
Normal file
49
interfaces/ops-warden-workload-reference-v1.yaml
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
apiVersion: helixforge.repo-manager/v1
|
||||||
|
kind: OwnerTaskInterface
|
||||||
|
metadata:
|
||||||
|
id: helixforge.workloads.ops-warden-reference.v1
|
||||||
|
title: Add explicit workload references to ops-warden lanes
|
||||||
|
source:
|
||||||
|
repo: repo-manager
|
||||||
|
workplan_task: RMGR-WP-0010-T06
|
||||||
|
target:
|
||||||
|
repo: ops-warden
|
||||||
|
owner_agent: ops-warden
|
||||||
|
suggested_work_record: WARDEN-WP-0032
|
||||||
|
approval:
|
||||||
|
dispositions: [approved, amended, rejected]
|
||||||
|
record_in: WARDEN-WP-0032 task or an ops-warden owner decision
|
||||||
|
task:
|
||||||
|
title: Adopt explicit workload references in the lane catalog
|
||||||
|
priority: high
|
||||||
|
objective: >-
|
||||||
|
Let workload-applicable lanes point to authoritative rapp workload identity
|
||||||
|
while keeping the catalog a pointer layer.
|
||||||
|
inputs:
|
||||||
|
- ref: repo-manager/docs/workload-reference-contract_v1.md
|
||||||
|
authority: repo-manager
|
||||||
|
- ref: ops-warden/registry/routing/catalog.yaml
|
||||||
|
authority: ops-warden
|
||||||
|
deliverables:
|
||||||
|
- >-
|
||||||
|
Add workload_ref with required rapp_id and name and optional deployable to
|
||||||
|
every workload-applicable lane.
|
||||||
|
- >-
|
||||||
|
Add an explicit applicability representation that distinguishes
|
||||||
|
not-applicable from an unresolved or invalid workload reference.
|
||||||
|
- Validate every supplied reference through Repo Manager exact resolution.
|
||||||
|
- Preserve native typed identity for lanes that do not represent workloads.
|
||||||
|
constraints:
|
||||||
|
- Do not copy workload classification, criticality, readiness, or placement into the catalog.
|
||||||
|
- Do not infer workload identity from path_template, owner_repo, or repository names.
|
||||||
|
- An absent pointer must not ambiguously mean both unknown and not-applicable.
|
||||||
|
acceptance:
|
||||||
|
- Every workload-applicable lane resolves exactly or reports unknown with a reason.
|
||||||
|
- Every non-workload lane is explicitly not-applicable.
|
||||||
|
- Catalog validation fails on malformed, ambiguous, or undeclared references.
|
||||||
|
- Existing lane identity and routing authority remain owned by ops-warden.
|
||||||
|
verification:
|
||||||
|
- make test
|
||||||
|
- make lint
|
||||||
|
- >-
|
||||||
|
uv run --project /home/worsch/repo-manager rmgr workload index --root /home/worsch
|
||||||
50
interfaces/rapp-workload-coverage-v1.yaml
Normal file
50
interfaces/rapp-workload-coverage-v1.yaml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
apiVersion: helixforge.repo-manager/v1
|
||||||
|
kind: OwnerTaskInterface
|
||||||
|
metadata:
|
||||||
|
id: helixforge.workloads.rapp-coverage.v1
|
||||||
|
title: Clarify universal rapp coverage for managed deployables
|
||||||
|
source:
|
||||||
|
repo: repo-manager
|
||||||
|
workplan_task: RMGR-WP-0010-T05
|
||||||
|
target:
|
||||||
|
repo: railiance-master
|
||||||
|
owner_agent: railiance-master
|
||||||
|
suggested_work_record: owner-selected Railiance Master schema workplan task
|
||||||
|
approval:
|
||||||
|
dispositions: [approved, amended, rejected]
|
||||||
|
record_in: Railiance Master file-backed workplan task or decision
|
||||||
|
task:
|
||||||
|
title: Clarify universal rapp coverage for managed deployables
|
||||||
|
priority: high
|
||||||
|
objective: >-
|
||||||
|
Make the normative rapp contract explicit that every managed running
|
||||||
|
deployable, including operational and tooling workloads participating in
|
||||||
|
controls, belongs to exactly one rapp.
|
||||||
|
inputs:
|
||||||
|
- ref: railiance-master/docs/adr/ADR-0007-rapp-declaration-contract.md
|
||||||
|
authority: railiance-master
|
||||||
|
- ref: repo-manager/docs/adr-004-authoritative-workload-declarations.md
|
||||||
|
authority: repo-manager boundary ruling
|
||||||
|
deliverables:
|
||||||
|
- >-
|
||||||
|
Amend or clarify ADR-0007 and the rapp authoring guide with universal
|
||||||
|
managed-deployable coverage.
|
||||||
|
- >-
|
||||||
|
State that a pre-rapp running deployable is migration debt and remains
|
||||||
|
unknown to workload controls until declared.
|
||||||
|
- >-
|
||||||
|
Preserve native identity for human access, credential patterns, broker
|
||||||
|
actions, one-shot operations, and infrastructure resources that are not workloads.
|
||||||
|
- Keep Railiance Master as the sole owner of the normative rapp schema vocabulary.
|
||||||
|
constraints:
|
||||||
|
- Do not create a parallel generic workload declaration surface.
|
||||||
|
- Do not require one rapp per process when deployables share rollout and rollback fate.
|
||||||
|
- Do not turn non-workload operational subjects into fake workloads.
|
||||||
|
acceptance:
|
||||||
|
- Normative documentation answers whether operational and tooling deployables require rapps.
|
||||||
|
- The exactly-one-rapp invariant remains explicit and schema-compatible.
|
||||||
|
- Migration debt and not-applicable subjects are distinguishable.
|
||||||
|
- Existing valid rapp declarations continue to validate.
|
||||||
|
verification:
|
||||||
|
- tools/validate-family-declarations.py --repo /home/worsch/rapp-issue-core
|
||||||
|
- tools/validate-family-declarations.py --repo /home/worsch/rapp-qonto
|
||||||
47
interfaces/workload-projection-transport-v1.yaml
Normal file
47
interfaces/workload-projection-transport-v1.yaml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
apiVersion: helixforge.repo-manager/v1
|
||||||
|
kind: OwnerTaskInterface
|
||||||
|
metadata:
|
||||||
|
id: helixforge.workloads.projection-transport.v1
|
||||||
|
title: Transport the authoritative workload projection
|
||||||
|
source:
|
||||||
|
repo: repo-manager
|
||||||
|
workplan_task: RMGR-WP-0010-T04
|
||||||
|
target:
|
||||||
|
repo: hub-core
|
||||||
|
owner_agent: hub-core
|
||||||
|
suggested_work_record: owner-selected hub-core projection workplan task
|
||||||
|
approval:
|
||||||
|
dispositions: [approved, amended, rejected]
|
||||||
|
record_in: hub-core file-backed workplan task or decision
|
||||||
|
task:
|
||||||
|
title: Transport the Repo Manager workload projection
|
||||||
|
priority: medium
|
||||||
|
objective: >-
|
||||||
|
Add a hub-core projection port for Repo Manager workload records without
|
||||||
|
transferring semantic authority or exposing Repo Manager persistence.
|
||||||
|
inputs:
|
||||||
|
- ref: repo-manager/docs/workload-reference-contract_v1.md
|
||||||
|
authority: repo-manager
|
||||||
|
- ref: repo-manager/src/repo_manager/workloads.py
|
||||||
|
authority: repo-manager
|
||||||
|
deliverables:
|
||||||
|
- >-
|
||||||
|
Ingest normalized workload records keyed by rapp_id and
|
||||||
|
workload_identity.name, with optional deployable membership.
|
||||||
|
- >-
|
||||||
|
Retain source repository, declaration path, source Git revision, and a
|
||||||
|
canonical UTC observation timestamp.
|
||||||
|
- Expose read-only cross-repository query through the existing projection port.
|
||||||
|
- Make the projection rebuildable from authoritative declarations.
|
||||||
|
constraints:
|
||||||
|
- hub-core transports and queries the projection but does not own workload semantics.
|
||||||
|
- Do not expose or depend on Repo Manager private persistence models.
|
||||||
|
- Do not infer workload identity from repository names or paths.
|
||||||
|
acceptance:
|
||||||
|
- A nine-declaration Repo Manager fixture can be ingested and queried by exact reference.
|
||||||
|
- Deleting and rebuilding the projection from source yields equivalent records.
|
||||||
|
- Unknown references remain unknown and are not repaired by aliases or path parsing.
|
||||||
|
- All observation timestamps are canonical UTC.
|
||||||
|
verification:
|
||||||
|
- make test
|
||||||
|
- make conformance
|
||||||
49
interfaces/zone-engine-workload-resolution-v1.yaml
Normal file
49
interfaces/zone-engine-workload-resolution-v1.yaml
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
apiVersion: helixforge.repo-manager/v1
|
||||||
|
kind: OwnerTaskInterface
|
||||||
|
metadata:
|
||||||
|
id: helixforge.workloads.zone-engine-resolution.v1
|
||||||
|
title: Consume explicit workload references without inference
|
||||||
|
source:
|
||||||
|
repo: repo-manager
|
||||||
|
workplan_task: RMGR-WP-0010-T07
|
||||||
|
target:
|
||||||
|
repo: zone-engine
|
||||||
|
owner_agent: zone-engine
|
||||||
|
suggested_work_record: ZONE-WP-0001-T07
|
||||||
|
approval:
|
||||||
|
dispositions: [approved, amended, rejected]
|
||||||
|
record_in: ZONE-WP-0001-T07 or a Zone Engine owner decision
|
||||||
|
task:
|
||||||
|
title: Prove Zone Engine workload resolution without inference
|
||||||
|
priority: high
|
||||||
|
objective: >-
|
||||||
|
Join ops-warden lane pointers to authoritative Repo Manager workload
|
||||||
|
projections and expose unresolved workload identity without guessing.
|
||||||
|
inputs:
|
||||||
|
- ref: repo-manager/docs/workload-reference-contract_v1.md
|
||||||
|
authority: repo-manager
|
||||||
|
- ref: helixforge.workloads.ops-warden-reference.v1
|
||||||
|
authority: ops-warden after approval
|
||||||
|
- ref: zone-engine/docs/security-zones_v0.1.md
|
||||||
|
authority: zone-engine draft pending canon adoption
|
||||||
|
deliverables:
|
||||||
|
- Consume exact rapp_id and workload name pairs plus optional deployable.
|
||||||
|
- Preserve resolved, unknown, and owner-asserted not-applicable as distinct states.
|
||||||
|
- Join authoritative projection data to explicit catalog references only.
|
||||||
|
- Emit evidence that name and path drift cannot change zone membership.
|
||||||
|
constraints:
|
||||||
|
- Do not parse repository slugs, owner_repo, credential paths, or path templates as identity.
|
||||||
|
- Unknown must fail closed wherever zone policy requires workload identity.
|
||||||
|
- Zone Engine does not author workload identity or ops-warden lane pointers.
|
||||||
|
acceptance:
|
||||||
|
- A valid exact reference resolves to one authoritative workload projection.
|
||||||
|
- A mismatched qonto versus rapp-qonto name remains unknown.
|
||||||
|
- A non-workload lane remains not-applicable rather than becoming unknown.
|
||||||
|
- No fallback identity path exists in implementation or fixtures.
|
||||||
|
verification:
|
||||||
|
- >-
|
||||||
|
uv run --project /home/worsch/repo-manager rmgr workload resolve --root
|
||||||
|
/home/worsch --rapp-id rapp-issue-core --name issue-core --deployable issue-core
|
||||||
|
- >-
|
||||||
|
uv run --project /home/worsch/repo-manager rmgr workload resolve --root
|
||||||
|
/home/worsch --rapp-id rapp-qonto --name rapp-qonto
|
||||||
72
schemas/owner-task-interface.schema.yaml
Normal file
72
schemas/owner-task-interface.schema.yaml
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
$schema: https://json-schema.org/draft/2020-12/schema
|
||||||
|
$id: https://helixforge.local/repo-manager/owner-task-interface.schema.yaml
|
||||||
|
title: HelixForge owner-task interface
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [apiVersion, kind, metadata, source, target, approval, task]
|
||||||
|
properties:
|
||||||
|
apiVersion:
|
||||||
|
const: helixforge.repo-manager/v1
|
||||||
|
kind:
|
||||||
|
const: OwnerTaskInterface
|
||||||
|
metadata:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [id, title]
|
||||||
|
properties:
|
||||||
|
id: {type: string, minLength: 1}
|
||||||
|
title: {type: string, minLength: 1}
|
||||||
|
source:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [repo, workplan_task]
|
||||||
|
properties:
|
||||||
|
repo: {type: string, minLength: 1}
|
||||||
|
workplan_task: {type: string, minLength: 1}
|
||||||
|
target:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [repo, owner_agent]
|
||||||
|
properties:
|
||||||
|
repo: {type: string, minLength: 1}
|
||||||
|
owner_agent: {type: string, minLength: 1}
|
||||||
|
suggested_work_record: {type: string, minLength: 1}
|
||||||
|
approval:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [dispositions, record_in]
|
||||||
|
properties:
|
||||||
|
dispositions:
|
||||||
|
const: [approved, amended, rejected]
|
||||||
|
record_in: {type: string, minLength: 1}
|
||||||
|
task:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [title, priority, objective, deliverables, acceptance]
|
||||||
|
properties:
|
||||||
|
title: {type: string, minLength: 1}
|
||||||
|
priority: {enum: [high, medium, low]}
|
||||||
|
objective: {type: string, minLength: 1}
|
||||||
|
inputs:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties: false
|
||||||
|
required: [ref, authority]
|
||||||
|
properties:
|
||||||
|
ref: {type: string, minLength: 1}
|
||||||
|
authority: {type: string, minLength: 1}
|
||||||
|
deliverables:
|
||||||
|
type: array
|
||||||
|
minItems: 1
|
||||||
|
items: {type: string, minLength: 1}
|
||||||
|
constraints:
|
||||||
|
type: array
|
||||||
|
items: {type: string, minLength: 1}
|
||||||
|
acceptance:
|
||||||
|
type: array
|
||||||
|
minItems: 1
|
||||||
|
items: {type: string, minLength: 1}
|
||||||
|
verification:
|
||||||
|
type: array
|
||||||
|
items: {type: string, minLength: 1}
|
||||||
|
|
@ -382,6 +382,21 @@ def main(argv: list[str] | None = None) -> int:
|
||||||
p_workload_resolve.add_argument("--name", required=True)
|
p_workload_resolve.add_argument("--name", required=True)
|
||||||
p_workload_resolve.add_argument("--deployable", default=None)
|
p_workload_resolve.add_argument("--deployable", default=None)
|
||||||
|
|
||||||
|
p_owner_interface = sub.add_parser(
|
||||||
|
"owner-interface",
|
||||||
|
help="Inspect validated owner-consumable task interfaces",
|
||||||
|
)
|
||||||
|
owner_interface_sub = p_owner_interface.add_subparsers(dest="owner_interface_command")
|
||||||
|
p_owner_interface_validate = owner_interface_sub.add_parser(
|
||||||
|
"validate", help="Validate and return owner task interfaces"
|
||||||
|
)
|
||||||
|
p_owner_interface_validate.add_argument(
|
||||||
|
"--path", default="interfaces", help="One interface YAML or an interface directory"
|
||||||
|
)
|
||||||
|
p_owner_interface_validate.add_argument(
|
||||||
|
"--owner", default=None, help="Return interfaces for this target repo or owner agent"
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
|
|
||||||
if args.version or args.command in (None, "version"):
|
if args.version or args.command in (None, "version"):
|
||||||
|
|
@ -847,6 +862,16 @@ def main(argv: list[str] | None = None) -> int:
|
||||||
print(json.dumps(result, indent=2))
|
print(json.dumps(result, indent=2))
|
||||||
return 0 if result.get("ok") else 1
|
return 0 if result.get("ok") else 1
|
||||||
|
|
||||||
|
if args.command == "owner-interface":
|
||||||
|
if not args.owner_interface_command:
|
||||||
|
p_owner_interface.print_help()
|
||||||
|
return 2
|
||||||
|
from repo_manager.owner_interfaces import validate_owner_interfaces
|
||||||
|
|
||||||
|
result = validate_owner_interfaces(Path(args.path), owner=args.owner)
|
||||||
|
print(json.dumps(result, indent=2))
|
||||||
|
return 0 if result.get("ok") else 1
|
||||||
|
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
|
||||||
177
src/repo_manager/owner_interfaces.py
Normal file
177
src/repo_manager/owner_interfaces.py
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
"""Validation and rendering for owner-consumable task interfaces."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
API_VERSION = "helixforge.repo-manager/v1"
|
||||||
|
KIND = "OwnerTaskInterface"
|
||||||
|
DISPOSITIONS = ("approved", "amended", "rejected")
|
||||||
|
PRIORITIES = frozenset({"high", "medium", "low"})
|
||||||
|
|
||||||
|
|
||||||
|
def _error(code: str, path: Path, message: str) -> dict[str, str]:
|
||||||
|
return {"code": code, "path": str(path), "message": message}
|
||||||
|
|
||||||
|
|
||||||
|
def _text(value: Any) -> str | None:
|
||||||
|
if not isinstance(value, str):
|
||||||
|
return None
|
||||||
|
value = value.strip()
|
||||||
|
return value or None
|
||||||
|
|
||||||
|
|
||||||
|
def _string_list(value: Any) -> list[str] | None:
|
||||||
|
if not isinstance(value, list) or not value:
|
||||||
|
return None
|
||||||
|
values = [_text(item) for item in value]
|
||||||
|
if any(item is None for item in values):
|
||||||
|
return None
|
||||||
|
return [item for item in values if item is not None]
|
||||||
|
|
||||||
|
|
||||||
|
def _interface_paths(path: Path) -> list[Path]:
|
||||||
|
path = path.expanduser().resolve()
|
||||||
|
if path.is_file():
|
||||||
|
return [path]
|
||||||
|
return sorted(path.glob("*.yaml"))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_owner_interfaces(path: Path, *, owner: str | None = None) -> dict[str, Any]:
|
||||||
|
"""Validate task interfaces and return their directly consumable content."""
|
||||||
|
path = path.expanduser().resolve()
|
||||||
|
errors: list[dict[str, str]] = []
|
||||||
|
interfaces: list[dict[str, Any]] = []
|
||||||
|
seen_ids: set[str] = set()
|
||||||
|
|
||||||
|
paths = _interface_paths(path)
|
||||||
|
if not paths:
|
||||||
|
errors.append(_error("interface_not_found", path, "no interface YAML files found"))
|
||||||
|
|
||||||
|
for interface_path in paths:
|
||||||
|
try:
|
||||||
|
raw = yaml.safe_load(interface_path.read_text(encoding="utf-8")) or {}
|
||||||
|
except (OSError, yaml.YAMLError) as exc:
|
||||||
|
errors.append(_error("interface_invalid", interface_path, str(exc)))
|
||||||
|
continue
|
||||||
|
if not isinstance(raw, dict):
|
||||||
|
errors.append(_error("interface_invalid", interface_path, "root must be a mapping"))
|
||||||
|
continue
|
||||||
|
if raw.get("apiVersion") != API_VERSION or raw.get("kind") != KIND:
|
||||||
|
errors.append(
|
||||||
|
_error(
|
||||||
|
"interface_kind_invalid",
|
||||||
|
interface_path,
|
||||||
|
f"expected apiVersion {API_VERSION!r} and kind {KIND!r}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
metadata = raw.get("metadata")
|
||||||
|
source = raw.get("source")
|
||||||
|
target = raw.get("target")
|
||||||
|
approval = raw.get("approval")
|
||||||
|
task = raw.get("task")
|
||||||
|
if not all(isinstance(item, dict) for item in (metadata, source, target, approval, task)):
|
||||||
|
errors.append(
|
||||||
|
_error(
|
||||||
|
"interface_shape_invalid",
|
||||||
|
interface_path,
|
||||||
|
"metadata, source, target, approval, and task must be mappings",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
interface_id = _text(metadata.get("id"))
|
||||||
|
title = _text(metadata.get("title"))
|
||||||
|
source_repo = _text(source.get("repo"))
|
||||||
|
source_task = _text(source.get("workplan_task"))
|
||||||
|
target_repo = _text(target.get("repo"))
|
||||||
|
owner_agent = _text(target.get("owner_agent"))
|
||||||
|
task_title = _text(task.get("title"))
|
||||||
|
objective = _text(task.get("objective"))
|
||||||
|
priority = _text(task.get("priority"))
|
||||||
|
deliverables = _string_list(task.get("deliverables"))
|
||||||
|
acceptance = _string_list(task.get("acceptance"))
|
||||||
|
dispositions = approval.get("dispositions")
|
||||||
|
|
||||||
|
required = {
|
||||||
|
"metadata.id": interface_id,
|
||||||
|
"metadata.title": title,
|
||||||
|
"source.repo": source_repo,
|
||||||
|
"source.workplan_task": source_task,
|
||||||
|
"target.repo": target_repo,
|
||||||
|
"target.owner_agent": owner_agent,
|
||||||
|
"task.title": task_title,
|
||||||
|
"task.objective": objective,
|
||||||
|
}
|
||||||
|
missing = [name for name, value in required.items() if value is None]
|
||||||
|
if missing:
|
||||||
|
errors.append(
|
||||||
|
_error(
|
||||||
|
"interface_required_field_missing",
|
||||||
|
interface_path,
|
||||||
|
"missing non-empty fields: " + ", ".join(missing),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if priority not in PRIORITIES:
|
||||||
|
errors.append(
|
||||||
|
_error(
|
||||||
|
"interface_priority_invalid",
|
||||||
|
interface_path,
|
||||||
|
"task.priority must be high, medium, or low",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if deliverables is None or acceptance is None:
|
||||||
|
errors.append(
|
||||||
|
_error(
|
||||||
|
"interface_task_invalid",
|
||||||
|
interface_path,
|
||||||
|
"task.deliverables and task.acceptance must be non-empty string lists",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if dispositions != list(DISPOSITIONS):
|
||||||
|
errors.append(
|
||||||
|
_error(
|
||||||
|
"interface_approval_invalid",
|
||||||
|
interface_path,
|
||||||
|
"approval.dispositions must be [approved, amended, rejected]",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if "status" in raw or "status" in metadata:
|
||||||
|
errors.append(
|
||||||
|
_error(
|
||||||
|
"interface_lifecycle_forbidden",
|
||||||
|
interface_path,
|
||||||
|
"an interface is not a work record and must not carry lifecycle status",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if interface_id in seen_ids:
|
||||||
|
errors.append(
|
||||||
|
_error("interface_id_duplicate", interface_path, f"duplicate id {interface_id!r}")
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
seen_ids.add(interface_id)
|
||||||
|
|
||||||
|
if owner is None or owner in {target_repo, owner_agent}:
|
||||||
|
document = dict(raw)
|
||||||
|
document["path"] = str(interface_path)
|
||||||
|
interfaces.append(document)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"ok": not errors,
|
||||||
|
"apiVersion": API_VERSION,
|
||||||
|
"kind": KIND,
|
||||||
|
"root": str(path),
|
||||||
|
"interface_count": len(interfaces),
|
||||||
|
"interfaces": interfaces,
|
||||||
|
"errors": errors,
|
||||||
|
}
|
||||||
74
tests/test_owner_interfaces.py
Normal file
74
tests/test_owner_interfaces.py
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from repo_manager.owner_interfaces import validate_owner_interfaces
|
||||||
|
|
||||||
|
|
||||||
|
def _write_interface(path: Path, *, interface_id: str = "example.port.v1") -> None:
|
||||||
|
path.write_text(
|
||||||
|
f"""apiVersion: helixforge.repo-manager/v1
|
||||||
|
kind: OwnerTaskInterface
|
||||||
|
metadata:
|
||||||
|
id: {interface_id}
|
||||||
|
title: Example port
|
||||||
|
source:
|
||||||
|
repo: repo-manager
|
||||||
|
workplan_task: RMGR-WP-0010-T04
|
||||||
|
target:
|
||||||
|
repo: hub-core
|
||||||
|
owner_agent: hub-core
|
||||||
|
approval:
|
||||||
|
dispositions: [approved, amended, rejected]
|
||||||
|
record_in: target owner work record
|
||||||
|
task:
|
||||||
|
title: Adopt the example port
|
||||||
|
priority: medium
|
||||||
|
objective: Consume the published contract without copying its authority.
|
||||||
|
deliverables:
|
||||||
|
- Add the consumer port.
|
||||||
|
acceptance:
|
||||||
|
- Prove an authoritative record can be consumed.
|
||||||
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_validates_and_filters_owner_consumable_interface(tmp_path: Path) -> None:
|
||||||
|
_write_interface(tmp_path / "example.yaml")
|
||||||
|
|
||||||
|
result = validate_owner_interfaces(tmp_path, owner="hub-core")
|
||||||
|
|
||||||
|
assert result["ok"] is True
|
||||||
|
assert result["interface_count"] == 1
|
||||||
|
assert result["interfaces"][0]["metadata"]["id"] == "example.port.v1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_interface_for_another_owner_is_not_returned(tmp_path: Path) -> None:
|
||||||
|
_write_interface(tmp_path / "example.yaml")
|
||||||
|
|
||||||
|
result = validate_owner_interfaces(tmp_path, owner="ops-warden")
|
||||||
|
|
||||||
|
assert result["ok"] is True
|
||||||
|
assert result["interfaces"] == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_rejects_lifecycle_status_because_interface_is_not_work_record(tmp_path: Path) -> None:
|
||||||
|
path = tmp_path / "example.yaml"
|
||||||
|
_write_interface(path)
|
||||||
|
path.write_text(path.read_text(encoding="utf-8") + "status: wait\n", encoding="utf-8")
|
||||||
|
|
||||||
|
result = validate_owner_interfaces(path)
|
||||||
|
|
||||||
|
assert result["ok"] is False
|
||||||
|
assert result["errors"][0]["code"] == "interface_lifecycle_forbidden"
|
||||||
|
|
||||||
|
|
||||||
|
def test_rejects_duplicate_interface_ids(tmp_path: Path) -> None:
|
||||||
|
_write_interface(tmp_path / "one.yaml")
|
||||||
|
_write_interface(tmp_path / "two.yaml")
|
||||||
|
|
||||||
|
result = validate_owner_interfaces(tmp_path)
|
||||||
|
|
||||||
|
assert result["ok"] is False
|
||||||
|
assert result["errors"][0]["code"] == "interface_id_duplicate"
|
||||||
|
|
@ -4,7 +4,7 @@ type: workplan
|
||||||
title: "Authoritative workload declarations and explicit references"
|
title: "Authoritative workload declarations and explicit references"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: repo-manager
|
repo: repo-manager
|
||||||
status: active
|
status: finished
|
||||||
owner: codex
|
owner: codex
|
||||||
topic_slug: infotech
|
topic_slug: infotech
|
||||||
created: "2026-08-22"
|
created: "2026-08-22"
|
||||||
|
|
@ -13,6 +13,7 @@ related:
|
||||||
- ZONE-WP-0001
|
- ZONE-WP-0001
|
||||||
- RMASTER-ADR-0007
|
- RMASTER-ADR-0007
|
||||||
- WARDEN-WP-0032
|
- WARDEN-WP-0032
|
||||||
|
- RMGR-ADR-005
|
||||||
state_hub_workstream_id: "f1fdfd70-76e0-5133-8365-4e68b9e9ca12"
|
state_hub_workstream_id: "f1fdfd70-76e0-5133-8365-4e68b9e9ca12"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -73,70 +74,80 @@ declarations, rejects duplicate deployable ownership, reports missing rapp
|
||||||
declarations, and returns `unknown` rather than guessing. Unit tests cover
|
declarations, and returns `unknown` rather than guessing. Unit tests cover
|
||||||
positive resolution and fail-closed cases.
|
positive resolution and fail-closed cases.
|
||||||
|
|
||||||
## Publish the normalized projection port
|
## Publish the normalized projection owner interface
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: RMGR-WP-0010-T04
|
id: RMGR-WP-0010-T04
|
||||||
status: wait
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "187fed63-63ab-57b3-92cb-83acf5870977"
|
state_hub_task_id: "187fed63-63ab-57b3-92cb-83acf5870977"
|
||||||
```
|
```
|
||||||
|
|
||||||
Expose the v1 index through the Repo Manager observation contract and hub-core
|
Supply hub-core with a directly adoptable task interface for the v1 projection,
|
||||||
port with source revision, declaration path, and observation timestamp. Do not
|
including provenance, canonical UTC observation time, rebuild semantics, and
|
||||||
expose private persistence models.
|
the private-persistence boundary.
|
||||||
|
|
||||||
**Handoff (2026-08-22):** sent hub-core message
|
**Result (2026-08-22):**
|
||||||
`1ea93859-7cdf-4c8e-86d6-003e93e70eef`; waiting for its owner workplan/task
|
`interfaces/workload-projection-transport-v1.yaml` is the complete owner-ready
|
||||||
identifier.
|
interface. Initial routing message:
|
||||||
|
`1ea93859-7cdf-4c8e-86d6-003e93e70eef`.
|
||||||
|
|
||||||
## Add authoritative estate coverage
|
## Publish the authoritative estate-coverage owner interface
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: RMGR-WP-0010-T05
|
id: RMGR-WP-0010-T05
|
||||||
status: wait
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "71a4dae7-8484-57f7-96b9-c2ef93aa4642"
|
state_hub_task_id: "71a4dae7-8484-57f7-96b9-c2ef93aa4642"
|
||||||
```
|
```
|
||||||
|
|
||||||
Join declarations to an authoritative runtime/deployable observation source and
|
Supply Railiance Master with a directly adoptable schema-owner task clarifying
|
||||||
report running units lacking rapp coverage. Repository names are insufficient
|
universal managed-deployable coverage, migration debt, and the non-workload
|
||||||
evidence. Separate non-workload operational subjects from undeclared workloads.
|
boundary.
|
||||||
|
|
||||||
**Handoff (2026-08-22):** sent Railiance Master message
|
**Result (2026-08-22):** `interfaces/rapp-workload-coverage-v1.yaml` is the
|
||||||
`d8e64195-9117-4439-bc67-d2e7696a9bf5` for the normative coverage clarification
|
complete owner-ready interface. Initial routing message:
|
||||||
and an owner work record. Runtime observation-source selection remains open.
|
`d8e64195-9117-4439-bc67-d2e7696a9bf5`. Runtime observation-source selection
|
||||||
|
belongs in the adopting owner work record, not as a duplicate Repo Manager todo.
|
||||||
|
|
||||||
## Adopt explicit ops-warden references
|
## Publish the ops-warden reference owner interface
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: RMGR-WP-0010-T06
|
id: RMGR-WP-0010-T06
|
||||||
status: wait
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "72aa51a0-2a06-571c-967d-fb18ee96b30f"
|
state_hub_task_id: "72aa51a0-2a06-571c-967d-fb18ee96b30f"
|
||||||
```
|
```
|
||||||
|
|
||||||
Ops-warden owns its catalog schema. Add an explicit workload reference and an
|
Supply ops-warden with the exact catalog adoption task, including field
|
||||||
unambiguous not-applicable/unknown distinction, then validate every resolved
|
semantics, owner boundary, validation, and no-inference acceptance tests.
|
||||||
reference through Repo Manager. Do not copy classification or criticality.
|
|
||||||
|
|
||||||
**Handoff (2026-08-22):** sent ops-warden message
|
**Result (2026-08-22):**
|
||||||
`ca18c1ab-540f-4765-89ba-43c068bb49d2`; waiting for its adoption workplan/task
|
`interfaces/ops-warden-workload-reference-v1.yaml` is directly adoptable by
|
||||||
identifier.
|
`WARDEN-WP-0032`. Initial routing message:
|
||||||
|
`ca18c1ab-540f-4765-89ba-43c068bb49d2`.
|
||||||
|
|
||||||
## Prove Zone Engine consumption
|
## Publish the Zone Engine consumption owner interface
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: RMGR-WP-0010-T07
|
id: RMGR-WP-0010-T07
|
||||||
status: wait
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "b68f40b1-cf63-589f-96ee-c6c1098d9802"
|
state_hub_task_id: "b68f40b1-cf63-589f-96ee-c6c1098d9802"
|
||||||
```
|
```
|
||||||
|
|
||||||
Zone Engine consumes the declaration projection and explicit catalog references,
|
Supply Zone Engine with a directly adoptable join-and-proof task that consumes
|
||||||
reports unknown subjects without inference, and demonstrates that path/repo-name
|
the declaration projection and explicit catalog references, reports unknown
|
||||||
drift cannot alter membership.
|
subjects, and prohibits path/repository-name inference.
|
||||||
|
|
||||||
**Handoff (2026-08-22):** replied through Zone Engine message
|
**Result (2026-08-22):**
|
||||||
`a271dc75-b2c9-4c9a-a91c-20aaa1cfb5dc`; waiting for its adoption/proof task
|
`interfaces/zone-engine-workload-resolution-v1.yaml` is directly adoptable by
|
||||||
identifier.
|
`ZONE-WP-0001-T07`, which already links `RMGR-WP-0010-T07`. Initial routing
|
||||||
|
message: `a271dc75-b2c9-4c9a-a91c-20aaa1cfb5dc`.
|
||||||
|
|
||||||
|
## Completion
|
||||||
|
|
||||||
|
The Repo Manager boundary is complete. `RMGR-ADR-005` makes interface
|
||||||
|
publication the producer-side terminal action: approval, amendment, and
|
||||||
|
implementation live only in each target owner's file-backed work record. No
|
||||||
|
cross-owner implementation todo remains duplicated in this workplan.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue