Generated from cat-harness/skills/sdlc/sdlc-core/finding-software.md — do not edit here.

✎ Edit this page’s source

Finding software — get it, do not route around it

Owner, 2026-10-09, after an ingest stopped at a missing tesseract and the agent fed the OCR step a cached copy instead: “why no tesseract?”, then “update skills and tools on finding software”, “github egress”, “also add apt”. The container is the agent’s to provision. Installing took one command and twenty seconds; the workaround was the wrong output, quietly.

The rule

A missing program is a step, not a stop. When a script says it needs something:

bun run cat software:ensure --for scripts/pdf-ocr.py   # what that script needs
bun run cat software:ensure tesseract pdftoppm         # by name
bun run cat software:check                             # report only

scripts/ensure-software.ts walks a ladder per entry and stops at the first rung that leaves the thing present (checked by presence, never by exit code):

runghowoffered when
presentcommand -v, or import for a Python modulealways checked first
aptapt-get install -y --no-install-recommends, one apt-get update and retry on failureapt-get exists and the agent is root or has passwordless sudo
pippython3 -m pip install, with --break-system-packages on an externally managed interpreterthe entry names a pip package
GitHub releasethe asset for this platform at a pinned tag, verified against its sha256 before unpacking, into ~/.local/binthe entry declares one

Every attempt is printed, including the rungs not offered and why. Exit 0 means everything asked for is present.

What not to do instead

  • Do not substitute. A cached OCR text, a weaker backend, a skipped step, a “could not determine” you could have determined: each produces output that reads as the real thing. If the right tool cannot be installed, say which rung failed and how (the tool prints it), and stop.
  • Do not install by hand and move on. If a script needs something the table does not list, add an entry to scripts/software.json — how to get it, its licence, and usedBy — so the next agent’s --for finds it. The tool refuses an undeclared name (exit 2) for exactly this reason.
  • Do not download an unpinned or unverified binary. A GitHub entry carries a tag (never latest) and a sha256 per platform, taken from the release’s own checksums file; the table’s test enforces both.
  • A licence is recorded, not a reason to refuse. PyMuPDF is AGPL: it is installed into the environment to RUN tools and is never vendored or shipped (see pdf-structure.py’s licence note). The entry says so.

When a rung is blocked by the network

That is blocked-build-dependencies: read the refusal (curl -sS "$HTTPS_PROXY/__agentproxy/status") before working around it. Measured from an agent container on 2026-10-09:

routeresult
apt (Ubuntu archive), PyPI, npmopen
github.com/<o>/<r>/releases/download/<tag>/<asset>200 — the GitHub rung works
raw.githubusercontent.com200
git clone https://github.com/...works
codeload.github.com archives, github.com/<o>/<r>/raw/...403
api.github.com403 at the time measured (it answered earlier the same day; treat as unreliable — find a tag with git ls-remote --tags instead)

A missing tool for the WHOLE environment

When every new session will need it, the durable fix is the environment’s setup script (the person’s cloud environment settings), not each agent installing it again. Say so to the person once, with the line to add — bun run cat software:ensure --for <script> covers it — and carry on installing it in this session meanwhile.


folio-assistant — code under the Apache License 2.0, documentation under CC BY 3.0. Generated schema and API references are rebuilt from source on every push to main.

deployed b08c961 · 2026-10-23T23:11:09Z