finish three custodian workplans from live hub evidence
Close CUST-WP-0064 after the 2026-08-24 unassisted fire ingested clay-borg, close CUST-WP-0065 now that all 120 active repos project a classification, and close ADHOC-2026-08-25. Mark CUST-WP-0067 T02/T10 done (reverse relays already gone; work-record recovery lives on 0068). Park the later no-checkout SBOM regression as CUST-IN-0015. Teach the classification gate to use this host's checkout path.
This commit is contained in:
parent
349238cf8f
commit
93b8174abd
8 changed files with 143 additions and 21 deletions
|
|
@ -60,3 +60,7 @@ Status 2026-08-24:
|
||||||
|
|
||||||
On railiance01 both services are reachable in-cluster with no tunnel at all —
|
On railiance01 both services are reachable in-cluster with no tunnel at all —
|
||||||
this is where "abandon tunneling" genuinely applies.
|
this is where "abandon tunneling" genuinely applies.
|
||||||
|
|
||||||
|
**Applied (2026-08-28):** both reverse relays are absent from the live
|
||||||
|
`tunnels.yaml` and from `bridge status`. `CUST-WP-0067-T02` is done. The
|
||||||
|
cache Postgres container remains until `CUST-WP-0068-T08`.
|
||||||
|
|
|
||||||
17
intakes.md
17
intakes.md
|
|
@ -69,3 +69,20 @@ origin_ref: CUST-WP-0062
|
||||||
notes: "Live review after cutover found the Ready SBOM Nexus pod at restartCount 9 in under five hours. The last container ran exactly 30 minutes, then readiness/liveness returned HTTP 500 because PostgreSQL rejected the expired v-token-sbom-nex-* credential; Kubernetes restarted the process and it recovered. The corrected runtime deployed on 2026-08-23 rereads the mounted URL for every new connection, recycles the pool every five minutes, keeps credentials out of the engine URL, and separates process liveness from database readiness. Completion evidence at 2026-08-22T23:06:19Z exceeded the old failure point with 30m51s on one pod UID across repeated mounted Secret refreshes: Ready, restart count zero, process/database/repository checks passing, zero health 500s, and zero credential-pattern log matches. Absorbed by finished SBOM-WP-0004 and RAPP-SBOM-NEXUS-WP-0003."
|
notes: "Live review after cutover found the Ready SBOM Nexus pod at restartCount 9 in under five hours. The last container ran exactly 30 minutes, then readiness/liveness returned HTTP 500 because PostgreSQL rejected the expired v-token-sbom-nex-* credential; Kubernetes restarted the process and it recovered. The corrected runtime deployed on 2026-08-23 rereads the mounted URL for every new connection, recycles the pool every five minutes, keeps credentials out of the engine URL, and separates process liveness from database readiness. Completion evidence at 2026-08-22T23:06:19Z exceeded the old failure point with 30m51s on one pod UID across repeated mounted Secret refreshes: Ready, restart count zero, process/database/repository checks passing, zero health 500s, and zero credential-pattern log matches. Absorbed by finished SBOM-WP-0004 and RAPP-SBOM-NEXUS-WP-0003."
|
||||||
state_hub_intake_id: "01a02e28-3beb-764a-b4fc-c34cdf59a01e"
|
state_hub_intake_id: "01a02e28-3beb-764a-b4fc-c34cdf59a01e"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## CUST-IN-0015 — Restore source-ref projection on later SBOM catch-up batches
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: CUST-IN-0015
|
||||||
|
kind: intake
|
||||||
|
title: "Restore source-ref projection on later SBOM catch-up batches"
|
||||||
|
status: open
|
||||||
|
lane: blue
|
||||||
|
priority: high
|
||||||
|
owner: repo-manager
|
||||||
|
tags: [sbom, catch-up]
|
||||||
|
origin: residual
|
||||||
|
origin_ref: CUST-WP-0064
|
||||||
|
updated: "2026-08-28"
|
||||||
|
notes: "CUST-WP-0064-T04 is met: the 2026-08-24 07:15 UTC unassisted fire ingested clay-borg (snapshot 63abb22f, forgejo-archive-v1, revision 18c57f2e, 77 entries) and wrote truthful no-manifest terminals for citation-work and config-atlas at pinned SHAs. From 2026-08-25 through 2026-08-28 the same weekday schedule writes three no-checkout snapshots each day (feature-control / evidence-source / evidence-binder on 2026-08-28). never_count is 76. Diagnose why Repo Manager source-ref projection followed the 2026-08-24 batch and not later ones; do not widen catch_up_limit while diagnosing. SBOM Nexus and Activity Core are counterparties, not a second owner."
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -81,3 +81,26 @@ def test_clean_active_fleet_converges_and_renders(tmp_path: Path) -> None:
|
||||||
assert report["source_warning_count"] == 0
|
assert report["source_warning_count"] == 0
|
||||||
assert "active classified: 1" in render_text(report)
|
assert "active classified: 1" in render_text(report)
|
||||||
assert "converged: yes" in render_text(report)
|
assert "converged: yes" in render_text(report)
|
||||||
|
|
||||||
|
|
||||||
|
def test_host_paths_for_this_machine_beat_other_host_local_path(tmp_path: Path) -> None:
|
||||||
|
local = tmp_path / "repo-manager"
|
||||||
|
_write_valid(local)
|
||||||
|
report = analyze_repositories(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"slug": "repo-manager",
|
||||||
|
"status": "active",
|
||||||
|
"category": "tooling",
|
||||||
|
"local_path": "/home/tegwick/repo-manager",
|
||||||
|
"host_paths": {
|
||||||
|
"bnt-lap001": str(local),
|
||||||
|
"239.62.205.92.host.secureserver.net": "/home/tegwick/repo-manager",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
hostname="bnt-lap001",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert report["projected_without_source"] == []
|
||||||
|
assert report["converged"] is True
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from __future__ import annotations
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
@ -50,17 +51,48 @@ def fetch_repositories(
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
def _source_path(record: dict[str, Any]) -> Path | None:
|
def _checkout_candidates(record: dict[str, Any], *, hostname: str) -> list[Path]:
|
||||||
local_path = record.get("local_path")
|
"""Prefer this host's recorded checkout over a `local_path` from another machine."""
|
||||||
if not isinstance(local_path, str) or not local_path.strip():
|
seen: set[str] = set()
|
||||||
return None
|
candidates: list[Path] = []
|
||||||
return Path(local_path) / ".repo-classification.yaml"
|
|
||||||
|
def add(raw: object) -> None:
|
||||||
|
if not isinstance(raw, str):
|
||||||
|
return
|
||||||
|
text = raw.strip()
|
||||||
|
if not text or text in seen:
|
||||||
|
return
|
||||||
|
seen.add(text)
|
||||||
|
candidates.append(Path(text))
|
||||||
|
|
||||||
|
host_paths = record.get("host_paths")
|
||||||
|
if isinstance(host_paths, dict):
|
||||||
|
short = hostname.split(".")[0]
|
||||||
|
add(host_paths.get(hostname))
|
||||||
|
add(host_paths.get(short))
|
||||||
|
for value in host_paths.values():
|
||||||
|
if isinstance(value, str) and Path(value).is_dir():
|
||||||
|
add(value)
|
||||||
|
add(record.get("local_path"))
|
||||||
|
return candidates
|
||||||
|
|
||||||
|
|
||||||
|
def _source_path(record: dict[str, Any], *, hostname: str) -> Path | None:
|
||||||
|
preferred: Path | None = None
|
||||||
|
for checkout in _checkout_candidates(record, hostname=hostname):
|
||||||
|
path = checkout / ".repo-classification.yaml"
|
||||||
|
if preferred is None:
|
||||||
|
preferred = path
|
||||||
|
if path.is_file():
|
||||||
|
return path
|
||||||
|
return preferred
|
||||||
|
|
||||||
|
|
||||||
def analyze_repositories(
|
def analyze_repositories(
|
||||||
records: list[dict[str, Any]],
|
records: list[dict[str, Any]],
|
||||||
*,
|
*,
|
||||||
validate_sources: bool = True,
|
validate_sources: bool = True,
|
||||||
|
hostname: str | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
active = [record for record in records if record.get("status") == "active"]
|
active = [record for record in records if record.get("status") == "active"]
|
||||||
classified = [record for record in active if record.get("category") is not None]
|
classified = [record for record in active if record.get("category") is not None]
|
||||||
|
|
@ -74,10 +106,11 @@ def analyze_repositories(
|
||||||
|
|
||||||
slug_counts: dict[str, int] = {}
|
slug_counts: dict[str, int] = {}
|
||||||
allowed = load_allowed() if validate_sources else None
|
allowed = load_allowed() if validate_sources else None
|
||||||
|
current_host = hostname or socket.gethostname()
|
||||||
for record in active:
|
for record in active:
|
||||||
slug = str(record.get("slug") or "<missing-slug>")
|
slug = str(record.get("slug") or "<missing-slug>")
|
||||||
slug_counts[slug] = slug_counts.get(slug, 0) + 1
|
slug_counts[slug] = slug_counts.get(slug, 0) + 1
|
||||||
path = _source_path(record)
|
path = _source_path(record, hostname=current_host)
|
||||||
source_present = path is not None and path.is_file()
|
source_present = path is not None and path.is_file()
|
||||||
|
|
||||||
if record.get("category") is None:
|
if record.get("category") is None:
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ type: workplan
|
||||||
title: "Ad hoc tasks 2026-08-25"
|
title: "Ad hoc tasks 2026-08-25"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: the-custodian
|
repo: the-custodian
|
||||||
status: active
|
status: finished
|
||||||
owner: codex
|
owner: codex
|
||||||
created: "2026-08-25"
|
created: "2026-08-25"
|
||||||
updated: "2026-08-25"
|
updated: "2026-08-28"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Ad hoc tasks 2026-08-25
|
# Ad hoc tasks 2026-08-25
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ type: workplan
|
||||||
title: "Controlled scan inputs for authoritative daily SBOM catch-up"
|
title: "Controlled scan inputs for authoritative daily SBOM catch-up"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: the-custodian
|
repo: the-custodian
|
||||||
status: active
|
status: finished
|
||||||
owner: codex
|
owner: codex
|
||||||
topic_slug: custodian
|
topic_slug: custodian
|
||||||
planning_priority: high
|
planning_priority: high
|
||||||
planning_order: 64
|
planning_order: 64
|
||||||
created: "2026-08-22"
|
created: "2026-08-22"
|
||||||
updated: "2026-08-23"
|
updated: "2026-08-28"
|
||||||
quality_dor: DoR-Ok
|
quality_dor: DoR-Ok
|
||||||
quality_dor_at: "2026-08-22"
|
quality_dor_at: "2026-08-22"
|
||||||
quality_dor_by: codex
|
quality_dor_by: codex
|
||||||
|
|
@ -158,7 +158,7 @@ than the attended target set; T04 retains only the first unassisted fire.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: CUST-WP-0064-T04
|
id: CUST-WP-0064-T04
|
||||||
status: progress
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "664d90b0-1169-58fa-9a77-df53e739f957"
|
state_hub_task_id: "664d90b0-1169-58fa-9a77-df53e739f957"
|
||||||
```
|
```
|
||||||
|
|
@ -180,11 +180,29 @@ An operator-trigger through the existing Temporal schedule subsequently
|
||||||
proved the production scheduled path and fleet summary without changing its
|
proved the production scheduled path and fleet summary without changing its
|
||||||
weekday cadence. The first unassisted 09:15 Europe/Berlin fire remains.
|
weekday cadence. The first unassisted 09:15 Europe/Berlin fire remains.
|
||||||
|
|
||||||
|
**Done (2026-08-28):** the first unassisted weekday fire ran at 2026-08-24
|
||||||
|
07:15 UTC (09:15 Europe/Berlin) against the next oldest three. `clay-borg`
|
||||||
|
ingested from `forgejo-archive-v1`:
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| snapshot | `63abb22f-0701-4d56-ba16-75bdff9362c4` |
|
||||||
|
| repo | `clay-borg` |
|
||||||
|
| revision | `18c57f2e9dce0589c1f0e24dd4d939aea50cee86` |
|
||||||
|
| entries | 77 (`Cargo.lock`) |
|
||||||
|
| provenance | archive sha256 `df9e5fac…`, 1_033_378 bytes |
|
||||||
|
| siblings | `citation-work` and `config-atlas` terminal `no-manifest` at pinned SHAs |
|
||||||
|
|
||||||
|
`never_count` is 76 (was 94 on 2026-08-23). Daily 07:15 UTC fires continued
|
||||||
|
24–28 August. Residual: from 2026-08-25 the same schedule writes `no-checkout`
|
||||||
|
again, so source-ref projection is not following later batches —
|
||||||
|
`CUST-IN-0015`.
|
||||||
|
|
||||||
## Acceptance
|
## Acceptance
|
||||||
|
|
||||||
- [x] Production scans consume a controlled, revision-pinned source input
|
- [x] Production scans consume a controlled, revision-pinned source input
|
||||||
- [x] No workstation filesystem is mounted or implicitly trusted
|
- [x] No workstation filesystem is mounted or implicitly trusted
|
||||||
- [x] Nexus remains the only authoritative snapshot writer
|
- [x] Nexus remains the only authoritative snapshot writer
|
||||||
- [x] One fire remains bounded to its original N targets across retries
|
- [x] One fire remains bounded to its original N targets across retries
|
||||||
- [ ] At least one normal scheduled fire produces real ingested snapshots
|
- [x] At least one normal scheduled fire produces real ingested snapshots
|
||||||
- [x] Source cleanup, provenance, failure evidence, and rollback are verified
|
- [x] Source cleanup, provenance, failure evidence, and rollback are verified
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ type: workplan
|
||||||
title: "Reclassify repos and guidance docs to the new sector domain scheme"
|
title: "Reclassify repos and guidance docs to the new sector domain scheme"
|
||||||
domain: infotech
|
domain: infotech
|
||||||
repo: the-custodian
|
repo: the-custodian
|
||||||
status: active
|
status: finished
|
||||||
owner: codex
|
owner: codex
|
||||||
created: "2026-08-23"
|
created: "2026-08-23"
|
||||||
updated: "2026-08-23"
|
updated: "2026-08-28"
|
||||||
quality_dor: DoR-Ok
|
quality_dor: DoR-Ok
|
||||||
quality_dor_at: "2026-08-23"
|
quality_dor_at: "2026-08-23"
|
||||||
quality_dor_by: codex
|
quality_dor_by: codex
|
||||||
|
|
@ -74,7 +74,7 @@ project names remain valid outside that field.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: CUST-WP-0065-T03
|
id: CUST-WP-0065-T03
|
||||||
status: progress
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "a1ad7cd3-5bfb-58ee-8f97-6c2e864b2fd9"
|
state_hub_task_id: "a1ad7cd3-5bfb-58ee-8f97-6c2e864b2fd9"
|
||||||
```
|
```
|
||||||
|
|
@ -118,11 +118,18 @@ classification at revision `81f9279` (`tooling`, primary `agents`, secondary
|
||||||
digest matched the checkout, and the standard API registration completed with
|
digest matched the checkout, and the standard API registration completed with
|
||||||
zero invalid results. Thirteen owner classifications remain.
|
zero invalid results. Thirteen owner classifications remain.
|
||||||
|
|
||||||
|
**Closed (2026-08-28):** owner classifications landed. The remaining four
|
||||||
|
active null-category rows were present, valid source files that had never
|
||||||
|
been projected: `core-hub`, `fin-hub`, `rail-kubernetes`, and `repo-manager`
|
||||||
|
(the last looked missing only because hub `local_path` pointed at the node
|
||||||
|
checkout). `rmgr repo-onboard --api-base http://127.0.0.1:8000` projected all
|
||||||
|
four. Active fleet is 120/120 classified.
|
||||||
|
|
||||||
## Verify fleet convergence and close
|
## Verify fleet convergence and close
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: CUST-WP-0065-T04
|
id: CUST-WP-0065-T04
|
||||||
status: progress
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "0c7c6f53-8a89-5102-87ca-e1991c227038"
|
state_hub_task_id: "0c7c6f53-8a89-5102-87ca-e1991c227038"
|
||||||
```
|
```
|
||||||
|
|
@ -159,6 +166,15 @@ its historical classification. The active fleet is now 103/116 classified;
|
||||||
the same 13 owner-source gaps remain, with no projection or source-validity
|
the same 13 owner-source gaps remain, with no projection or source-validity
|
||||||
failure.
|
failure.
|
||||||
|
|
||||||
|
**Closed (2026-08-28):** after the four remaining projections, the live gate
|
||||||
|
is 125 registered, 120 active, 120 classified, zero null-category, zero
|
||||||
|
missing source, zero present-but-unprojected, zero invalid source. A false
|
||||||
|
`projected_without_source` on `repo-manager` (hub `local_path` is the node
|
||||||
|
checkout; this workstation's path is in `host_paths.bnt-lap001`) is fixed in
|
||||||
|
`tools/repo_classification_convergence.py`. `make classification-check`
|
||||||
|
passes. Project names were not rewritten. Non-blocking source-validation
|
||||||
|
warnings remain (75) and are not a close blocker.
|
||||||
|
|
||||||
## Indexing note
|
## Indexing note
|
||||||
|
|
||||||
**Resolved (2026-08-23):** Repo Manager revisions `7a15f1d` and `7b9fdaa`
|
**Resolved (2026-08-23):** Repo Manager revisions `7a15f1d` and `7b9fdaa`
|
||||||
|
|
@ -175,6 +191,6 @@ none was rewritten or treated as the new records.
|
||||||
- [x] Live missing-classification baseline is reproducible and split by cause
|
- [x] Live missing-classification baseline is reproducible and split by cause
|
||||||
- [x] Legacy project identity is distinguished from sector-domain metadata
|
- [x] Legacy project identity is distinguished from sector-domain metadata
|
||||||
- [x] Canonical and agent-facing migration guidance is current
|
- [x] Canonical and agent-facing migration guidance is current
|
||||||
- [ ] Missing source classifications are owner-reviewed and validated
|
- [x] Missing source classifications are owner-reviewed and validated
|
||||||
- [x] Present source classifications project into State Hub
|
- [x] Present source classifications project into State Hub
|
||||||
- [ ] Active fleet has zero unexplained null category projections
|
- [x] Active fleet has zero unexplained null category projections
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ repo: the-custodian
|
||||||
status: active
|
status: active
|
||||||
owner: codex
|
owner: codex
|
||||||
created: "2026-08-24"
|
created: "2026-08-24"
|
||||||
updated: "2026-08-24"
|
updated: "2026-08-28"
|
||||||
quality_dor: DoR-Ok
|
quality_dor: DoR-Ok
|
||||||
quality_dor_at: "2026-08-24"
|
quality_dor_at: "2026-08-24"
|
||||||
quality_dor_by: codex
|
quality_dor_by: codex
|
||||||
|
|
@ -104,7 +104,7 @@ the live API.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: CUST-WP-0067-T02
|
id: CUST-WP-0067-T02
|
||||||
status: progress
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "4093e928-d752-5a91-96c3-2e80f0e1dac5"
|
state_hub_task_id: "4093e928-d752-5a91-96c3-2e80f0e1dac5"
|
||||||
```
|
```
|
||||||
|
|
@ -161,6 +161,14 @@ any repo and was blocked in-session; the change and its sequencing constraint
|
||||||
are recorded in `docs/recovery/tunnels-yaml-proposed-changes-CUST-WP-0067.md`.
|
are recorded in `docs/recovery/tunnels-yaml-proposed-changes-CUST-WP-0067.md`.
|
||||||
Remote agents and the documented port map must be repointed *before* removal.
|
Remote agents and the documented port map must be repointed *before* removal.
|
||||||
|
|
||||||
|
**Done (2026-08-28):** both reverse relays are gone from
|
||||||
|
`~/.config/bridge/tunnels.yaml` and from `bridge status`. The only
|
||||||
|
`state-hub-*` tunnel is `state-hub-primary` (`direction: local`,
|
||||||
|
`127.0.0.1:8000` → cluster `10.43.68.154:8000`). Live check: one listener on
|
||||||
|
8000, it is `ssh`, and `/state/health` reports `instance_role=primary`
|
||||||
|
(`railiance01`). Local uvicorn is not running. Cache Postgres
|
||||||
|
`infra-postgres-1` is still up on purpose until `CUST-WP-0068-T08`.
|
||||||
|
|
||||||
## Make the hub target explicit and unspoofable
|
## Make the hub target explicit and unspoofable
|
||||||
|
|
||||||
```task
|
```task
|
||||||
|
|
@ -650,7 +658,7 @@ their classification later, with no separate backfill path to write or trust.
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: CUST-WP-0067-T10
|
id: CUST-WP-0067-T10
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "509414a4-dee9-5892-96ab-eab8f85982c8"
|
state_hub_task_id: "509414a4-dee9-5892-96ab-eab8f85982c8"
|
||||||
```
|
```
|
||||||
|
|
@ -731,3 +739,6 @@ It is a content-conformance pass, an identifier decision overlapping
|
||||||
`RMGR-WP-0005`, an orphan disposition, and a per-repository unblocking pass —
|
`RMGR-WP-0005`, an orphan disposition, and a per-repository unblocking pass —
|
||||||
across ~40 repositories, several needing owner judgement. The mechanical share
|
across ~40 repositories, several needing owner judgement. The mechanical share
|
||||||
is done.
|
is done.
|
||||||
|
|
||||||
|
**Done (2026-08-25, recorded 2026-08-28):** promoted to `CUST-WP-0068`. This
|
||||||
|
task's remaining work is that workplan; it is not tracked twice.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue