deps: move dependency scanning to the runner (drop in-process syft)Verified

In-process syft on the server doesn't scale (and cave repos are bare, so there's
no working tree to scan). Extraction now runs on a runner; the SBOM comes back
through the step log — no token piped into the runner container (Option B).

src/sbom.lisp:
  enqueue-deps-scan      schedule a workflow run (marker workflow_name=deps-scan,
                         image :deps-scan-image, one syft step) the runner picks up
  maybe-ingest-scan-run  on run finalize, read the scan step's log, extract the
                         CycloneDX JSON, ingest-repo-deps + dashboard refresh
  ingest-sbom-json       direct ingest (used by --sbom and the completion hook)
  %extract-json-object   tolerant JSON extraction from the step log
  removed: run-syft-sbom, scan-repo-deps, maybe-scan-repo-deps-async (in-process)

workflow.lisp: check-workflow-job-completion calls maybe-ingest-scan-run.
server.lisp: post-receive enqueues a runner scan on default-branch push.
main.lisp: `deps-scan` enqueues a runner scan; `--sbom` still ingests directly.
Containerfile.scan (alpine+bash+syft) + containers.yaml matrix -> cave-scan:main.

Operational note: a runner able to pull :deps-scan-image must be running, else
scans queue harmlessly. Guarded by :deps-scan-enabled (off by default).

Verified: make load clean (no refs to removed fns); 10 simulation assertions —
enqueue builds the right run/job/step, the completion hook ingests the SBOM from
the step log (past noise) producing graph + alerts, disabled is a no-op, and
non-scan runs are ignored. Full runner round-trip needs a live runner (not
exercised in the test harness).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Anthony Green2026-06-19 07:31:26 -0400 2df92ea7ad31e768884ea7e97c1af170a4d96890
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>