STATE-WP-0094: dependency-edge receiving rollout package and owner note
Prepared, not executed: image, migration b3c4d5e6f7a8, Helm --reuse-values --atomic, pre/post checks, normal Forge receive proof plus repeat noop, and rollback without schema downgrade. Requires founder per-change authorization. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 692292@bnt-lap001 Assistant-Session: 89c7d8e8-e1b3-47eb-9411-a0ab227fca2f
This commit is contained in:
parent
c287ed8259
commit
fad9b3f7dc
2 changed files with 182 additions and 0 deletions
169
docs/rollout-dependency-edge-receiving.md
Normal file
169
docs/rollout-dependency-edge-receiving.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# Rollout package: dependency-edge receiving (STATE-WP-0094-T05)
|
||||
|
||||
> **REQUIRES FOUNDER PER-CHANGE AUTHORIZATION.** This package is prepared, not
|
||||
> executed. Nothing below has been run against railiance01. No image was
|
||||
> promoted, no Helm command, migration, schedule or production write was
|
||||
> performed while preparing it. The 2026-10-04 "Do the rollout" instruction
|
||||
> covered the qualifier receiving change (revision 71) only and does not
|
||||
> authorize this change.
|
||||
|
||||
Prepared 2026-10-04 from `main` at `c287ed8`. Shape mirrors the previous
|
||||
authorized rollout (`docs/evidence/2026-10-04-receiving-rollout.json`,
|
||||
`the-custodian/docs/changes/state-hub-qualifier-rollout-2026-10-04.json`).
|
||||
|
||||
## What ships
|
||||
|
||||
| Commit | Change | Runtime effect |
|
||||
| --- | --- | --- |
|
||||
| `fceb5f2` | Forge receiving derives `depends_on` edges and reconciles source-owned `workplan_dependencies` rows | New `created_edges`/`removed_edges`/`unresolved_edges`/`external_edges` in receipt outcomes; receiving writes dependency rows |
|
||||
| `3b9a5c5` | `GET /messages/` accepts `offset` (stable `created_at,id` order); C-25 pages replies | Additive query parameter |
|
||||
| `c287ed8` | Dry-run edge preview in `diff_against_hub`; classifier no longer collapses duplicate edge keys | Pure preview; reconcile uses the shared planner |
|
||||
| `57f2751` | Historical projection / owner-return planner source (already in `5dd3df9`) | Ships only if the live image predates `5dd3df9` |
|
||||
|
||||
Migration: `b3c4d5e6f7a8` (revises `a0b1c2d3e4f5`, the schema recorded as live
|
||||
in revision 71). Additive only: two nullable columns
|
||||
`workplan_dependencies.source_owner` (varchar 160) and `source_commit`
|
||||
(varchar 64), plus index `ix_workplan_dependencies_source_owner`. Existing rows
|
||||
stay NULL, i.e. externally authored, so receiving never removes them.
|
||||
|
||||
Local verification (2026-10-04, scratch database on the local PostgreSQL):
|
||||
`a0b1c2d3e4f5 -> b3c4d5e6f7a8 -> a0b1c2d3e4f5 -> b3c4d5e6f7a8` passed with a
|
||||
pre-existing dependency row preserved through every step (owner stayed NULL).
|
||||
`alembic check` reports long-standing model/migration drift on unrelated tables
|
||||
only; nothing on `workplan_dependencies`. Full test suite: 1018 passed.
|
||||
|
||||
## 0. Preconditions (read-only)
|
||||
|
||||
1. Founder authorization for this exact change and target commit, recorded as a
|
||||
change proposal in `the-custodian/docs/changes/` (copy the shape of
|
||||
`state-hub-qualifier-rollout-2026-10-04.json`; `outcome: pending`).
|
||||
2. Identify the live revision and image. The historical-receiving proposal
|
||||
(`main-5dd3df9`) has no recorded outcome, so do not assume revision 71:
|
||||
```bash
|
||||
helm -n state-hub history state-hub --max 5
|
||||
kubectl -n state-hub get deploy -o jsonpath='{range .items[*]}{.metadata.name}={.spec.template.spec.containers[0].image}{"\n"}{end}'
|
||||
kubectl -n state-hub exec deploy/state-hub -- /app/.venv/bin/python -m alembic current
|
||||
```
|
||||
Record `PREV_REV`, `PREV_IMAGE` and the live schema. Expected schema:
|
||||
`a0b1c2d3e4f5`. Stop if it is anything else.
|
||||
3. Fresh CPU headroom for the surge (API 250m, MCP 10m, migrate job 50m) as in
|
||||
the previous rollout; stop on unrelated pending pods.
|
||||
4. The proof repository is `state-hub` itself. Read its current hub edges so
|
||||
the post-check can tell receiving's rows from earlier ones:
|
||||
```bash
|
||||
curl -s "http://10.43.68.154:8000/workplans/?repo=state-hub" > /tmp/edge-pre-workplans.json
|
||||
```
|
||||
and, inside the cluster DB (read-only):
|
||||
`select count(*), source_owner from workplan_dependencies group by 2;`
|
||||
(column does not exist yet; before migration expect an error, after it all
|
||||
NULL).
|
||||
|
||||
## 1. Image
|
||||
|
||||
1. Push `main` (operator; agents in this session did not push). The Forgejo
|
||||
`image.yaml` workflow builds on pushes touching `api/**`, `migrations/**`,
|
||||
`scripts/**` etc. and tags `main-<short sha of the pushed head>`.
|
||||
2. Confirm the build-and-push run succeeded and that its source commit contains
|
||||
`c287ed8`:
|
||||
`git merge-base --is-ancestor c287ed8 <image source sha>`.
|
||||
3. Record `TAG=main-<sha7>` and the image digest. Do not use a mutable tag.
|
||||
4. Verify the receiving source digest in the image equals the tested source:
|
||||
`sha256sum api/services/forge_projection.py` at the image source commit vs.
|
||||
`kubectl exec ... sha256sum /app/api/services/forge_projection.py` after
|
||||
rollout.
|
||||
|
||||
## 2. Server-side dry run
|
||||
|
||||
```bash
|
||||
helm pull / copy chart: deploy/railiance/apps/charts/state-hub -> /tmp/cust-edge-rollout/chart
|
||||
helm -n state-hub upgrade state-hub /tmp/cust-edge-rollout/chart \
|
||||
--reuse-values --set image.tag=$TAG --dry-run=server
|
||||
```
|
||||
|
||||
Expected diff vs. live: image tag, version labels and the consequent config
|
||||
checksum only. Any other change stops the rollout.
|
||||
|
||||
## 3. Upgrade (the authorized action)
|
||||
|
||||
```bash
|
||||
helm -n state-hub upgrade state-hub /tmp/cust-edge-rollout/chart \
|
||||
--reuse-values --set image.tag=$TAG --atomic --timeout 3m
|
||||
```
|
||||
|
||||
The chart's `pre-upgrade` hook job (`migrate-job.yaml`) runs
|
||||
`alembic upgrade head`, applying `b3c4d5e6f7a8`. `--atomic` restores the prior
|
||||
release if the hook or rollout fails.
|
||||
|
||||
## 4. Post-checks
|
||||
|
||||
- `helm -n state-hub status state-hub` = deployed, new revision `NEW_REV`.
|
||||
- API and MCP `1/1` Ready; container image IDs match the recorded digest.
|
||||
- `alembic current` = `b3c4d5e6f7a8`.
|
||||
- `\d workplan_dependencies` shows `source_owner`, `source_commit`; index
|
||||
present; every pre-existing row has `source_owner IS NULL`.
|
||||
- `GET /state/health` OK; `GET /messages/?to_agent=state-hub&limit=1&offset=0`
|
||||
answers 200.
|
||||
- Receiving source digest in the container matches the tested file.
|
||||
|
||||
## 5. Post-deploy proof (normal immutable Forge receive)
|
||||
|
||||
Use the normal receiving endpoint only. No task PATCH, no direct consistency
|
||||
repair, no C-20 `--fix` between the two receipts.
|
||||
|
||||
1. `SHA=$(git ls-remote <forge state-hub> refs/heads/main | cut -f1)` - the
|
||||
exact forge head at receipt time (it must contain `c287ed8`).
|
||||
2. Receipt:
|
||||
```bash
|
||||
curl -s -X POST http://10.43.68.154:8000/repos/state-hub/work-record-projection/reconcile \
|
||||
-H 'Content-Type: application/json' -d "{\"expected_commit\":\"$SHA\"}"
|
||||
```
|
||||
Expected at the time of writing (from the live C-20 report):
|
||||
- `created_edges` includes `STATE-WP-0095-T04 starts_after STATE-WP-0095-T03`
|
||||
and, if `RMGR-WP-0019-T03` resolves uniquely fleet-wide,
|
||||
`STATE-WP-0095-T04 starts_after RMGR-WP-0019-T03`, each with
|
||||
`source_owner = forge:state-hub`, `source_commit = $SHA`;
|
||||
- `unresolved_edges` lists `STATE-WP-0078 -> ACTIVITY-WP-0026` (`missing`)
|
||||
unless that workplan has been received by then;
|
||||
- `external_edges` lists declarations already present as C-20/API rows;
|
||||
- `removed_edges` is empty (no owned rows exist before the first receipt);
|
||||
- `status: applied`.
|
||||
3. Repeat the identical request (fresh idempotency, same `$SHA`): expected
|
||||
`status: noop`, zero created/removed edges; `unresolved_edges` is reported
|
||||
again by design.
|
||||
4. DB proof: `select source_owner, count(*) from workplan_dependencies group by 1`
|
||||
- new rows only under `forge:state-hub`; NULL-owner count unchanged.
|
||||
5. Record evidence as `docs/evidence/<date>-dependency-edge-receiving-rollout.json`
|
||||
in the shape of `2026-10-04-receiving-rollout.json` (image, digest,
|
||||
`helm_revision`, schema, both receipt outcomes, DB counts).
|
||||
|
||||
A failed receipt (e.g. a historical identifier collision as seen for
|
||||
`the-custodian`) rolls back transactionally; record it as a failure, not as
|
||||
edge-receiving proof.
|
||||
|
||||
## 6. Rollback
|
||||
|
||||
- Upgrade failure: `--atomic` restores `PREV_REV` automatically.
|
||||
- Post-upgrade verification failure:
|
||||
`helm -n state-hub rollback state-hub $PREV_REV --wait --timeout 3m`.
|
||||
- **Schema: do not downgrade.** `b3c4d5e6f7a8` is additive and nullable; the
|
||||
previous image ignores the columns (its ORM model does not map them, and
|
||||
inserts leave them NULL). Downgrade would drop `source_owner` and so erase
|
||||
the ownership marker of every receiving-created row, turning those rows into
|
||||
"external" edges that later receipts could never remove. Retain schema and
|
||||
data on rollback.
|
||||
- Receiving-created rows under the previous image: they remain as ordinary
|
||||
dependency edges. If they must go, delete only rows with
|
||||
`source_owner = 'forge:<repo>'` - a separate authorized data change, not
|
||||
part of rollback.
|
||||
|
||||
## Open risks
|
||||
|
||||
- Live revision/image is unconfirmed (historical-receiving outcome not
|
||||
recorded); step 0.2 is mandatory.
|
||||
- Pre-existing C-20/API edges are never adopted; a removed declaration does not
|
||||
remove them.
|
||||
- A checkout without `workplans/` is refused before edges are touched while
|
||||
the hub still holds that repository's workplans, so owned edges are not
|
||||
removed on an empty source; the dry-run preview does not model that refusal
|
||||
and would list those removals (preview-only discrepancy).
|
||||
- No scheduled freshness is part of this change; receiving remains manual.
|
||||
|
|
@ -219,3 +219,16 @@ nonterminal or future gates stop preparation even with prior successful pickup.
|
|||
Missing safety metadata refuses planning. Thirty staged tests pass; the initial
|
||||
13-test result remains historical preparation evidence, not complete safety
|
||||
coverage. T03/T05 stay progress; no dispatch, schedule or source gate clearing.
|
||||
|
||||
|
||||
2026-10-04 T05 follow-up (claude-state-hub): consistency report-only run at
|
||||
HEAD and with the b2af3c1 checker produced identical findings (27 C-03 FAIL on
|
||||
STATE-WP-0069/0070/0071/0073/0080 stale hub IDs, 11 WARN C-20/C-26/C-28/C-35);
|
||||
none caused by fceb5f2/3b9a5c5. Migration b3c4d5e6f7a8 ran
|
||||
a0b1c2d3e4f5 -> head -> a0b1c2d3e4f5 -> head on a scratch local PostgreSQL
|
||||
database with a pre-existing edge row preserved (owner NULL). c287ed8 adds the
|
||||
dry-run edge preview (create/remove/retain/unresolved/external) through the
|
||||
shared pure planner and fixes duplicate-edge-key collapse in the classifier.
|
||||
Rollout package prepared, not executed, and requires founder per-change
|
||||
authorization: docs/rollout-dependency-edge-receiving.md. No deployment,
|
||||
schedule or production write. T03/T05 status unchanged.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue