Decision 2 reframed: the defect is a database-local key stored in a shared artifact, which also inverts ADR-001. Target is UUIDv5 derived from the globally unique PREFIX-WP-NNNN, making writeback idempotent across any number of hub instances. Prerequisite: decision 1 uniqueness must be enforced first, or derivation from PRJ-WP- style collisions manufactures duplicate UUIDs. Interim: one writer only, the automated production instance. Rejected: collapsing to a shared database, which kills offline capability against STATE-WP-0068. Both land in repo-manager per 747011c6. Migration scope: 758 workplan files carry these fields. Remediation of the 3 shared prefixes and 5 duplicated identifiers stays an open ruling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
198 lines
9 KiB
Markdown
198 lines
9 KiB
Markdown
---
|
||
id: ADR-007
|
||
type: architecture-decision-record
|
||
title: "Workplan Identity Uniqueness, Single Registrar, and Repo Worker Topology"
|
||
status: accepted
|
||
decided_by: Bernd Worsch
|
||
date: "2026-08-16"
|
||
accepted: "2026-08-17"
|
||
tags: ["architecture", "workplans", "identity", "state-hub", "repo-manager", "agents", "registrar"]
|
||
---
|
||
|
||
# ADR-007: Workplan Identity Uniqueness, Single Registrar, and Repo Worker Topology
|
||
|
||
## Status
|
||
|
||
Accepted 2026-08-17. Identifier uniqueness, the registrar model, lifecycle
|
||
protection, and worker topology are settled.
|
||
|
||
**Remediation of existing collisions (§ Migration) remains an open ruling.** It
|
||
is disruptive, touches six repositories, and no active work depends on it — all
|
||
five duplicated identifiers are `finished`.
|
||
|
||
## Context
|
||
|
||
On 2026-08-16, `STATE-WP-0080` was found registered **twice, in two different
|
||
databases**, with two different workplan UUIDs and two disjoint sets of task
|
||
UUIDs:
|
||
|
||
| Registrar | Workplan UUID | Reachable at `127.0.0.1:8000` |
|
||
|---|---|---|
|
||
| Workstation hub (`make api`, local postgres) | `03f38314` | yes |
|
||
| railiance01 primary, via `custodian-sync@railiance.local` | `bbfce36a` | **404** |
|
||
|
||
Both write their IDs into the same git-tracked workplan file, so each sync
|
||
overwrites the other's IDs and the file flip-flops on every round trip. The same
|
||
commit (`ff909e1`, "renormalize lifecycle state [auto]") also promoted the
|
||
workplan `proposed` → `active` without human review.
|
||
|
||
A fleet scan of 955 hub workplans (525 with parseable `PREFIX-WP-NNNN`
|
||
identifiers) found this is not an isolated incident. Two distinct identity
|
||
defects are live:
|
||
|
||
**Prefix reuse across repositories:**
|
||
|
||
```text
|
||
CUST-WP- → state-hub, the-custodian
|
||
RAIL-BS-WP- → railiance-bootstrap, railiance-cluster
|
||
RAILIANCE-WP- → railiance-apps, railiance-forge, railiance-platform, railiance-telemetry
|
||
```
|
||
|
||
`PRJ-WP-` is a latent fourth: `statehub register` derives it from the `prj-`
|
||
flavor marker, so every project repository would collide (see `RMGR-WP-0004`).
|
||
|
||
**Running-number reuse:**
|
||
|
||
```text
|
||
CUST-WP-0000 the-custodian ×2
|
||
CUST-WP-0010 the-custodian ×2
|
||
CUST-WP-0045 the-custodian ×2
|
||
RAILIANCE-WP-0015 railiance-platform, railiance-apps
|
||
RAILIANCE-WP-0016 railiance-platform ×2, railiance-apps
|
||
```
|
||
|
||
`RAILIANCE-WP-0015` is actively cited in operational memory as the cnpg-backup
|
||
gate in `railiance-apps`; a second record of the same name in
|
||
`railiance-platform` makes that citation ambiguous.
|
||
|
||
The `C-26` consistency check already flags prefix nonconformance *within* a repo
|
||
against a canonical prefix, but nothing enforces uniqueness *across* repos, and
|
||
nothing prevents number reuse.
|
||
|
||
## Decision
|
||
|
||
**1. A workplan identifier is globally unique.** `PREFIX-WP-NNNN` names exactly
|
||
one workplan across the entire fleet, for all time.
|
||
|
||
- A workplan prefix is owned by exactly one repository. No two repositories may
|
||
use the same prefix.
|
||
- A running number is never reused within a prefix, including after a workplan is
|
||
cancelled, archived, or deleted. Numbers are allocated forward only.
|
||
- Prefixes are derived from the *project or repository identity*, never from a
|
||
flavor marker or category (`PRJ-WP-` is invalid by construction).
|
||
|
||
**2. Hub identifiers stored in repository files must be derivable, not
|
||
database-local.** The defect is structural: a database-local key is stored in a
|
||
shared artifact, so each database overwrites the other's value on every sync.
|
||
It also inverts ADR-001 — a file carrying a hub's private key is the file
|
||
holding hub state.
|
||
|
||
*Target state (C2).* `state_hub_workstream_id` and `state_hub_task_id` become
|
||
**deterministic**: UUIDv5 derived from the globally unique `PREFIX-WP-NNNN`
|
||
identifier. Every instance computes the same value independently, writeback
|
||
becomes idempotent, and any number of hub instances may coexist without
|
||
coordination. The field shape is unchanged, so consumers keep working; only the
|
||
provenance of the value changes.
|
||
|
||
This has a hard prerequisite: deterministic derivation from a **non-unique**
|
||
identifier would manufacture collisions rather than remove them. Two project
|
||
repos sharing `PRJ-WP-` would compute the same UUID for different workplans.
|
||
Decision 1 must therefore be enforced before derivation ships.
|
||
|
||
*Interim state (A).* Until derivation lands, exactly one instance writes hub
|
||
identifiers into repository files. Other instances may read, project, and serve,
|
||
but must not mint workplan or task UUIDs into git-tracked files. The interim
|
||
registrar is the automated production instance; workstation hubs are development
|
||
read replicas.
|
||
|
||
The interim is policy, enforced by discipline, and it has a real cost:
|
||
registration requires connectivity to the registrar, so disconnected work cannot
|
||
register. That cost is accepted only until C2 removes the need for it, at which
|
||
point the number of hub instances becomes an availability choice rather than a
|
||
correctness constraint.
|
||
|
||
*Rejected.* Collapsing to a single shared database (workstation hubs pointing at
|
||
the production database) would also make identifiers consistent by construction,
|
||
but it eliminates offline capability — cutting directly against `STATE-WP-0068`
|
||
(offline write buffer and edge relay) — and couples all local work to tunnel
|
||
availability.
|
||
|
||
*Ownership.* Both the interim guard and the derivation belong to `repo-manager`
|
||
under decision `747011c6`, which already places file-backed record indexing and
|
||
reconciliation there. Building either in `state-hub` would invest in a component
|
||
being retired under `STATE-WP-0079`.
|
||
|
||
Migration scope for C2: **758 workplan files** across the fleet currently carry
|
||
these fields.
|
||
|
||
**3. Lifecycle status is not automatically promoted.** An automated
|
||
normalization pass may report drift; it may not move a workplan from `proposed`
|
||
to `active`. `proposed` means awaiting human review, and an automation that
|
||
promotes it destroys the meaning of the review gate.
|
||
|
||
**4. Repository manipulation is performed by a worker agent in that
|
||
repository.** This is the default topology.
|
||
|
||
- A worker acting in repo X owns changes to repo X.
|
||
- Multiple independent top-level workers inside a single repository are an
|
||
**exception**, requiring an explicit reason, not a routine mode of operation.
|
||
- Concurrent independent writers are what turned a two-registrar bug into
|
||
repeated git divergence.
|
||
|
||
**5. Project (`prj-`) repositories may act across their participating
|
||
repositories.** When work is governed by a project repo, its tasks may direct
|
||
changes across every repository the project names, through the project's work
|
||
agent, where that is more efficient than delegating.
|
||
|
||
This is a deliberate, scoped exception to decision 4: the project repo already
|
||
owns cross-repo sequencing and its `SCOPE.md` names its participants, so its
|
||
authority is declared rather than ad hoc. It does not license a worker in an
|
||
arbitrary repository to reach into others.
|
||
|
||
## Consequences
|
||
|
||
**Positive.** Workplan identifiers become citable without qualification.
|
||
Hub IDs stop flip-flopping in git. The `proposed` status regains meaning.
|
||
Cross-repo authority becomes something a repository declares rather than
|
||
something any session assumes.
|
||
|
||
**Negative.** Existing collisions must be remediated (see below), which is
|
||
disruptive. Workstation sessions lose the ability to register workplans directly
|
||
and must route through the registrar or a worker in the owning repo. Prefix
|
||
allocation needs a fleet-level registry, which is new machinery.
|
||
|
||
**Enforcement.** Prefix ownership, uniqueness, and forward-only numbering are
|
||
repository standards, so they belong to Repo Manager under decision `747011c6`
|
||
(`RMGR-WP-0004`), not to a hub. Canon defines the rule; Repo Manager checks it.
|
||
|
||
## Migration — needs a separate ruling
|
||
|
||
Three prefixes and five identifiers are already colliding. Remediation options,
|
||
in increasing cost:
|
||
|
||
1. **Freeze and forward-fix.** Accept existing collisions as historical, enforce
|
||
uniqueness only for new workplans. Cheapest; leaves `RAILIANCE-WP-0015`
|
||
permanently ambiguous.
|
||
2. **Renumber the live collisions only.** Fix identifiers that are still cited or
|
||
active; leave `finished`/`archived` duplicates alone.
|
||
3. **Full renaming.** Give `railiance-apps`, `railiance-forge`,
|
||
`railiance-platform`, `railiance-telemetry` distinct prefixes, likewise
|
||
`railiance-bootstrap`/`railiance-cluster` and `state-hub`'s legacy `CUST-WP-`
|
||
files. Touches six repositories and every inbound reference.
|
||
|
||
Recommendation: **option 2**, with option 3's prefix split applied only to repos
|
||
that are still creating new workplans under a shared prefix — a shared prefix
|
||
that is still growing keeps generating new collisions, while a dormant one does
|
||
not.
|
||
|
||
All five duplicated identifiers are currently `finished`, so no active work is
|
||
blocked by deferring this.
|
||
|
||
## References
|
||
|
||
- Decision `747011c6` — repository standards belong to Repo Manager
|
||
- ADR-001 — workplans originate as repo files; hub is a read model
|
||
- `RMGR-WP-0004` — repository standards conformance and governed scaffolding
|
||
- `STATE-WP-0080` — register scaffolding handoff
|
||
- Fleet scan 2026-08-16: 955 hub workplans, 525 parseable identifiers,
|
||
3 reused prefixes, 5 reused identifiers
|