Adopt the design of google/ax (Task / Workspace / Model manifests, a reconciler, isolated resumable execution, a runner contract), not yet its software: it needs Kubernetes and Agent Substrate, lists only Google and Anthropic providers, and has no pipelines, artifacts or budgets. We add a thin Run layer for those. Each pipeline concern (classify, triage, repo-facts, verify, judge, recheck, score) becomes a declared task with its own inputs, outputs and budget. That bounds the tokens per task and lets each be developed alone. Decisions listed for the maintainer. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
4.8 KiB
Brief: an AI service plane for OpenUltraSAST (Kubernetes-style, at the model level)
Status: brief for maintainer decision (2026-09-29). Not a spec yet; no approvals reset.
Why now
Two measurements say the current pipeline cannot be developed further at its token cost:
- Development: 90% of the tokens of the last session were tool I/O (inline scripts and their output), because
every stage lives in ad-hoc scripts run from one long conversation (
dev-tokens-are-tool-iomemory). - Pipeline: verification costs $0.009-0.022 per candidate for two passes because every hunt re-derives the same
per-repository facts and every pass re-sends its whole conversation; resume, budgets and pass agreement are
hand-rolled per script (
results-v2-model-pipeline.json).
Google's ax (open-sourced 2026-09; Apache-2, Go, early) frames agent work as a Kubernetes-style service:
declarative Task / Workspace / Model manifests, a reconciler, sandboxed isolated execution, suspend and
resume, and a runner contract any binary can honour. Its software needs Kubernetes and Agent Substrate, lists
only Google and Anthropic providers, and has no pipelines, artifacts or budgets yet. Its design is what we
adopt; its software is a later deployment target.
The model
| ax primitive | OpenUltraSAST meaning |
|---|---|
Workspace |
one pinned checkout: repository, commit, product-file filter; materialised once, bound by many tasks |
Model |
provider, model id, credentials, prices, per-task budget (usd, calls); DeepSeek via an adapter |
Task |
one concern, isolated: declared inputs (artifacts), declared outputs, resource limits, idempotent per unit, resumable |
| runner contract | our Python entrypoint reads the task and workspace YAML from the environment and writes artifacts |
| reconciler | a local loop over manifests today (one engine container at a time on the 7 GB host); ax later, unchanged manifests |
What ax lacks and we add as a thin layer: a Run manifest that lists tasks with dependencies (a DAG), artifact
locations, and budgets. This layer is what the reconciler executes; the tasks themselves are ax-shaped.
The concerns as tasks
classify-sinks-- workspace ->candidates.json(model-read, no tools). Exists:model_sinks.py.triage-- candidates ->kept.jsonwithdata_originper function (no tools). Exists insideverify_batched.py.repo-facts-- workspace ->facts.json: per-file callers and entry points, computed once, no model. New; this is the memory layer, and it is an artifact, not a cache.verify-- kept + facts,pass=n->verdicts.jsonl, one hunt per file, budgeted. Exists as a script.judge-- verdicts ->agreed.json: agreement across passes, then a no-tools judgement over each agreed finding's rationale and code. Agreement exists; the judge does not.recheck-fixed-- fixed workspace + agreed ->fixed_alerts.json. Exists (--fixed).score-- all of the above + the population record ->score.json. Exists (score_batched.py).
Each task: one manifest, one entrypoint, one fixture test, one budget. A task that fails leaves no partial truth: it is unfinished, never "clean" (the 402 lesson).
What it buys, measured or estimated
- Tokens per task bounded by declared inputs;
repo-factsreplaces the 1-2 tool turns per hunt that today re-grep callers (each turn re-sends the whole context); estimated 20-40% per hunt, to be measured on the 46-candidate validation set. - Two passes and the judge become tasks with their own budgets instead of loops inside a script.
- Development in small contexts: one task type at a time, against a fixture; the transcript measure is the check.
- Portability: the same manifests run on the laptop reconciler now and on
axon a cluster later.
What it does not buy
Recall and precision come from the model and the questions asked, not from orchestration. The open problems recorded in the strategy review (judge quality, the config-family question, pass stability) remain the detection work; this design makes them affordable to attack.
Risks
axis early ("heavy development", "major breaking changes"); we depend on its shape, not its releases.- Its provider list excludes DeepSeek; an adapter task image is ours to keep.
- A local reconciler is new code; it must stay thin (manifests, DAG, artifacts, budgets, resume) or it becomes the next thing that eats tokens.
Decisions needed
- Adopt this as a spec (
/kiro-spec-initon this brief) and pause further script-level pipeline changes. - Manifest format: ax's
ax.io/v1alpha1kinds verbatim plus ourRunkind, so the later move is a rename of the reconciler, not of the manifests. - First increment:
repo-facts+verifyas tasks under the local reconciler, measured against the 46-candidate set for cost and recall before the other concerns move.