From 93b8174abdc31f8ac3057b4a791d233598056f27 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 28 Aug 2026 20:27:05 +0200 Subject: [PATCH] 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. --- ...nels-yaml-proposed-changes-CUST-WP-0067.md | 4 ++ intakes.md | 17 +++++++ tests/test_repo_classification_convergence.py | 23 ++++++++++ tools/repo_classification_convergence.py | 45 ++++++++++++++++--- workplans/ADHOC-2026-08-25.md | 4 +- ...UST-WP-0064-sbom-controlled-scan-inputs.md | 26 +++++++++-- ...-guidance-docs-to-the-new-sector-domain.md | 28 +++++++++--- ...WP-0067-hub-authority-target-resolution.md | 17 +++++-- 8 files changed, 143 insertions(+), 21 deletions(-) diff --git a/docs/recovery/tunnels-yaml-proposed-changes-CUST-WP-0067.md b/docs/recovery/tunnels-yaml-proposed-changes-CUST-WP-0067.md index 1e5287f..3aab265 100644 --- a/docs/recovery/tunnels-yaml-proposed-changes-CUST-WP-0067.md +++ b/docs/recovery/tunnels-yaml-proposed-changes-CUST-WP-0067.md @@ -60,3 +60,7 @@ Status 2026-08-24: On railiance01 both services are reachable in-cluster with no tunnel at all — 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`. diff --git a/intakes.md b/intakes.md index 302bd5e..704d30a 100644 --- a/intakes.md +++ b/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." 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." +``` diff --git a/tests/test_repo_classification_convergence.py b/tests/test_repo_classification_convergence.py index 5563628..15e9afe 100644 --- a/tests/test_repo_classification_convergence.py +++ b/tests/test_repo_classification_convergence.py @@ -81,3 +81,26 @@ def test_clean_active_fleet_converges_and_renders(tmp_path: Path) -> None: assert report["source_warning_count"] == 0 assert "active classified: 1" 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 diff --git a/tools/repo_classification_convergence.py b/tools/repo_classification_convergence.py index 4c64371..44dab43 100644 --- a/tools/repo_classification_convergence.py +++ b/tools/repo_classification_convergence.py @@ -10,6 +10,7 @@ from __future__ import annotations import argparse import json import os +import socket import sys import urllib.error import urllib.request @@ -50,17 +51,48 @@ def fetch_repositories( return payload -def _source_path(record: dict[str, Any]) -> Path | None: - local_path = record.get("local_path") - if not isinstance(local_path, str) or not local_path.strip(): - return None - return Path(local_path) / ".repo-classification.yaml" +def _checkout_candidates(record: dict[str, Any], *, hostname: str) -> list[Path]: + """Prefer this host's recorded checkout over a `local_path` from another machine.""" + seen: set[str] = set() + candidates: list[Path] = [] + + 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( records: list[dict[str, Any]], *, validate_sources: bool = True, + hostname: str | None = None, ) -> dict[str, Any]: active = [record for record in records if record.get("status") == "active"] 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] = {} allowed = load_allowed() if validate_sources else None + current_host = hostname or socket.gethostname() for record in active: slug = str(record.get("slug") or "") 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() if record.get("category") is None: diff --git a/workplans/ADHOC-2026-08-25.md b/workplans/ADHOC-2026-08-25.md index 5efc7b8..25164a3 100644 --- a/workplans/ADHOC-2026-08-25.md +++ b/workplans/ADHOC-2026-08-25.md @@ -4,10 +4,10 @@ type: workplan title: "Ad hoc tasks 2026-08-25" domain: infotech repo: the-custodian -status: active +status: finished owner: codex created: "2026-08-25" -updated: "2026-08-25" +updated: "2026-08-28" --- # Ad hoc tasks 2026-08-25 diff --git a/workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md b/workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md index dd6d00f..6c575b5 100644 --- a/workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md +++ b/workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md @@ -4,13 +4,13 @@ type: workplan title: "Controlled scan inputs for authoritative daily SBOM catch-up" domain: infotech repo: the-custodian -status: active +status: finished owner: codex topic_slug: custodian planning_priority: high planning_order: 64 created: "2026-08-22" -updated: "2026-08-23" +updated: "2026-08-28" quality_dor: DoR-Ok quality_dor_at: "2026-08-22" quality_dor_by: codex @@ -158,7 +158,7 @@ than the attended target set; T04 retains only the first unassisted fire. ```task id: CUST-WP-0064-T04 -status: progress +status: done priority: medium 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 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 - [x] Production scans consume a controlled, revision-pinned source input - [x] No workstation filesystem is mounted or implicitly trusted - [x] Nexus remains the only authoritative snapshot writer - [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 diff --git a/workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md b/workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md index b4d6cf0..c32c635 100644 --- a/workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md +++ b/workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md @@ -4,10 +4,10 @@ type: workplan title: "Reclassify repos and guidance docs to the new sector domain scheme" domain: infotech repo: the-custodian -status: active +status: finished owner: codex created: "2026-08-23" -updated: "2026-08-23" +updated: "2026-08-28" quality_dor: DoR-Ok quality_dor_at: "2026-08-23" quality_dor_by: codex @@ -74,7 +74,7 @@ project names remain valid outside that field. ```task id: CUST-WP-0065-T03 -status: progress +status: done priority: high 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 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 ```task id: CUST-WP-0065-T04 -status: progress +status: done priority: medium 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 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 **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] Legacy project identity is distinguished from sector-domain metadata - [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 -- [ ] Active fleet has zero unexplained null category projections +- [x] Active fleet has zero unexplained null category projections diff --git a/workplans/CUST-WP-0067-hub-authority-target-resolution.md b/workplans/CUST-WP-0067-hub-authority-target-resolution.md index 3018c46..695d950 100644 --- a/workplans/CUST-WP-0067-hub-authority-target-resolution.md +++ b/workplans/CUST-WP-0067-hub-authority-target-resolution.md @@ -7,7 +7,7 @@ repo: the-custodian status: active owner: codex created: "2026-08-24" -updated: "2026-08-24" +updated: "2026-08-28" quality_dor: DoR-Ok quality_dor_at: "2026-08-24" quality_dor_by: codex @@ -104,7 +104,7 @@ the live API. ```task id: CUST-WP-0067-T02 -status: progress +status: done priority: high 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`. 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 ```task @@ -650,7 +658,7 @@ their classification later, with no separate backfill path to write or trust. ```task id: CUST-WP-0067-T10 -status: todo +status: done priority: high 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 — across ~40 repositories, several needing owner judgement. The mechanical share 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.