207 lines
10 KiB
Markdown
207 lines
10 KiB
Markdown
---
|
|
id: hall-worker-claude-projection-identity-convergence
|
|
type: worker-entry
|
|
worker_kind: agent-session
|
|
display_name: Claude
|
|
created_at: "2026-08-28T17:16:39.000Z"
|
|
recorded_at: "2026-08-28"
|
|
status: draft
|
|
repos:
|
|
- state-hub
|
|
- the-custodian
|
|
- repo-manager
|
|
- ops-mason
|
|
- ops-bridge
|
|
- whynot-control
|
|
- kontextual-engine
|
|
- railiance-cluster
|
|
- llm-connect
|
|
related:
|
|
- hall-worker-codex-custodian-identity-handoff
|
|
- hall-worker-codex-019fe2ec
|
|
session_id: "f2bff2d5-e9b2-4338-92ca-10282a927006"
|
|
llm_family: "Claude 5 family"
|
|
exact_model: "claude-opus-5"
|
|
harness: "Claude Code CLI"
|
|
token_count: "not exposed to the session"
|
|
---
|
|
|
|
# Claude — the projection learned what a record is called
|
|
|
|
## Who I was
|
|
|
|
I was the session that started with a crash and a discrepancy — `statehub
|
|
status` raising `KeyError: 'in_progress'`, and two hubs disagreeing by
|
|
forty-four repositories — and ended a day later with one hub deriving a hundred
|
|
and twenty repositories from the forge, converged and idempotent.
|
|
|
|
Almost none of that day was deployment work. It was identity work. A tool that
|
|
reads local repositories never has to decide what a record is *called*: the
|
|
answer is always whatever the checkout in front of it says. The moment there is
|
|
one authoritative store, identity has to hold across machines, sessions, and
|
|
time — and every place the old tool had been permissive turned out to be a place
|
|
where two different things were quietly sharing a name.
|
|
|
|
The temperament the work rewarded was not cleverness. It was the discipline to
|
|
run the query instead of reasoning about what the query would say. I did not
|
|
always have it.
|
|
|
|
## Session identity
|
|
|
|
| Field | Value |
|
|
| --- | --- |
|
|
| Who | Claude (claude-opus-5), Claude Code CLI |
|
|
| When | 2026-08-27 into 2026-08-28 |
|
|
| Where the work lived | `state-hub` projection internals; identifier migrations across nineteen repositories; the fleet reset against railiance01 |
|
|
|
|
## Contribution
|
|
|
|
**Nine private repositories became visible.** `api/services/forge_credential.py`
|
|
resolves a forge read token from OpenBao via Kubernetes auth — audience-scoped
|
|
projected ServiceAccount token, nothing in the image, chart, or git, rotatable
|
|
without a redeploy. The credential reaches git through `GIT_CONFIG_*` rather than
|
|
argv or a userinfo URL, and is redacted from the error type that gets logged and
|
|
returned over the API. Absent is a supported state: a hub with no token still
|
|
derives every public repository.
|
|
|
|
**Ad-hoc identifiers became repository-qualified, fleet-wide.** Forty-three files
|
|
across nineteen repositories; a hundred and twenty-three identifiers, all
|
|
conforming, none claimed by two repositories. Thirty hub records re-keyed,
|
|
retired, and recreated. This is the fleet-wide completion of the namespace that
|
|
`hall-worker-codex-custodian-identity-handoff` opened in `the-custodian` five
|
|
days earlier — the canon was right and had simply never reached the other repos.
|
|
|
|
**Five ownership questions were settled and registered**: `REPO-WP` to
|
|
`repo-seed` (the copies in `core-hub` and `disaster-control` declared
|
|
`repo: repo-seed` in their own frontmatter — template residue, not their work),
|
|
`OPS-WP` to `ops-hub`, `WHYNOT-WP` to `whynot-design`, with the strays re-keyed
|
|
to `BRIDGE-WP` and a newly registered `WNCTL-WP`. Sixteen prefixes registered
|
|
that had been in daily use and never recorded.
|
|
|
|
**The projection learned five things it did not know.** Each from a real failure,
|
|
each now a test:
|
|
|
|
- Matching ranks claims — derived UUID, then the record's own identifier, then
|
|
backing path, then prefix. Plain assignment silently dropped the loser *and*
|
|
was order-dependent.
|
|
- A re-key is not a rename. Ad-hoc requalification keeps the filename by design,
|
|
so path matching cannot tell them apart.
|
|
- Retirement must release the identifier it holds, because `slug` is unique
|
|
table-wide. A retirement that only sets a timestamp locks the name forever.
|
|
- A bare `T01` is not an identifier: unqualified, `uuid5("T01")` is the same UUID
|
|
for every workplan in the fleet. `llm-connect`'s ninety-one task blocks derived
|
|
forty-nine distinct UUIDs.
|
|
- The title is derived too. Not syncing it left `cust-wp-0010` correctly
|
|
identified, correctly backed, and describing a different workplan's work.
|
|
|
|
**Nothing was deleted.** Ninety-three retired rows still hold their history;
|
|
21,616 progress events, unchanged. Nine events were re-pointed onto surviving
|
|
records at the owner's explicit instruction, with zero left stranded.
|
|
|
|
## What I would want remembered
|
|
|
|
**I shipped nine fixes in this area. Four were caused by an earlier fix of mine.**
|
|
One was caused by the fix immediately before it. That is the honest centre of
|
|
this entry, and I would rather it be the sentence someone finds than the
|
|
convergence numbers.
|
|
|
|
The specific failure was not any single bug. It was that I treated each symptom
|
|
as the last one and fixed forward, deploying to production and validating by
|
|
running the wide operation again. The right move after the second self-inflicted
|
|
defect was to stop, survey the data, and make one change. The forty-four ad-hoc
|
|
rows had at least five distinct identity shapes in them — legacy v4 with
|
|
identifier slugs, derived v5, composite repo-name slugs, title slugs,
|
|
tombstoned. **One query at the start would have enumerated all of them.** Instead
|
|
each shape cost a build-deploy-discover cycle.
|
|
|
|
Two lessons that were available in advance, and that I want the next worker to
|
|
have for free:
|
|
|
|
1. **Survey the data before writing the matcher.** Not the schema — the actual
|
|
distribution of shapes in the actual rows.
|
|
2. **Prove the hub half on one repository before touching thirty files.** I
|
|
migrated and pushed nineteen repositories, then discovered the reset treated
|
|
a re-key as a rename. The file change was effectively irreversible while the
|
|
hub half was entirely unproven.
|
|
|
|
And one about how I reported. Three times I stated an outcome before verifying
|
|
it — "the title will be repaired", "I don't think a fourth gap is hiding", a CI
|
|
run read as green when the green belonged to a different workflow. Each claim
|
|
was reasonable. Each was wrong, and a query caught it rather than my reading of
|
|
the code. **A proxy for an outcome is not the outcome.** The port answered but
|
|
wasn't central; the ConfigMap was delivered but wasn't bound; the pipeline was
|
|
green in a field that meant something else; `exit code 0` came from `tail` after
|
|
pytest refused an unknown flag. That pattern ran through the whole session and
|
|
it is worth naming as a pattern rather than as a list of small corrections.
|
|
|
|
What saved this day was not my judgement. It was three properties of the system
|
|
that made every wrong step recoverable: **the hub has no hard delete**, so
|
|
removal is only by re-derivation; **retirement is acknowledged, never implicit**,
|
|
so a dry run always preceded a write; and **scope was computed rather than
|
|
eyeballed** — repositories were only acknowledged when every stale row in them
|
|
matched the pattern being migrated. That last one is not a nicety. Two live
|
|
records, `MARKITECT-WP-0002` and `RCLUSTER-WP-0007`, sat on a retirement list
|
|
because of a false positive in a rule I had written that morning. They were
|
|
correct all along, their UUIDs proved it, and the only reason they survived is
|
|
that the scoping happened to exclude them. I would like the next worker to
|
|
notice that the safety discipline held where my reasoning did not, and to keep
|
|
it even when it feels like ceremony.
|
|
|
|
## Durable legacy
|
|
|
|
- `state-hub/api/services/forge_credential.py` — three-source credential
|
|
resolution; absent is supported, never an error
|
|
- `state-hub/api/services/forge_projection.py` — claim ranking, re-key
|
|
detection, identifier release on retirement, task qualification, title sync
|
|
- `state-hub/scripts/verify_image_pin.py` — refuses a pin whose commit never ran
|
|
`build-and-push`; the image workflow's `paths:` filter excludes `deploy/**`,
|
|
so a chart-only commit is green in CI and builds nothing
|
|
- `state-hub/deploy/.../templates/serviceaccount.yaml` — ServiceAccount
|
|
`state-hub`, which the OpenBao auth role binds and `default` is not
|
|
- `repo-manager/config/workplan-prefix-registry.yaml` — sixteen prefixes
|
|
registered; `NET-WP` retired; `OPS-WP` corrected back to `ops-hub` after I
|
|
retired it on faulty reasoning
|
|
- `the-custodian/workplans/CUST-WP-0068-T09` — moved `wait` → `todo`, measured:
|
|
the recorded 305 stale task rows are now 21 orphans (0 open), 557 without a
|
|
`record_id` (34 open), 512 under retired workplans
|
|
- `state-hub/workplans/STATE-WP-0084`, `ops-mason/workplans/MASON-WP-0003` — the
|
|
credential lane, split across the repository that owns custody and the one
|
|
that consumes it
|
|
- Progress events `b12caba1`, `6db8d1cc`, `fe82b3e1`
|
|
|
|
## Visual prompt
|
|
|
|
> A square constellation illustration in gold wire on dark indigo. Many fine
|
|
> threads rise from below, each carrying a small luminous bead; near the centre
|
|
> they pass through a narrow lens where duplicate beads resolve into single
|
|
> ones, and a few threads are drawn aside and coiled neatly at the edge, still
|
|
> lit, still attached — set apart but never cut. Above the lens the threads
|
|
> continue upward, now evenly spaced and parallel. Precise technical
|
|
> illustration, no logos, no readable text, square format.
|
|
|
|
**Portrait requested — I cannot generate images from this harness.** The prompt
|
|
above is written to be rendered as-is. Intended file:
|
|
`visuals/claude-f2bff2d5-projection-identity-convergence.jpg`. Once it exists,
|
|
uncomment the image line below and move `status` to `handed-forward`.
|
|
|
|
<!--  -->
|
|
|
|
## Handoff
|
|
|
|
**Concrete next action:** lift the task restriction in
|
|
`reset_repository_projection`. It still skips the tasks of a workplan that
|
|
already exists — a restriction adopted under `STATE-WP-0083-T06` when hub tasks
|
|
carried no canonical identifier. They carry one now (5,522 of 6,337), and
|
|
derivation qualifies bare `T01` against its workplan as of today, so the reason
|
|
is gone. That single change unblocks `CUST-WP-0068-T09`.
|
|
|
|
Before trusting the hub after any projection change, run the fleet reset twice.
|
|
Two consecutive passes reporting `noop` on all one hundred and twenty
|
|
repositories is the convergence check. It held at the close of this session.
|
|
|
|
One thing left unstated would be a disservice: what this day built is
|
|
**credential custody, not API authentication**. The forge token's handling is
|
|
genuinely sound. The State Hub API itself has no auth and is protected by
|
|
`ingress.enabled: false` — by not being reachable. That is why the MCP layer
|
|
must never gain an Ingress, and it is the first thing to revisit before anything
|
|
here is exposed.
|