feat(workplan): open STATE-WP-0083 for the forge-derived projection reset
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Implements ADR-012 decision 7. Placed in state-hub because central must do its
own reading: the pod can clone Forgejo anonymously, and driving this from Repo
Manager would push a workstation-computed projection into central, which
ADR-010 decision 5 forbids.

Also the only sanctioned removal path — the hub has no hard-delete for work
records by design.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
tegwick 2026-08-25 23:29:24 +02:00
parent b02f3c22ad
commit 6390b7bead

View file

@ -0,0 +1,146 @@
---
id: STATE-WP-0083
type: workplan
title: "Forge-derived projection reset, per repository"
domain: infotech
repo: state-hub
status: active
owner: codex
topic_slug: infotech
created: "2026-08-25"
updated: "2026-08-25"
related:
- CUST-ADR-012
- CUST-WP-0068
---
# Forge-derived projection reset, per repository
## Goal
Implement `ADR-012` decision 7: discard a repository's projection and rebuild it
from what Forgejo holds. Per repository, routine, idempotent, and verifiable.
This is the only sanctioned way to remove a hub record. The hub has no
hard-delete for work records — `DELETE /tasks/{id}` is `cancel_task`,
`DELETE /workstreams/{id}` is `410 Gone`, and no `session.delete` exists for
either. Re-derivation is not one option among several; it is the mechanism.
## Why here and not in Repo Manager
`ADR-012` decision 1 says central derives from the forge, and the pod can reach
it: `git ls-remote https://forgejo.coulomb.social/...` succeeds anonymously from
inside the cluster, and `git` is already in the image.
Driving the reset from Repo Manager would mean computing a projection on a
workstation and pushing it into central — precisely the injection of derived
state `ADR-010` decision 5 forbids. Central must do its own reading. The cost is
that workplan parsing exists in two places; `scripts/consistency_check.py`
already parses frontmatter and task blocks here, so the capability is present
and should be extracted rather than rewritten.
## What it must not do
- **Not restore preliminary overlay records.** They exist because the forge does
not hold them; a rebuild from the forge cannot reproduce them and must not
pretend to.
- **Not destroy hub-native records.** Progress events, decisions and inbox
messages originate in the hub (`ADR-010` decision 4). They are not
forge-derived and must survive a rebuild of forge-derived state.
- **Not proceed silently when records would be lost.** Stop, name them, and
require an explicit acknowledgement.
## Derive a repository's projection from the forge
```task
id: STATE-WP-0083-T01
status: todo
priority: high
```
Given a repository slug, clone or fetch its default branch from Forgejo into a
temporary location, parse the workplan files, and compute the projection that
state would produce: workplans, tasks, their identifiers, statuses and backing
paths.
Read-only and side-effect free. Record the commit the derivation came from —
`ADR-012` decision 2 requires provenance, and the existing `git_fingerprint` is
worthless precisely because nothing ever wrote it correctly.
Acceptance: deriving `the-custodian` twice from the same commit yields identical
output, and the commit is reported.
## Report the difference before changing anything
```task
id: STATE-WP-0083-T02
status: todo
priority: high
```
Compare the derived projection against what the hub currently holds for that
repository, and report: records the forge has that the hub lacks, records the
hub has that the forge does not, and records whose fields differ.
This is the half that is immediately useful without any destructive capability,
and it is what makes decision 7's "verifiable" real. It also answers the
question `CUST-WP-0068-T09` is waiting on — which stale rows would actually
clear — before anyone commits to clearing them.
Acceptance: a dry-run diff for a repository with known drift matches what
manual inspection shows.
## Apply the reset transactionally
```task
id: STATE-WP-0083-T03
status: todo
priority: high
```
Replace the repository's forge-derived records with the derived projection in
one transaction: create what is missing, update what differs, remove what no
longer derives. Hub-native records are untouched.
Refuse by default when removal would destroy a record with no counterpart in the
forge; report what would be lost and require an explicit acknowledgement to
proceed. That refusal is evaluated per repository, so one unresolved repository
never blocks the rest.
Acceptance: reset twice produces the same projection; a repository holding
records the forge lacks is refused with those records named; hub-native record
counts are unchanged across a reset.
## Fleet form as a loop over the repository form
```task
id: STATE-WP-0083-T04
status: todo
priority: medium
```
The fleet-wide reset iterates the per-repository reset and shares its
implementation, so the rarely-used dangerous path is exercised by the frequently
used safe one. Repositories that refuse are skipped and reported, never aborting
the pass.
Acceptance: a fleet dry-run reports per-repository outcomes including refusals,
and completes despite them.
## Retire what the reset replaces
```task
id: STATE-WP-0083-T05
status: wait
priority: medium
```
Once reset is trusted, `CUST-WP-0068-T09` can clear its stale rows — 4 workplan
rows from the prefix migration and 305 task rows across 44 workplans.
`ADR-003` decision 2's `mtime`-based fingerprints should also be replaced by the
source commit at this point; `ADR-012` invalidated that composition and the
replacement belongs with the provenance work in `T01`.
Acceptance: `CUST-WP-0068-T09` closes; no fingerprint input depends on a local
filesystem.