feat(retirement): route SBOM scans through repo-manager
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
This commit is contained in:
parent
b9d9ffed5f
commit
81861d816b
3 changed files with 70 additions and 22 deletions
|
|
@ -144,12 +144,12 @@ def record_mutation(
|
|||
detail: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
try:
|
||||
from datetime import datetime, timezone
|
||||
from datetime import UTC, datetime
|
||||
|
||||
path = meter_path()
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
row = {
|
||||
"ts": datetime.now(timezone.utc).isoformat(),
|
||||
"ts": datetime.now(UTC).isoformat().replace("+00:00", "Z"),
|
||||
"source": source,
|
||||
"kind": kind,
|
||||
"repo_slug": repo_slug,
|
||||
|
|
@ -409,6 +409,31 @@ def rm_update_register_entry(
|
|||
return result
|
||||
|
||||
|
||||
def rm_scan_sbom(
|
||||
*,
|
||||
repo_path: str | Path,
|
||||
repo_slug: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Derive a versioned SBOM snapshot from repository-owned sources."""
|
||||
args = ["sbom", "scan", "--path", str(repo_path)]
|
||||
if repo_slug:
|
||||
args.extend(["--slug", repo_slug])
|
||||
|
||||
code, out, err = run_rmgr(args)
|
||||
try:
|
||||
result = json.loads(out.strip() or "{}")
|
||||
except json.JSONDecodeError:
|
||||
result = {
|
||||
"ok": False,
|
||||
"error": f"rmgr non-json exit={code} stderr={err!r} stdout={out[:500]!r}",
|
||||
}
|
||||
if code != 0:
|
||||
result.setdefault("ok", False)
|
||||
result.setdefault("error", f"rmgr exit={code} stderr={err!r}")
|
||||
result["exit_code"] = code
|
||||
return result
|
||||
|
||||
|
||||
def rm_scaffold(
|
||||
*,
|
||||
repo_path: str | Path,
|
||||
|
|
|
|||
|
|
@ -208,16 +208,13 @@ flow and needs a State Hub API restart to take effect. Piloting on
|
|||
| | |
|
||||
| --- | --- |
|
||||
| Flag | `RM_SLICE_REGISTERS` |
|
||||
| Owner API | **does not exist** — see the readiness correction above |
|
||||
| Rollback | n/a until there is something to roll back from |
|
||||
| Owner API | `rmgr register ...`; `rmgr sbom scan|licence-report` |
|
||||
| Rollback | flag → `off`; retain current State Hub projection |
|
||||
|
||||
**Blocked.** repo-manager has no register surface: no SBOM ingest, no repo
|
||||
goals, no contributions, no technical-debt or extension-point registry. This
|
||||
slice cannot start until a repo-manager workplan builds one.
|
||||
|
||||
Low coupling and low write volume still make this the right slice to prove the
|
||||
`RM_SLICE_*` pattern **once the owner side exists** — but that is a
|
||||
repo-manager-side prerequisite, not a State Hub task.
|
||||
**Owner ready (2026-08-21).** Finished RMGR-WP-0008 supplies the shared six-kind
|
||||
register spine, governed compatibility adapter, lockfile-derived SBOM and
|
||||
licence/copyleft report. Historical SBOM rows are cache provenance; rebuild
|
||||
from current repository sources rather than copying them into authority.
|
||||
|
||||
### A4 · Work + repo UI — 22 items
|
||||
`work-coordination-ui` (14, `replace`) · `repo-ui` (8)
|
||||
|
|
@ -225,11 +222,14 @@ repo-manager-side prerequisite, not a State Hub task.
|
|||
| | |
|
||||
| --- | --- |
|
||||
| Flag | `RM_SLICE_UI` |
|
||||
| Owner API | owner-side dashboard; hub pages redirect |
|
||||
| Owner API | hub-core projection/query plus hub/ops UI clients |
|
||||
| Rollback | remove redirect; hub pages are unchanged behind it |
|
||||
|
||||
`replace`, not `move`: the contract changes. Must follow A1–A3, since the pages
|
||||
render what those slices serve.
|
||||
render what those slices serve. RMGR-ADR-003 corrects the earlier inventory
|
||||
assignment: Repo Manager owns the repository contracts, not a dashboard. A4's
|
||||
UI implementation therefore rides with B5 even though these 22 inventory items
|
||||
remain grouped here for traceability.
|
||||
|
||||
### A5 · Topic spine — 8 items
|
||||
`topic-spine` (8, `replace`)
|
||||
|
|
@ -240,14 +240,17 @@ render what those slices serve.
|
|||
| Owner API | `rmgr` topic/classification contract |
|
||||
| Rollback | flag → `off` |
|
||||
|
||||
Last in Wave A: it is a `replace` under a changed contract, and A1–A4 read it.
|
||||
The versioned Repo Manager classification contract is published and accepted by
|
||||
finished HUB-WP-0004. Consumer route switching remains a State Hub cutover
|
||||
action.
|
||||
|
||||
---
|
||||
|
||||
## Wave B — hub-core (128 items) · gated on HUB-WP-0004
|
||||
## Wave B — hub-core (128 items) · HUB-WP-0004 owner gate satisfied
|
||||
|
||||
HUB-WP-0004 is `proposed`. **No hub-side adapter work until it is at least
|
||||
`ready`.** Slices are listed to fix scope and order, not to start.
|
||||
HUB-WP-0004 finished 2026-08-21 and accepted the project architecture, runtime,
|
||||
ports, conformance scaffold, and absorption plan. Individual production
|
||||
cutovers still require their slice evidence and operator gates.
|
||||
|
||||
| Slice | Capabilities | Items |
|
||||
| --- | --- | --- |
|
||||
|
|
@ -435,14 +438,14 @@ discovered live at cutover.
|
|||
|
||||
1. ~~Resolve the legacy-meter window discrepancy~~ — done 2026-08-20; E1 and the
|
||||
evidenced part of E2 are executed.
|
||||
2. **Raise a `repo-manager` workplan for the register surface** (A3), the UI
|
||||
surface (A4), and the topic-spine contract (A5). Until that exists, 79 of
|
||||
Wave A's 165 items have nowhere to go. This is now the critical path for the
|
||||
whole retirement, ahead of anything State Hub can do to itself.
|
||||
2. ~~Build the Repo Manager receiving surface for A3/A5 and settle A4
|
||||
ownership~~ — finished in RMGR-WP-0008. A4 belongs to hub-core projection UI,
|
||||
not Repo Manager.
|
||||
3. Cut **A2a** (task status + reconcile, ~6 items) — the only slice in the plan
|
||||
executable today. First step is operational: create `~/.repo-manager/dual-run.yaml`
|
||||
and enable writeback/reconcile for one pilot repo, then watch the mutation
|
||||
meter. A2b–A2e need repo-manager parsers and adapters that do not exist.
|
||||
4. Do not open Wave B until HUB-WP-0004 reaches `ready`; 125 items sit behind it.
|
||||
4. HUB-WP-0004's owner-readiness gate is satisfied; advance Wave B only through
|
||||
its per-slice dual-run and operator gates.
|
||||
5. Do not build register, UI, or topic capability in State Hub to unblock
|
||||
items 2–3 — `policies/retirement-freeze.md` makes that inadmissible.
|
||||
|
|
|
|||
|
|
@ -93,3 +93,23 @@ def test_rm_update_register_entry_builds_shared_spine_command(monkeypatch):
|
|||
assert seen["args"][0:2] == ["register", "put"]
|
||||
assert "technical-debt" in seen["args"]
|
||||
assert '{"severity":"high"}' in seen["args"]
|
||||
|
||||
|
||||
def test_rm_scan_sbom_builds_derived_snapshot_command(monkeypatch):
|
||||
seen = {}
|
||||
|
||||
def fake_run(args, *, timeout=120):
|
||||
seen["args"] = args
|
||||
return (
|
||||
0,
|
||||
'{"schema":"repo-manager.sbom-snapshot.v1","ok":true,"entry_count":3}',
|
||||
"",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(adapter, "run_rmgr", fake_run)
|
||||
result = adapter.rm_scan_sbom(repo_path="/repos/demo", repo_slug="demo")
|
||||
|
||||
assert result["ok"] is True
|
||||
assert result["schema"] == "repo-manager.sbom-snapshot.v1"
|
||||
assert result["exit_code"] == 0
|
||||
assert seen["args"] == ["sbom", "scan", "--path", "/repos/demo", "--slug", "demo"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue