feat(forge): report unreadable repositories as unreadable (STATE-WP-0084-T01)
A private repository failed derivation the same way a broken one did, so "cannot read" and "does not exist" were indistinguishable from outside. They authorise opposite things: only the second can justify retiring a record. - ForgeUnreadableError (a ForgeDeriveError, so old callers still catch it) for permission-shaped clone failures, including Forgejo's 404 for an unauthenticated private repo — indistinguishable here, and the safe reading of an ambiguous answer cannot destroy a record. - GIT_TERMINAL_PROMPT=0: an unattended pass must fail, not block on a username prompt. Failing is what makes the case observable. - DerivedProjection.retirement_eligible separates "no records found" from "no records exist". A checkout with no workplans/ directory cannot evidence an absence — the empty-clone path that would have proposed every record in a repository for retirement. - Retirement from an ineligible source is refused even when acknowledged. - Fleet keeps unreadable out of the error bucket. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 3377672@bnt-lap001 Assistant-Session: 15463ccf-238f-4e13-b163-93aa25c6d166
This commit is contained in:
parent
8b38f815e5
commit
85181cd3e4
3 changed files with 294 additions and 6 deletions
|
|
@ -54,7 +54,7 @@ wait on `MASON-WP-0003-T02`.
|
|||
|
||||
```task
|
||||
id: STATE-WP-0084-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e955067d-2152-59f7-b672-12755ceb26b0"
|
||||
```
|
||||
|
|
@ -81,6 +81,33 @@ Acceptance: an unreadable repository is reported as unreadable; a clone that
|
|||
succeeds but returns nothing produces no retirement proposals; both cases are
|
||||
covered by tests that fail if the retirement path is reachable from either.
|
||||
|
||||
**Done 2026-08-26.** `api/services/forge_projection.py`:
|
||||
|
||||
- `ForgeUnreadableError` subclasses `ForgeDeriveError`, so existing callers
|
||||
keep catching it while new ones can tell the cases apart. A permission-shaped
|
||||
git failure is classified into it; a genuine fault stays a plain error.
|
||||
Forgejo's 404 for an unauthenticated private repository classifies as
|
||||
unreadable, because the two are indistinguishable at this layer and the safe
|
||||
reading of an ambiguous answer is the one that cannot destroy a record.
|
||||
- `_run_git` runs with `GIT_TERMINAL_PROMPT=0`. Without it an unattended pass
|
||||
blocks on a username prompt instead of failing, and an unreadable repository
|
||||
is only observable if it fails.
|
||||
- `DerivedProjection.records_source_present` / `.retirement_eligible` separate
|
||||
"no records found" from "no records exist". A checkout with no `workplans/`
|
||||
directory can no longer evidence an absence.
|
||||
- `diff_against_hub` withholds `stale` rather than computing and filtering it,
|
||||
and says so in `stale_withheld`. `would_remove` is 0 for such a source.
|
||||
- `reset_repository_projection` returns `status="unreadable"` for a repository
|
||||
it may not read, and refuses retirement from an ineligible source **even when
|
||||
`acknowledge_retirements=True`** — consenting to a conclusion is not the same
|
||||
as the evidence for it existing.
|
||||
- `reset_fleet_projection` keeps unreadable repositories out of `errors`, in
|
||||
their own bucket with its own count. Nine repositories in an error bucket
|
||||
read as nine broken repositories; they were nine we were not allowed to read.
|
||||
|
||||
13 tests in `TestUnreadableIsNotMissing` (`tests/test_forge_projection.py`);
|
||||
58 pass across the forge/projection/backfill suites.
|
||||
|
||||
## Deliver the credential to the pod
|
||||
|
||||
```task
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue