From 8eac1b2c699ef762c49cae4e186afb7f637aa1ba Mon Sep 17 00:00:00 2001 From: Marius Ciepluch <11855163+norandom@users.noreply.github.com> Date: Fri, 2 Oct 2026 20:16:23 +0200 Subject: [PATCH] plane(1.1): PlaneProfile -- one TOML per cluster, env-overridable, no secrets ops/k8s/profiles/kind.toml and k3s.toml hold the values that differ per cluster (exec, kube_context, registry, images, router_url, ax_server, memory, atespace, snapshots_bucket, image_pull_secret, pools); kind-images.json carries the digest pin ops/ax/up.sh wrote. load_profile() reads OUSAST_PLANE_PROFILE (name or path, default kind), applies OUSAST_ and OUSAST_POOL__, and refuses an unknown key, a credential-shaped key or value, an exec outside local|remote and an images file without digest pins. `python -m openultrasast.plane.profile --print FIELD` serves shell scripts. The module-audit manifest is regenerated for the new module (counts 147/5, no orphan). Gates: pytest full suite 2002 passed, 1 failed (the module-audit manifest, fixed by regenerating it; tests/test_module_audit.py re-run exit 0), ruff check 0, ruff format 0, mypy 0. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo --- .kiro/specs/plane-on-kubernetes/tasks.md | 2 +- .../measurements/2026-09-08-module-audit.json | 3539 +++++++++-------- ops/k8s/profiles/k3s.toml | 26 + ops/k8s/profiles/kind-images.json | 3 + ops/k8s/profiles/kind.toml | 25 + src/openultrasast/plane/profile.py | 266 ++ tests/test_plane_profile.py | 156 + 7 files changed, 2250 insertions(+), 1767 deletions(-) create mode 100644 ops/k8s/profiles/k3s.toml create mode 100644 ops/k8s/profiles/kind-images.json create mode 100644 ops/k8s/profiles/kind.toml create mode 100644 src/openultrasast/plane/profile.py create mode 100644 tests/test_plane_profile.py diff --git a/.kiro/specs/plane-on-kubernetes/tasks.md b/.kiro/specs/plane-on-kubernetes/tasks.md index d394d63..a3405bd 100644 --- a/.kiro/specs/plane-on-kubernetes/tasks.md +++ b/.kiro/specs/plane-on-kubernetes/tasks.md @@ -10,7 +10,7 @@ its kind proof and the record it writes under `benchmarks/measurements/-k8 go-ahead** with a ceiling; steps that need a hand on a cluster that does not exist yet are marked **manual**. - [ ] 1. Profile and literals (design section 1; open-question checks 2, 3, 7) -- [ ] 1.1 `PlaneProfile` +- [x] 1.1 `PlaneProfile` - `src/openultrasast/plane/profile.py`: frozen dataclass with the fields of design section 1; `load_profile()` reads `ops/k8s/profiles/.toml` (a name or a path; default `kind`), then applies `OUSAST_` overrides; rejects an unknown key, a secret-looking key (`*_KEY`, `*_SECRET`), an `exec` diff --git a/benchmarks/measurements/2026-09-08-module-audit.json b/benchmarks/measurements/2026-09-08-module-audit.json index 56cfd90..caaab40 100644 --- a/benchmarks/measurements/2026-09-08-module-audit.json +++ b/benchmarks/measurements/2026-09-08-module-audit.json @@ -1,1770 +1,1777 @@ { - "modules": 151, - "counts": { - "load_bearing": 146, - "standalone_capability": 5 + "modules": 152, + "counts": { + "load_bearing": 147, + "standalone_capability": 5 + }, + "orphaned": [], + "standalone": { + "fusion": "two-panel adjudication engine; reached from the scan's report stage and used standalone", + "mcp": "narrow MCP server over stdio; its own `ousast mcp` entry point and OpenCode integration", + "push_inputs": "pre-push-safety-net task 1.2: offline input provenance validator; python -m openultrasast.push_inputs", + "push_scoring": "pre-push-safety-net task 1.3: frozen-profile diagnostic outcome scorer; python -m openultrasast.push_scoring", + "skills": "skill router; consumed by mcp and by the OpenCode integration, not by the scan path" + }, + "rows": [ + { + "module": "benchmark", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "gate", + "improve.evolve", + "map_gate", + "mcp", + "pairs", + "plane.tasks.loop" + ] }, - "orphaned": [], - "standalone": { - "fusion": "two-panel adjudication engine; reached from the scan's report stage and used standalone", - "mcp": "narrow MCP server over stdio; its own `ousast mcp` entry point and OpenCode integration", - "push_inputs": "pre-push-safety-net task 1.2: offline input provenance validator; python -m openultrasast.push_inputs", - "push_scoring": "pre-push-safety-net task 1.3: frozen-profile diagnostic outcome scorer; python -m openultrasast.push_scoring", - "skills": "skill router; consumed by mcp and by the OpenCode integration, not by the scan path" + { + "module": "calibration", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] }, - "rows": [ - { - "module": "benchmark", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "gate", - "improve.evolve", - "map_gate", - "mcp", - "pairs", - "plane.tasks.loop" - ] - }, - { - "module": "calibration", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "cli", - "classification": "load_bearing", - "reason": "entry point", - "importers": [ - "mcp" - ] - }, - { - "module": "complexity", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "complexity.hints", - "complexity.ledger", - "complexity.map", - "map_gate", - "plane.tasks.verify", - "regress.candidate", - "semantic.prove_filter", - "tool_hunter" - ] - }, - { - "module": "complexity.hints", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "complexity", - "complexity.map" - ] - }, - { - "module": "complexity.ledger", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "complexity", - "complexity.map", - "regress.candidate" - ] - }, - { - "module": "complexity.map", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "complexity", - "complexity.hints", - "complexity.ledger", - "map_gate", - "plane.tasks.verify", - "regress.candidate", - "semantic.prove_filter", - "tool_hunter" - ] - }, - { - "module": "complexity.signals", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "complexity", - "complexity.hints", - "complexity.map" - ] - }, - { - "module": "config", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "harness", - "hunter_tools", - "learn.cli", - "model.endpoint", - "pairs", - "plane.memory", - "push.assistance", - "push.policy", - "push.runner", - "regress.candidate", - "regress.recipes", - "run" - ] - }, - { - "module": "contracts", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 7.3: strict shared manifest serialization, independent of push", - "importers": [ - "config", - "cpg.artifact", - "model.contracts", - "push.cache", - "push.contracts", - "push.policy", - "push.vetoes" - ] - }, - { - "module": "cpg", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 4: the CPG seam package", - "importers": [ - "cli", - "cpg.artifact", - "cpg.backend", - "model.config_value", - "model.dominance", - "model.partitions", - "model.pipeline", - "model.scan", - "model.taint", - "push.cache", - "push.eligibility", - "push.policy", - "push.reuse", - "push.runner" - ] - }, - { - "module": "cpg.artifact", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 4.3/7.3: backend graph identity and live lease contract; validation integration pending", - "importers": [ - "cpg.backend", - "push.cache", - "push.eligibility", - "push.policy", - "push.reuse", - "push.runner" - ] - }, - { - "module": "cpg.backend", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 4.1/4.5: the single Joern subprocess boundary", - "importers": [ - "cli", - "cpg.artifact", - "model.config_value", - "model.dominance", - "model.partitions", - "model.pipeline", - "model.scan", - "model.taint", - "push.cache", - "push.policy", - "push.reuse", - "push.runner" - ] - }, - { - "module": "cpg.capability", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 4.2/4.3: the engine probe that degrades to suspicion", - "importers": [ - "cpg.backend" - ] - }, - { - "module": "cpg.session", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cpg.backend" - ] - }, - { - "module": "findings", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "benchmark", - "calibration", - "cli", - "complexity.ledger", - "complexity.map", - "complexity.signals", - "fusion", - "gate", - "hunter", - "hunter_tools", - "improve.evolve", - "map_gate", - "model.ladder", - "model.report", - "pairs", - "plane.tasks.alerts", - "rank", - "regress.candidate", - "reports", - "scoring.project_score", - "semantic.obligations.check", - "semantic.overlay", - "semantic.prove_filter", - "tool_hunter", - "verification" - ] - }, - { - "module": "fusion", - "classification": "standalone_capability", - "reason": "two-panel adjudication engine; reached from the scan's report stage and used standalone", - "importers": [ - "cli" - ] - }, - { - "module": "gate", - "classification": "load_bearing", - "reason": "entry point", - "importers": [ - "cli", - "plane.tasks.loop" - ] - }, - { - "module": "harness", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "hunter", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "hunter_tools", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "tool_hunter" - ] - }, - { - "module": "improve", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "improve.evolve", - "improve.memory", - "plane.tasks.loop" - ] - }, - { - "module": "improve.evolve", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "improve", - "plane.tasks.loop" - ] - }, - { - "module": "improve.journal", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "improve", - "improve.evolve", - "improve.memory", - "plane.tasks.loop" - ] - }, - { - "module": "improve.memory", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "improve.evolve", - "plane.tasks.loop" - ] - }, - { - "module": "improve.validator", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "improve", - "improve.evolve", - "improve.memory", - "plane.tasks.loop" - ] - }, - { - "module": "index", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "skills" - ] - }, - { - "module": "learn", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "learn.calibrate", - "learn.cli", - "learn.compile", - "learn.embeddings", - "learn.evaluate", - "learn.examples", - "learn.excerpt", - "learn.experiments", - "learn.features", - "learn.folds", - "learn.labels", - "learn.leaks", - "learn.program", - "learn.retrieve", - "learn.roles", - "plane.harvest", - "plane.tasks.features", - "plane.tasks.remember", - "plane.tasks.roles" - ] - }, - { - "module": "learn.calibrate", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.evaluate" - ] - }, - { - "module": "learn.cli", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "learn.compile", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli", - "learn.evaluate", - "learn.experiments" - ] - }, - { - "module": "learn.embeddings", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli", - "learn.retrieve" - ] - }, - { - "module": "learn.evaluate", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli", - "learn.compile", - "learn.experiments" - ] - }, - { - "module": "learn.examples", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli", - "learn.compile", - "learn.evaluate", - "learn.experiments", - "learn.folds", - "learn.program", - "learn.retrieve" - ] - }, - { - "module": "learn.excerpt", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.embeddings", - "learn.examples", - "learn.labels", - "plane.harvest" - ] - }, - { - "module": "learn.experiments", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli" - ] - }, - { - "module": "learn.features", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.excerpt", - "plane.tasks.features" - ] - }, - { - "module": "learn.folds", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli", - "learn.compile", - "learn.evaluate", - "learn.experiments", - "learn.program", - "learn.retrieve" - ] - }, - { - "module": "learn.labels", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "learn.calibrate", - "learn.cli", - "learn.compile", - "learn.evaluate", - "learn.examples", - "learn.excerpt", - "learn.retrieve", - "plane.harvest" - ] - }, - { - "module": "learn.leaks", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli" - ] - }, - { - "module": "learn.program", - "classification": "load_bearing", - "reason": "learned-decision-engine Req 3.1/3.3: the AI classifier program (signature, Retrieve -> Classify(k), response cache); `ousast learn compile` (task 6.5) and the decide task (task 8) are its callers", - "importers": [ - "learn.cli", - "learn.compile", - "learn.evaluate", - "learn.experiments" - ] - }, - { - "module": "learn.retrieve", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.evaluate", - "learn.program" - ] - }, - { - "module": "learn.roles", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.features", - "plane.tasks.features", - "plane.tasks.roles" - ] - }, - { - "module": "learn.schema", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.compile", - "learn.evaluate", - "learn.features", - "learn.leaks", - "learn.program", - "learn.retrieve", - "learn.roles", - "plane.tasks.remember", - "plane.tasks.roles" - ] - }, - { - "module": "map_gate", - "classification": "load_bearing", - "reason": "entry point", - "importers": [] - }, - { - "module": "mapping", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "gate", - "hunter_tools", - "improve.evolve", - "learn.features", - "map_gate", - "model.scan", - "pairs", - "plane.tasks.alerts", - "preprocess", - "push.runner" - ] - }, - { - "module": "mcp", - "classification": "standalone_capability", - "reason": "narrow MCP server over stdio; its own `ousast mcp` entry point and OpenCode integration", - "importers": [ - "cli" - ] - }, - { - "module": "model", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "cpg.backend", - "cpg.session", - "learn.cli", - "learn.features", - "learn.labels", - "model.candidates", - "model.config_value", - "model.dominance", - "model.layout", - "model.partitions", - "model.pipeline", - "model.regions", - "model.report", - "model.scan", - "model.specs", - "model.taint", - "plane.budget", - "plane.generate", - "plane.tasks.features", - "plane.tasks.repo_facts", - "preprocess", - "push.assistance", - "push.cache", - "push.contracts", - "push.policy", - "push.report", - "push.reuse", - "push.runner", - "push.snapshot", - "push.vetoes", - "tool_hunter" - ] - }, - { - "module": "model.audit", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 3: this classifier; run by the maintainer, not by the scan", - "importers": [] - }, - { - "module": "model.candidates", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "model.scan" - ] - }, - { - "module": "model.config_value", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 6.3: constant abstraction for the configuration families", - "importers": [ - "model.pipeline", - "model.scan" - ] - }, - { - "module": "model.contracts", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 4.1/7.3: generic deadline, change and exact scope contracts; integration pending", - "importers": [ - "cli", - "cpg.backend", - "cpg.session", - "model.partitions", - "model.regions", - "model.scan", - "push.assistance", - "push.cache", - "push.contracts", - "push.policy", - "push.report", - "push.reuse", - "push.runner", - "push.snapshot", - "push.vetoes" - ] - }, - { - "module": "model.dominance", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 6.2: guard dominance, the arbiter for bugs that never crash", - "importers": [ - "model.pipeline", - "model.scan" - ] - }, - { - "module": "model.endpoint", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 8: the bounded model client the hunter, the plane and the CLI share", - "importers": [ - "cli", - "learn.cli", - "plane.budget", - "plane.generate", - "push.assistance", - "tool_hunter" - ] - }, - { - "module": "model.evidence", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "model.scan" - ] - }, - { - "module": "model.ladder", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 5: the evidence ladder every finding carries", - "importers": [ - "model.config_value", - "model.dominance", - "model.pipeline", - "model.scan", - "model.taint", - "push.policy" - ] - }, - { - "module": "model.layout", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "model.partitions", - "model.scan", - "plane.tasks.repo_facts", - "preprocess", - "push.runner" - ] - }, - { - "module": "model.partitions", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "model.scan" - ] - }, - { - "module": "model.pipeline", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 8: the enumerator proposes, the LLM answers, the model disposes", - "importers": [ - "model.scan", - "push.policy" - ] - }, - { - "module": "model.regions", - "classification": "load_bearing", - "reason": "contributor-scan Req 2: regions from entry points, with the families each admits", - "importers": [ - "cli", - "model.layout", - "model.scan", - "push.policy", - "push.reuse", - "push.runner" - ] - }, - { - "module": "model.report", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 9.3/11.1: per-slice reporting with the overfitting gap", - "importers": [ - "cli" - ] - }, - { - "module": "model.scan", - "classification": "load_bearing", - "reason": "contributor-scan Req 3: the repository driver -- one CPG, one budget, ranked spend", - "importers": [ - "cli", - "push.policy", - "push.report", - "push.runner", - "push.vetoes" - ] - }, - { - "module": "model.shipped", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "push.runner" - ] - }, - { - "module": "model.specs", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 7.2-7.4: the security vocabularies the CPG queries are parameterised by", - "importers": [ - "model.config_value", - "model.dominance", - "model.pipeline", - "model.regions", - "model.report", - "model.scan", - "model.taint", - "push.policy" - ] - }, - { - "module": "model.taint", - "classification": "load_bearing", - "reason": "model-grounded-detection Req 6.1: taint reachability over the CPG, the flow-family arbiter", - "importers": [ - "model.pipeline", - "model.scan" - ] - }, - { - "module": "model.taxonomy", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "learn.features", - "learn.labels", - "model.candidates", - "model.report", - "model.scan", - "model.specs", - "plane.tasks.features" - ] - }, - { - "module": "pair_gate", - "classification": "load_bearing", - "reason": "entry point", - "importers": [ - "cli" - ] - }, - { - "module": "pairs", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "improve", - "improve.evolve", - "improve.memory", - "learn.examples", - "learn.labels", - "model.candidates", - "pair_gate", - "plane.harvest", - "plane.tasks.loop" - ] - }, - { - "module": "plane", - "classification": "load_bearing", - "reason": "ai-service-plane Req 1-4: the service plane package; runner and reconciler integration in tasks 3-4", - "importers": [ - "cli", - "improve.memory", - "learn.cli", - "learn.compile", - "learn.embeddings", - "learn.examples", - "learn.experiments", - "learn.program", - "plane.egress", - "plane.engine_alerts", - "plane.generate", - "plane.harvest", - "plane.memory", - "plane.reconciler", - "plane.router", - "plane.runner", - "plane.tasks.loop", - "plane.tasks.remember", - "plane.tasks.roles", - "plane.tasks.verify", - "plane.workspaces" - ] - }, - { - "module": "plane.budget", - "classification": "load_bearing", - "reason": "ai-service-plane Req 2.3/2.4: metered chat client with usd and calls ceilings; task integration in tasks 3, 6", - "importers": [ - "learn.cli", - "learn.compile", - "learn.embeddings", - "learn.experiments", - "learn.program", - "plane.generate", - "plane.tasks.roles", - "plane.tasks.verify" - ] - }, - { - "module": "plane.doctor", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "plane.reconciler" - ] - }, - { - "module": "plane.egress", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "plane.reconciler" - ] - }, - { - "module": "plane.engine_alerts", - "classification": "load_bearing", - "reason": "harnessx-removal Req 6.3: engine alerts where quick mode has no rules (PHP), on the host", - "importers": [ - "cli" - ] - }, - { - "module": "plane.generate", - "classification": "load_bearing", - "reason": "ai-service-plane task 7: an increment's Workspaces, per-case Tasks and Run from a recorded set", - "importers": [ - "cli", - "plane.harvest" - ] - }, - { - "module": "plane.harvest", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "plane.manifests", - "classification": "load_bearing", - "reason": "ai-service-plane Req 1.1-1.4: ax-shaped Task/Workspace/Model and the Run kind, validated before anything runs", - "importers": [ - "learn.cli", - "plane.egress", - "plane.generate", - "plane.harvest", - "plane.reconciler", - "plane.router", - "plane.runner", - "plane.workspaces" - ] - }, - { - "module": "plane.memory", - "classification": "load_bearing", - "reason": "harnessx-removal Req 6.1/6.4: the memory store (file, S3), ingest and fact reuse; `ousast plane remember`", - "importers": [ - "cli", - "improve.memory", - "learn.cli", - "learn.compile", - "learn.embeddings", - "learn.examples", - "learn.experiments", - "learn.program", - "plane.generate", - "plane.tasks.loop", - "plane.tasks.remember" - ] - }, - { - "module": "plane.reconciler", - "classification": "load_bearing", - "reason": "ai-service-plane Req 3.1-3.5, 7.3: ax-backed Run execution, status attribution and doctor; `ousast plane`", - "importers": [ - "cli", - "plane.engine_alerts", - "plane.generate", - "plane.harvest", - "plane.memory", - "plane.workspaces" - ] - }, - { - "module": "plane.router", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "plane.reconciler", - "plane.runner" - ] - }, - { - "module": "plane.runner", - "classification": "load_bearing", - "reason": "ai-service-plane Req 4.1-4.4: the ax-task-runner entrypoint (PID 1 of the task image); delivers the output directory", - "importers": [] - }, - { - "module": "plane.tasks", - "classification": "load_bearing", - "reason": "ai-service-plane Req 4.1: the task entrypoint package the runner imports by name; runner integration pending (task 3)", - "importers": [ - "cli", - "learn.excerpt", - "learn.features", - "learn.labels", - "learn.roles", - "plane.engine_alerts", - "plane.generate", - "plane.harvest", - "plane.memory", - "plane.tasks.agree", - "plane.tasks.alerts", - "plane.tasks.features", - "plane.tasks.remember", - "plane.tasks.roles" - ] - }, - { - "module": "plane.tasks.agree", - "classification": "load_bearing", - "reason": "ai-service-plane Req 5.3/2.1: agreement across two verify passes with per-candidate cost, turns and site match", - "importers": [] - }, - { - "module": "plane.tasks.alerts", - "classification": "load_bearing", - "reason": "harnessx-removal Req 6.3: quick-mode alerts on a case's vulnerable and fixed pins, model-free", - "importers": [ - "learn.features", - "plane.engine_alerts", - "plane.tasks.features" - ] - }, - { - "module": "plane.tasks.features", - "classification": "load_bearing", - "reason": "learned-decision-engine Req 1.1-1.3/7.2: one allow-listed feature record per candidate, model-free", - "importers": [] - }, - { - "module": "plane.tasks.loop", - "classification": "load_bearing", - "reason": "harnessx-removal Req 6.3: the improvement loop's snapshot, measure, propose and improve steps", - "importers": [ - "plane.memory" - ] - }, - { - "module": "plane.tasks.remember", - "classification": "load_bearing", - "reason": "harnessx-removal Req 6.1: a case's delivered artifacts as memory rows, model-free", - "importers": [ - "cli" - ] - }, - { - "module": "plane.tasks.repo_facts", - "classification": "load_bearing", - "reason": "ai-service-plane Req 5.1/5.2: source-only functions and callers per file; runner integration pending", - "importers": [ - "learn.excerpt", - "learn.features", - "learn.labels", - "learn.roles", - "plane.generate", - "plane.harvest", - "plane.tasks.alerts", - "plane.tasks.features", - "plane.tasks.remember", - "plane.tasks.roles" - ] - }, - { - "module": "plane.tasks.roles", - "classification": "load_bearing", - "reason": "learned-decision-engine Req 8.1: model role classification per file chunk (model_sinks.py moved in); its Run template comes with the harvest (task 5)", - "importers": [ - "plane.harvest" - ] - }, - { - "module": "plane.tasks.verify", - "classification": "load_bearing", - "reason": "ai-service-plane Req 5.3/2.1-2.3/7.2: the batched hunt with known callers, metered and resumable per file hunt", - "importers": [ - "plane.generate", - "plane.harvest", - "plane.tasks.agree", - "plane.tasks.roles" - ] - }, - { - "module": "plane.workspaces", - "classification": "load_bearing", - "reason": "ai-service-plane task 4: Workspace manifests per case pin of a population; `ousast plane workspaces`", - "importers": [ - "cli" - ] - }, - { - "module": "policy", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "findings", - "hunter", - "improve.evolve", - "improve.validator", - "plane.tasks.loop", - "scoring.project_score", - "semantic.overlay" - ] - }, - { - "module": "policy.verycode", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "policy" - ] - }, - { - "module": "preprocess", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "complexity.hints", - "complexity.map", - "complexity.signals", - "findings", - "gate", - "hunter", - "hunter_tools", - "improve.evolve", - "index", - "learn.features", - "learn.labels", - "learn.roles", - "map_gate", - "mapping", - "model.candidates", - "model.partitions", - "model.scan", - "pairs", - "plane.harvest", - "plane.tasks.alerts", - "plane.tasks.features", - "plane.tasks.repo_facts", - "push.runner", - "rank", - "regress.candidate", - "semantic.overlay", - "skills" - ] - }, - { - "module": "profiling", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "provenance", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "provider", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli", - "learn.compile", - "learn.program", - "model.endpoint", - "tool_hunter" - ] - }, - { - "module": "provider.openrouter", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.cli", - "learn.compile", - "learn.program", - "model.endpoint", - "tool_hunter" - ] - }, - { - "module": "push", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 5.5/7.3: experimental push contract package; no hook installed", - "importers": [ - "cli", - "push.assistance", - "push.eligibility", - "push.policy", - "push.report", - "push.reuse", - "push.runner", - "push.snapshot", - "push.vetoes" - ] - }, - { - "module": "push.assistance", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "push.runner" - ] - }, - { - "module": "push.cache", - "classification": "load_bearing", - "reason": "pre-push-safety-net task 6.2: bounded artifact publication and leased eviction; runner integration in 6.4", - "importers": [ - "push.policy", - "push.reuse", - "push.runner" - ] - }, - { - "module": "push.contracts", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 5.5/7.3: immutable comparisons and independent result statuses; runner pending", - "importers": [ - "push.policy", - "push.report", - "push.reuse", - "push.runner", - "push.snapshot", - "push.vetoes" - ] - }, - { - "module": "push.eligibility", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "push.runner" - ] - }, - { - "module": "push.policy", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 2.4/3.1/3.4/4.1: targeted same-semantics base comparison; replay integration pending", - "importers": [ - "push.eligibility", - "push.report", - "push.runner", - "push.vetoes" - ] - }, - { - "module": "push.report", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 5.1/5.2/5.5/7.3: compact output and complete artifacts; runner integration pending", - "importers": [ - "push.assistance", - "push.runner" - ] - }, - { - "module": "push.reuse", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "push.runner" - ] - }, - { - "module": "push.runner", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "push.assistance" - ] - }, - { - "module": "push.snapshot", - "classification": "load_bearing", - "reason": "pre-push-safety-net Req 1.1/1.3/1.4/7.1: local immutable ref resolution; runner integration pending", - "importers": [ - "push.runner" - ] - }, - { - "module": "push.vetoes", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "push.runner" - ] - }, - { - "module": "push_inputs", - "classification": "standalone_capability", - "reason": "pre-push-safety-net task 1.2: offline input provenance validator; python -m openultrasast.push_inputs", - "importers": [] - }, - { - "module": "push_scoring", - "classification": "standalone_capability", - "reason": "pre-push-safety-net task 1.3: frozen-profile diagnostic outcome scorer; python -m openultrasast.push_scoring", - "importers": [ - "push.eligibility" - ] - }, - { - "module": "rank", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "calibration", - "cli", - "findings", - "gate", - "hunter", - "hunter_tools", - "improve.evolve", - "map_gate", - "pairs", - "plane.tasks.alerts" - ] - }, - { - "module": "redaction", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "harness", - "model.pipeline", - "push.assistance", - "push.report", - "reports", - "semantic.obligations.intent" - ] - }, - { - "module": "regress", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "regress.candidate" - ] - }, - { - "module": "regress.candidate", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "regress" - ] - }, - { - "module": "regress.recipes", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "regress", - "regress.candidate" - ] - }, - { - "module": "regress.safety", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "regress", - "regress.candidate" - ] - }, - { - "module": "regress.verdict", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "regress", - "regress.candidate" - ] - }, - { - "module": "reports", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "repos", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "push_inputs" - ] - }, - { - "module": "ruleset", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "findings", - "hunter", - "improve.evolve", - "improve.memory", - "improve.validator", - "learn.features", - "learn.labels", - "learn.roles", - "pairs", - "plane.tasks.alerts", - "plane.tasks.features", - "plane.tasks.loop", - "ruleset.store", - "semantic.facts" - ] - }, - { - "module": "ruleset.frameworks", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "learn.labels", - "learn.roles", - "ruleset.store", - "semantic.facts" - ] - }, - { - "module": "ruleset.store", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "improve.validator", - "ruleset" - ] - }, - { - "module": "run", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "reports" - ] - }, - { - "module": "sandbox", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "regress.candidate", - "regress.recipes", - "regress.safety", - "regress.verdict" - ] - }, - { - "module": "sandbox.probe", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "sandbox" - ] - }, - { - "module": "sandbox.runner", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "regress.candidate", - "regress.recipes", - "regress.safety", - "sandbox" - ] - }, - { - "module": "scoring", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "improve.evolve" - ] - }, - { - "module": "scoring.project_score", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "scoring" - ] - }, - { - "module": "semantic", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "cpg.capability", - "hunter_tools", - "learn.features", - "learn.roles", - "mapping", - "model.candidates", - "model.layout", - "model.scan", - "model.specs", - "pairs", - "semantic.cst", - "semantic.engines", - "semantic.functions", - "semantic.ir", - "semantic.obligations.check", - "semantic.obligations.facts", - "semantic.obligations.operations", - "semantic.obligations.shapes", - "semantic.overlay", - "semantic.prove_filter", - "semantic.taint" - ] - }, - { - "module": "semantic.cst", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "semantic.ir" - ] - }, - { - "module": "semantic.engines", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cpg.capability", - "semantic", - "semantic.overlay" - ] - }, - { - "module": "semantic.extra", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "semantic", - "semantic.cst", - "semantic.engines" - ] - }, - { - "module": "semantic.facts", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "hunter_tools", - "learn.features", - "learn.roles", - "mapping", - "model.candidates", - "model.layout", - "model.scan", - "model.specs", - "pairs", - "semantic", - "semantic.obligations.check", - "semantic.obligations.facts", - "semantic.obligations.operations", - "semantic.obligations.shapes", - "semantic.overlay", - "semantic.taint" - ] - }, - { - "module": "semantic.functions", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "hunter_tools", - "pairs", - "semantic.obligations.shapes", - "semantic.overlay" - ] - }, - { - "module": "semantic.ir", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "hunter_tools", - "model.candidates", - "pairs", - "semantic.cst", - "semantic.functions", - "semantic.obligations.check", - "semantic.obligations.operations", - "semantic.obligations.shapes", - "semantic.overlay", - "semantic.taint" - ] - }, - { - "module": "semantic.obligations", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "hunter_tools", - "model.candidates", - "model.specs", - "pairs", - "semantic.obligations.check", - "semantic.obligations.dominance", - "semantic.obligations.intent", - "semantic.obligations.operations", - "semantic.obligations.policy", - "semantic.obligations.shapes", - "semantic.obligations.siblings" - ] - }, - { - "module": "semantic.obligations.check", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "semantic.obligations", - "semantic.obligations.intent" - ] - }, - { - "module": "semantic.obligations.dominance", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "hunter_tools", - "pairs", - "semantic.obligations.check" - ] - }, - { - "module": "semantic.obligations.facts", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "model.specs", - "pairs", - "semantic.obligations", - "semantic.obligations.check", - "semantic.obligations.operations", - "semantic.obligations.policy", - "semantic.obligations.shapes", - "semantic.obligations.siblings" - ] - }, - { - "module": "semantic.obligations.intent", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "semantic.obligations.operations", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "model.candidates", - "semantic.obligations.check", - "semantic.obligations.dominance", - "semantic.obligations.siblings" - ] - }, - { - "module": "semantic.obligations.policy", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "semantic.obligations.check" - ] - }, - { - "module": "semantic.obligations.shapes", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "semantic.obligations", - "semantic.obligations.operations" - ] - }, - { - "module": "semantic.obligations.siblings", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "semantic.obligations.check" - ] - }, - { - "module": "semantic.overlay", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "semantic", - "semantic.prove_filter" - ] - }, - { - "module": "semantic.prove_filter", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "semantic" - ] - }, - { - "module": "semantic.taint", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "semantic.overlay" - ] - }, - { - "module": "skills", - "classification": "standalone_capability", - "reason": "skill router; consumed by mcp and by the OpenCode integration, not by the scan path", - "importers": [ - "hunter" - ] - }, - { - "module": "stages", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli" - ] - }, - { - "module": "tool_hunter", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "cli", - "model.endpoint", - "plane.budget", - "plane.tasks.roles", - "plane.tasks.verify" - ] - }, - { - "module": "verification", - "classification": "load_bearing", - "reason": "reachable from gate, map_gate, pair_gate, cli", - "importers": [ - "calibration", - "cli", - "fusion", - "reports" - ] - } - ] + { + "module": "cli", + "classification": "load_bearing", + "reason": "entry point", + "importers": [ + "mcp" + ] + }, + { + "module": "complexity", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "complexity.hints", + "complexity.ledger", + "complexity.map", + "map_gate", + "plane.tasks.verify", + "regress.candidate", + "semantic.prove_filter", + "tool_hunter" + ] + }, + { + "module": "complexity.hints", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "complexity", + "complexity.map" + ] + }, + { + "module": "complexity.ledger", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "complexity", + "complexity.map", + "regress.candidate" + ] + }, + { + "module": "complexity.map", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "complexity", + "complexity.hints", + "complexity.ledger", + "map_gate", + "plane.tasks.verify", + "regress.candidate", + "semantic.prove_filter", + "tool_hunter" + ] + }, + { + "module": "complexity.signals", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "complexity", + "complexity.hints", + "complexity.map" + ] + }, + { + "module": "config", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "harness", + "hunter_tools", + "learn.cli", + "model.endpoint", + "pairs", + "plane.memory", + "push.assistance", + "push.policy", + "push.runner", + "regress.candidate", + "regress.recipes", + "run" + ] + }, + { + "module": "contracts", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 7.3: strict shared manifest serialization, independent of push", + "importers": [ + "config", + "cpg.artifact", + "model.contracts", + "push.cache", + "push.contracts", + "push.policy", + "push.vetoes" + ] + }, + { + "module": "cpg", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 4: the CPG seam package", + "importers": [ + "cli", + "cpg.artifact", + "cpg.backend", + "model.config_value", + "model.dominance", + "model.partitions", + "model.pipeline", + "model.scan", + "model.taint", + "push.cache", + "push.eligibility", + "push.policy", + "push.reuse", + "push.runner" + ] + }, + { + "module": "cpg.artifact", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 4.3/7.3: backend graph identity and live lease contract; validation integration pending", + "importers": [ + "cpg.backend", + "push.cache", + "push.eligibility", + "push.policy", + "push.reuse", + "push.runner" + ] + }, + { + "module": "cpg.backend", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 4.1/4.5: the single Joern subprocess boundary", + "importers": [ + "cli", + "cpg.artifact", + "model.config_value", + "model.dominance", + "model.partitions", + "model.pipeline", + "model.scan", + "model.taint", + "push.cache", + "push.policy", + "push.reuse", + "push.runner" + ] + }, + { + "module": "cpg.capability", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 4.2/4.3: the engine probe that degrades to suspicion", + "importers": [ + "cpg.backend" + ] + }, + { + "module": "cpg.session", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cpg.backend" + ] + }, + { + "module": "findings", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "benchmark", + "calibration", + "cli", + "complexity.ledger", + "complexity.map", + "complexity.signals", + "fusion", + "gate", + "hunter", + "hunter_tools", + "improve.evolve", + "map_gate", + "model.ladder", + "model.report", + "pairs", + "plane.tasks.alerts", + "rank", + "regress.candidate", + "reports", + "scoring.project_score", + "semantic.obligations.check", + "semantic.overlay", + "semantic.prove_filter", + "tool_hunter", + "verification" + ] + }, + { + "module": "fusion", + "classification": "standalone_capability", + "reason": "two-panel adjudication engine; reached from the scan's report stage and used standalone", + "importers": [ + "cli" + ] + }, + { + "module": "gate", + "classification": "load_bearing", + "reason": "entry point", + "importers": [ + "cli", + "plane.tasks.loop" + ] + }, + { + "module": "harness", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "hunter", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "hunter_tools", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "tool_hunter" + ] + }, + { + "module": "improve", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "improve.evolve", + "improve.memory", + "plane.tasks.loop" + ] + }, + { + "module": "improve.evolve", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "improve", + "plane.tasks.loop" + ] + }, + { + "module": "improve.journal", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "improve", + "improve.evolve", + "improve.memory", + "plane.tasks.loop" + ] + }, + { + "module": "improve.memory", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "improve.evolve", + "plane.tasks.loop" + ] + }, + { + "module": "improve.validator", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "improve", + "improve.evolve", + "improve.memory", + "plane.tasks.loop" + ] + }, + { + "module": "index", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "skills" + ] + }, + { + "module": "learn", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "learn.calibrate", + "learn.cli", + "learn.compile", + "learn.embeddings", + "learn.evaluate", + "learn.examples", + "learn.excerpt", + "learn.experiments", + "learn.features", + "learn.folds", + "learn.labels", + "learn.leaks", + "learn.program", + "learn.retrieve", + "learn.roles", + "plane.harvest", + "plane.tasks.features", + "plane.tasks.remember", + "plane.tasks.roles" + ] + }, + { + "module": "learn.calibrate", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.evaluate" + ] + }, + { + "module": "learn.cli", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "learn.compile", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli", + "learn.evaluate", + "learn.experiments" + ] + }, + { + "module": "learn.embeddings", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli", + "learn.retrieve" + ] + }, + { + "module": "learn.evaluate", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli", + "learn.compile", + "learn.experiments" + ] + }, + { + "module": "learn.examples", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli", + "learn.compile", + "learn.evaluate", + "learn.experiments", + "learn.folds", + "learn.program", + "learn.retrieve" + ] + }, + { + "module": "learn.excerpt", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.embeddings", + "learn.examples", + "learn.labels", + "plane.harvest" + ] + }, + { + "module": "learn.experiments", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli" + ] + }, + { + "module": "learn.features", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.excerpt", + "plane.tasks.features" + ] + }, + { + "module": "learn.folds", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli", + "learn.compile", + "learn.evaluate", + "learn.experiments", + "learn.program", + "learn.retrieve" + ] + }, + { + "module": "learn.labels", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "learn.calibrate", + "learn.cli", + "learn.compile", + "learn.evaluate", + "learn.examples", + "learn.excerpt", + "learn.retrieve", + "plane.harvest" + ] + }, + { + "module": "learn.leaks", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli" + ] + }, + { + "module": "learn.program", + "classification": "load_bearing", + "reason": "learned-decision-engine Req 3.1/3.3: the AI classifier program (signature, Retrieve -> Classify(k), response cache); `ousast learn compile` (task 6.5) and the decide task (task 8) are its callers", + "importers": [ + "learn.cli", + "learn.compile", + "learn.evaluate", + "learn.experiments" + ] + }, + { + "module": "learn.retrieve", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.evaluate", + "learn.program" + ] + }, + { + "module": "learn.roles", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.features", + "plane.tasks.features", + "plane.tasks.roles" + ] + }, + { + "module": "learn.schema", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.compile", + "learn.evaluate", + "learn.examples", + "learn.features", + "learn.leaks", + "learn.program", + "learn.retrieve", + "learn.roles", + "plane.tasks.remember", + "plane.tasks.roles" + ] + }, + { + "module": "map_gate", + "classification": "load_bearing", + "reason": "entry point", + "importers": [] + }, + { + "module": "mapping", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "gate", + "hunter_tools", + "improve.evolve", + "learn.features", + "map_gate", + "model.scan", + "pairs", + "plane.tasks.alerts", + "preprocess", + "push.runner" + ] + }, + { + "module": "mcp", + "classification": "standalone_capability", + "reason": "narrow MCP server over stdio; its own `ousast mcp` entry point and OpenCode integration", + "importers": [ + "cli" + ] + }, + { + "module": "model", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "cpg.backend", + "cpg.session", + "learn.cli", + "learn.features", + "learn.labels", + "model.candidates", + "model.config_value", + "model.dominance", + "model.layout", + "model.partitions", + "model.pipeline", + "model.regions", + "model.report", + "model.scan", + "model.specs", + "model.taint", + "plane.budget", + "plane.generate", + "plane.tasks.features", + "plane.tasks.repo_facts", + "preprocess", + "push.assistance", + "push.cache", + "push.contracts", + "push.policy", + "push.report", + "push.reuse", + "push.runner", + "push.snapshot", + "push.vetoes", + "tool_hunter" + ] + }, + { + "module": "model.audit", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 3: this classifier; run by the maintainer, not by the scan", + "importers": [] + }, + { + "module": "model.candidates", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "model.scan" + ] + }, + { + "module": "model.config_value", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 6.3: constant abstraction for the configuration families", + "importers": [ + "model.pipeline", + "model.scan" + ] + }, + { + "module": "model.contracts", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 4.1/7.3: generic deadline, change and exact scope contracts; integration pending", + "importers": [ + "cli", + "cpg.backend", + "cpg.session", + "model.partitions", + "model.regions", + "model.scan", + "push.assistance", + "push.cache", + "push.contracts", + "push.policy", + "push.report", + "push.reuse", + "push.runner", + "push.snapshot", + "push.vetoes" + ] + }, + { + "module": "model.dominance", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 6.2: guard dominance, the arbiter for bugs that never crash", + "importers": [ + "model.pipeline", + "model.scan" + ] + }, + { + "module": "model.endpoint", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 8: the bounded model client the hunter, the plane and the CLI share", + "importers": [ + "cli", + "learn.cli", + "plane.budget", + "plane.generate", + "push.assistance", + "tool_hunter" + ] + }, + { + "module": "model.evidence", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "model.scan" + ] + }, + { + "module": "model.ladder", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 5: the evidence ladder every finding carries", + "importers": [ + "model.config_value", + "model.dominance", + "model.pipeline", + "model.scan", + "model.taint", + "push.policy" + ] + }, + { + "module": "model.layout", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "model.partitions", + "model.scan", + "plane.tasks.repo_facts", + "preprocess", + "push.runner" + ] + }, + { + "module": "model.partitions", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "model.scan" + ] + }, + { + "module": "model.pipeline", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 8: the enumerator proposes, the LLM answers, the model disposes", + "importers": [ + "model.scan", + "push.policy" + ] + }, + { + "module": "model.regions", + "classification": "load_bearing", + "reason": "contributor-scan Req 2: regions from entry points, with the families each admits", + "importers": [ + "cli", + "model.layout", + "model.scan", + "push.policy", + "push.reuse", + "push.runner" + ] + }, + { + "module": "model.report", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 9.3/11.1: per-slice reporting with the overfitting gap", + "importers": [ + "cli" + ] + }, + { + "module": "model.scan", + "classification": "load_bearing", + "reason": "contributor-scan Req 3: the repository driver -- one CPG, one budget, ranked spend", + "importers": [ + "cli", + "push.policy", + "push.report", + "push.runner", + "push.vetoes" + ] + }, + { + "module": "model.shipped", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "push.runner" + ] + }, + { + "module": "model.specs", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 7.2-7.4: the security vocabularies the CPG queries are parameterised by", + "importers": [ + "model.config_value", + "model.dominance", + "model.pipeline", + "model.regions", + "model.report", + "model.scan", + "model.taint", + "push.policy" + ] + }, + { + "module": "model.taint", + "classification": "load_bearing", + "reason": "model-grounded-detection Req 6.1: taint reachability over the CPG, the flow-family arbiter", + "importers": [ + "model.pipeline", + "model.scan" + ] + }, + { + "module": "model.taxonomy", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "learn.features", + "learn.labels", + "model.candidates", + "model.report", + "model.scan", + "model.specs", + "plane.tasks.features" + ] + }, + { + "module": "pair_gate", + "classification": "load_bearing", + "reason": "entry point", + "importers": [ + "cli" + ] + }, + { + "module": "pairs", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "improve", + "improve.evolve", + "improve.memory", + "learn.examples", + "learn.labels", + "model.candidates", + "pair_gate", + "plane.harvest", + "plane.tasks.loop" + ] + }, + { + "module": "plane", + "classification": "load_bearing", + "reason": "ai-service-plane Req 1-4: the service plane package; runner and reconciler integration in tasks 3-4", + "importers": [ + "cli", + "improve.memory", + "learn.cli", + "learn.compile", + "learn.embeddings", + "learn.examples", + "learn.experiments", + "learn.program", + "plane.egress", + "plane.engine_alerts", + "plane.generate", + "plane.harvest", + "plane.memory", + "plane.reconciler", + "plane.router", + "plane.runner", + "plane.tasks.loop", + "plane.tasks.remember", + "plane.tasks.roles", + "plane.tasks.verify", + "plane.workspaces" + ] + }, + { + "module": "plane.budget", + "classification": "load_bearing", + "reason": "ai-service-plane Req 2.3/2.4: metered chat client with usd and calls ceilings; task integration in tasks 3, 6", + "importers": [ + "learn.cli", + "learn.compile", + "learn.embeddings", + "learn.experiments", + "learn.program", + "plane.generate", + "plane.tasks.roles", + "plane.tasks.verify" + ] + }, + { + "module": "plane.doctor", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "plane.reconciler" + ] + }, + { + "module": "plane.egress", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "plane.reconciler" + ] + }, + { + "module": "plane.engine_alerts", + "classification": "load_bearing", + "reason": "harnessx-removal Req 6.3: engine alerts where quick mode has no rules (PHP), on the host", + "importers": [ + "cli" + ] + }, + { + "module": "plane.generate", + "classification": "load_bearing", + "reason": "ai-service-plane task 7: an increment's Workspaces, per-case Tasks and Run from a recorded set", + "importers": [ + "cli", + "plane.harvest" + ] + }, + { + "module": "plane.harvest", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "plane.manifests", + "classification": "load_bearing", + "reason": "ai-service-plane Req 1.1-1.4: ax-shaped Task/Workspace/Model and the Run kind, validated before anything runs", + "importers": [ + "learn.cli", + "plane.egress", + "plane.generate", + "plane.harvest", + "plane.reconciler", + "plane.router", + "plane.runner", + "plane.workspaces" + ] + }, + { + "module": "plane.memory", + "classification": "load_bearing", + "reason": "harnessx-removal Req 6.1/6.4: the memory store (file, S3), ingest and fact reuse; `ousast plane remember`", + "importers": [ + "cli", + "improve.memory", + "learn.cli", + "learn.compile", + "learn.embeddings", + "learn.examples", + "learn.experiments", + "learn.program", + "plane.generate", + "plane.tasks.loop", + "plane.tasks.remember" + ] + }, + { + "module": "plane.profile", + "classification": "load_bearing", + "reason": "python -m openultrasast.plane.profile", + "importers": [] + }, + { + "module": "plane.reconciler", + "classification": "load_bearing", + "reason": "ai-service-plane Req 3.1-3.5, 7.3: ax-backed Run execution, status attribution and doctor; `ousast plane`", + "importers": [ + "cli", + "plane.engine_alerts", + "plane.generate", + "plane.harvest", + "plane.memory", + "plane.workspaces" + ] + }, + { + "module": "plane.router", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "plane.reconciler", + "plane.runner" + ] + }, + { + "module": "plane.runner", + "classification": "load_bearing", + "reason": "ai-service-plane Req 4.1-4.4: the ax-task-runner entrypoint (PID 1 of the task image); delivers the output directory", + "importers": [] + }, + { + "module": "plane.tasks", + "classification": "load_bearing", + "reason": "ai-service-plane Req 4.1: the task entrypoint package the runner imports by name; runner integration pending (task 3)", + "importers": [ + "cli", + "learn.excerpt", + "learn.features", + "learn.labels", + "learn.roles", + "plane.engine_alerts", + "plane.generate", + "plane.harvest", + "plane.memory", + "plane.tasks.agree", + "plane.tasks.alerts", + "plane.tasks.features", + "plane.tasks.remember", + "plane.tasks.roles" + ] + }, + { + "module": "plane.tasks.agree", + "classification": "load_bearing", + "reason": "ai-service-plane Req 5.3/2.1: agreement across two verify passes with per-candidate cost, turns and site match", + "importers": [] + }, + { + "module": "plane.tasks.alerts", + "classification": "load_bearing", + "reason": "harnessx-removal Req 6.3: quick-mode alerts on a case's vulnerable and fixed pins, model-free", + "importers": [ + "learn.features", + "plane.engine_alerts", + "plane.tasks.features" + ] + }, + { + "module": "plane.tasks.features", + "classification": "load_bearing", + "reason": "learned-decision-engine Req 1.1-1.3/7.2: one allow-listed feature record per candidate, model-free", + "importers": [] + }, + { + "module": "plane.tasks.loop", + "classification": "load_bearing", + "reason": "harnessx-removal Req 6.3: the improvement loop's snapshot, measure, propose and improve steps", + "importers": [ + "plane.memory" + ] + }, + { + "module": "plane.tasks.remember", + "classification": "load_bearing", + "reason": "harnessx-removal Req 6.1: a case's delivered artifacts as memory rows, model-free", + "importers": [ + "cli" + ] + }, + { + "module": "plane.tasks.repo_facts", + "classification": "load_bearing", + "reason": "ai-service-plane Req 5.1/5.2: source-only functions and callers per file; runner integration pending", + "importers": [ + "learn.excerpt", + "learn.features", + "learn.labels", + "learn.roles", + "plane.generate", + "plane.harvest", + "plane.tasks.alerts", + "plane.tasks.features", + "plane.tasks.remember", + "plane.tasks.roles" + ] + }, + { + "module": "plane.tasks.roles", + "classification": "load_bearing", + "reason": "learned-decision-engine Req 8.1: model role classification per file chunk (model_sinks.py moved in); its Run template comes with the harvest (task 5)", + "importers": [ + "plane.harvest" + ] + }, + { + "module": "plane.tasks.verify", + "classification": "load_bearing", + "reason": "ai-service-plane Req 5.3/2.1-2.3/7.2: the batched hunt with known callers, metered and resumable per file hunt", + "importers": [ + "plane.generate", + "plane.harvest", + "plane.tasks.agree", + "plane.tasks.roles" + ] + }, + { + "module": "plane.workspaces", + "classification": "load_bearing", + "reason": "ai-service-plane task 4: Workspace manifests per case pin of a population; `ousast plane workspaces`", + "importers": [ + "cli" + ] + }, + { + "module": "policy", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "findings", + "hunter", + "improve.evolve", + "improve.validator", + "plane.tasks.loop", + "scoring.project_score", + "semantic.overlay" + ] + }, + { + "module": "policy.verycode", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "policy" + ] + }, + { + "module": "preprocess", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "complexity.hints", + "complexity.map", + "complexity.signals", + "findings", + "gate", + "hunter", + "hunter_tools", + "improve.evolve", + "index", + "learn.features", + "learn.labels", + "learn.roles", + "map_gate", + "mapping", + "model.candidates", + "model.partitions", + "model.scan", + "pairs", + "plane.harvest", + "plane.tasks.alerts", + "plane.tasks.features", + "plane.tasks.repo_facts", + "push.runner", + "rank", + "regress.candidate", + "semantic.overlay", + "skills" + ] + }, + { + "module": "profiling", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "provenance", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "provider", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli", + "learn.compile", + "learn.program", + "model.endpoint", + "tool_hunter" + ] + }, + { + "module": "provider.openrouter", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.cli", + "learn.compile", + "learn.program", + "model.endpoint", + "tool_hunter" + ] + }, + { + "module": "push", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 5.5/7.3: experimental push contract package; no hook installed", + "importers": [ + "cli", + "push.assistance", + "push.eligibility", + "push.policy", + "push.report", + "push.reuse", + "push.runner", + "push.snapshot", + "push.vetoes" + ] + }, + { + "module": "push.assistance", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "push.runner" + ] + }, + { + "module": "push.cache", + "classification": "load_bearing", + "reason": "pre-push-safety-net task 6.2: bounded artifact publication and leased eviction; runner integration in 6.4", + "importers": [ + "push.policy", + "push.reuse", + "push.runner" + ] + }, + { + "module": "push.contracts", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 5.5/7.3: immutable comparisons and independent result statuses; runner pending", + "importers": [ + "push.policy", + "push.report", + "push.reuse", + "push.runner", + "push.snapshot", + "push.vetoes" + ] + }, + { + "module": "push.eligibility", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "push.runner" + ] + }, + { + "module": "push.policy", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 2.4/3.1/3.4/4.1: targeted same-semantics base comparison; replay integration pending", + "importers": [ + "push.eligibility", + "push.report", + "push.runner", + "push.vetoes" + ] + }, + { + "module": "push.report", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 5.1/5.2/5.5/7.3: compact output and complete artifacts; runner integration pending", + "importers": [ + "push.assistance", + "push.runner" + ] + }, + { + "module": "push.reuse", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "push.runner" + ] + }, + { + "module": "push.runner", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "push.assistance" + ] + }, + { + "module": "push.snapshot", + "classification": "load_bearing", + "reason": "pre-push-safety-net Req 1.1/1.3/1.4/7.1: local immutable ref resolution; runner integration pending", + "importers": [ + "push.runner" + ] + }, + { + "module": "push.vetoes", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "push.runner" + ] + }, + { + "module": "push_inputs", + "classification": "standalone_capability", + "reason": "pre-push-safety-net task 1.2: offline input provenance validator; python -m openultrasast.push_inputs", + "importers": [] + }, + { + "module": "push_scoring", + "classification": "standalone_capability", + "reason": "pre-push-safety-net task 1.3: frozen-profile diagnostic outcome scorer; python -m openultrasast.push_scoring", + "importers": [ + "push.eligibility" + ] + }, + { + "module": "rank", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "calibration", + "cli", + "findings", + "gate", + "hunter", + "hunter_tools", + "improve.evolve", + "map_gate", + "pairs", + "plane.tasks.alerts" + ] + }, + { + "module": "redaction", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "harness", + "model.pipeline", + "push.assistance", + "push.report", + "reports", + "semantic.obligations.intent" + ] + }, + { + "module": "regress", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "regress.candidate" + ] + }, + { + "module": "regress.candidate", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "regress" + ] + }, + { + "module": "regress.recipes", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "regress", + "regress.candidate" + ] + }, + { + "module": "regress.safety", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "regress", + "regress.candidate" + ] + }, + { + "module": "regress.verdict", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "regress", + "regress.candidate" + ] + }, + { + "module": "reports", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "repos", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "push_inputs" + ] + }, + { + "module": "ruleset", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "findings", + "hunter", + "improve.evolve", + "improve.memory", + "improve.validator", + "learn.features", + "learn.labels", + "learn.roles", + "pairs", + "plane.tasks.alerts", + "plane.tasks.features", + "plane.tasks.loop", + "ruleset.store", + "semantic.facts" + ] + }, + { + "module": "ruleset.frameworks", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "learn.labels", + "learn.roles", + "ruleset.store", + "semantic.facts" + ] + }, + { + "module": "ruleset.store", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "improve.validator", + "ruleset" + ] + }, + { + "module": "run", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "reports" + ] + }, + { + "module": "sandbox", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "regress.candidate", + "regress.recipes", + "regress.safety", + "regress.verdict" + ] + }, + { + "module": "sandbox.probe", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "sandbox" + ] + }, + { + "module": "sandbox.runner", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "regress.candidate", + "regress.recipes", + "regress.safety", + "sandbox" + ] + }, + { + "module": "scoring", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "improve.evolve" + ] + }, + { + "module": "scoring.project_score", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "scoring" + ] + }, + { + "module": "semantic", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "cpg.capability", + "hunter_tools", + "learn.features", + "learn.roles", + "mapping", + "model.candidates", + "model.layout", + "model.scan", + "model.specs", + "pairs", + "semantic.cst", + "semantic.engines", + "semantic.functions", + "semantic.ir", + "semantic.obligations.check", + "semantic.obligations.facts", + "semantic.obligations.operations", + "semantic.obligations.shapes", + "semantic.overlay", + "semantic.prove_filter", + "semantic.taint" + ] + }, + { + "module": "semantic.cst", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "semantic.ir" + ] + }, + { + "module": "semantic.engines", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cpg.capability", + "semantic", + "semantic.overlay" + ] + }, + { + "module": "semantic.extra", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "semantic", + "semantic.cst", + "semantic.engines" + ] + }, + { + "module": "semantic.facts", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "hunter_tools", + "learn.features", + "learn.roles", + "mapping", + "model.candidates", + "model.layout", + "model.scan", + "model.specs", + "pairs", + "semantic", + "semantic.obligations.check", + "semantic.obligations.facts", + "semantic.obligations.operations", + "semantic.obligations.shapes", + "semantic.overlay", + "semantic.taint" + ] + }, + { + "module": "semantic.functions", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "hunter_tools", + "pairs", + "semantic.obligations.shapes", + "semantic.overlay" + ] + }, + { + "module": "semantic.ir", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "hunter_tools", + "model.candidates", + "pairs", + "semantic.cst", + "semantic.functions", + "semantic.obligations.check", + "semantic.obligations.operations", + "semantic.obligations.shapes", + "semantic.overlay", + "semantic.taint" + ] + }, + { + "module": "semantic.obligations", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "hunter_tools", + "model.candidates", + "model.specs", + "pairs", + "semantic.obligations.check", + "semantic.obligations.dominance", + "semantic.obligations.intent", + "semantic.obligations.operations", + "semantic.obligations.policy", + "semantic.obligations.shapes", + "semantic.obligations.siblings" + ] + }, + { + "module": "semantic.obligations.check", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "semantic.obligations", + "semantic.obligations.intent" + ] + }, + { + "module": "semantic.obligations.dominance", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "hunter_tools", + "pairs", + "semantic.obligations.check" + ] + }, + { + "module": "semantic.obligations.facts", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "model.specs", + "pairs", + "semantic.obligations", + "semantic.obligations.check", + "semantic.obligations.operations", + "semantic.obligations.policy", + "semantic.obligations.shapes", + "semantic.obligations.siblings" + ] + }, + { + "module": "semantic.obligations.intent", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "semantic.obligations.operations", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "model.candidates", + "semantic.obligations.check", + "semantic.obligations.dominance", + "semantic.obligations.siblings" + ] + }, + { + "module": "semantic.obligations.policy", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "semantic.obligations.check" + ] + }, + { + "module": "semantic.obligations.shapes", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "semantic.obligations", + "semantic.obligations.operations" + ] + }, + { + "module": "semantic.obligations.siblings", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "semantic.obligations.check" + ] + }, + { + "module": "semantic.overlay", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "semantic", + "semantic.prove_filter" + ] + }, + { + "module": "semantic.prove_filter", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "semantic" + ] + }, + { + "module": "semantic.taint", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "semantic.overlay" + ] + }, + { + "module": "skills", + "classification": "standalone_capability", + "reason": "skill router; consumed by mcp and by the OpenCode integration, not by the scan path", + "importers": [ + "hunter" + ] + }, + { + "module": "stages", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli" + ] + }, + { + "module": "tool_hunter", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "cli", + "model.endpoint", + "plane.budget", + "plane.tasks.roles", + "plane.tasks.verify" + ] + }, + { + "module": "verification", + "classification": "load_bearing", + "reason": "reachable from gate, map_gate, pair_gate, cli", + "importers": [ + "calibration", + "cli", + "fusion", + "reports" + ] + } + ] } diff --git a/ops/k8s/profiles/k3s.toml b/ops/k8s/profiles/k3s.toml new file mode 100644 index 0000000..d1fa987 --- /dev/null +++ b/ops/k8s/profiles/k3s.toml @@ -0,0 +1,26 @@ +# The k3s profile: the plane on the maintainer's production cluster (plane-on-kubernetes design section 1; +# requirements "Target Environment"). Documented values, not secrets: the kubeconfig for the context, the GHCR +# pull secret's content and the store's keys live outside this file. Every field is overridable by OUSAST_, +# a pool by OUSAST_POOL__. Nothing has been applied to a k3s cluster yet; docs/deployment.md says +# which items are planned. + +exec = "remote" # the same CLI from a laptop or a CI job, through the cluster's kube context +kube_context = "ousast-k3s" # the maintainer's context name for the k3s cluster +registry = "ghcr.io/norandom" # images built by GitHub Actions from tagged commits (design section 8) +images = "images.json" # the release asset {"runner": "ghcr.io/norandom/ousast-runner@sha256:...", "engine": ...} +router_url = "" # empty: port-forward to atenet-router through the kube context (ax-tunnel) +ax_server = "" # empty: the ax CLI's own tunnel through the kube context (ax-tunnel) +memory = "s3://sast-memory" # the memory store; S3_ENDPOINT and the keys come from the CI secrets or .env +atespace = "default" +snapshots_bucket = "s3://ax-snapshots" # ax's AX_SNAPSHOTS_BUCKET, ours (rendered into the ax-server patch) +image_pull_secret = "ghcr-pull" # only needed while the GHCR packages are private + +[pools.default] # production sizing: four verify-class workers; the HPA (design section 5) scales it +replicas = 4 +cpu = "1" +memory = "1536Mi" + +[pools.engine] # two engine workers sized from the measured 2.1 GiB peak under a 3 GiB limit +replicas = 2 +cpu = "2" +memory = "4Gi" diff --git a/ops/k8s/profiles/kind-images.json b/ops/k8s/profiles/kind-images.json new file mode 100644 index 0000000..16e396f --- /dev/null +++ b/ops/k8s/profiles/kind-images.json @@ -0,0 +1,3 @@ +{ + "runner": "localhost:5001/ousast-runner@sha256:412db0a1504af8f1b23a395af8d1dcbd152c1bb6d16594f2b733067163a31884" +} diff --git a/ops/k8s/profiles/kind.toml b/ops/k8s/profiles/kind.toml new file mode 100644 index 0000000..e383696 --- /dev/null +++ b/ops/k8s/profiles/kind.toml @@ -0,0 +1,25 @@ +# The kind profile: the plane on this laptop's single-node kind cluster, which ops/ax/up.sh creates and which is +# the development default (plane-on-kubernetes design section 1). Values only; a credential never goes here +# (keys stay in the environment or .env). Every field is overridable by OUSAST_ in the environment, a pool +# by OUSAST_POOL__. This file may name the kind cluster; the code must not (tests/test_plane_literals.py). + +exec = "local" # the reconciler runs on this host against the cluster below +kube_context = "kind-ousast" # kind's context for KIND_CLUSTER_NAME=ousast +registry = "localhost:5001" # the kind-local registry up.sh creates (Substrate rewrites it for the node) +images = "kind-images.json" # digest-pinned image references, written by up.sh after each build +router_url = "" # empty: port-forward to atenet-router through the kube context (ax-tunnel) +ax_server = "" # empty: the ax CLI's own tunnel through the kube context (ax-tunnel) +memory = "s3://sast-memory" # the memory store; S3_ENDPOINT and the keys come from .env +atespace = "default" +snapshots_bucket = "s3://ax-snapshots" # ax's AX_SNAPSHOTS_BUCKET, ours (rendered into the ax-server patch) +image_pull_secret = "" # the kind registry needs none + +[pools.default] # ousast-pool: two gVisor workers fit the 7 GB host beside the control planes +replicas = 2 +cpu = "1" +memory = "1536Mi" + +[pools.engine] # engine-pool (design section 4): one worker for the Joern engine task +replicas = 1 +cpu = "2" +memory = "4Gi" diff --git a/src/openultrasast/plane/profile.py b/src/openultrasast/plane/profile.py new file mode 100644 index 0000000..c208591 --- /dev/null +++ b/src/openultrasast/plane/profile.py @@ -0,0 +1,266 @@ +"""The plane's configuration surface: one ``PlaneProfile`` per cluster (plane-on-kubernetes design section 1). + +A profile is a TOML file under ``ops/k8s/profiles/`` (``kind.toml`` for the laptop's kind cluster, ``k3s.toml`` for +the production cluster) holding every value that differs between clusters: the execution mode, the kube context, +the registry and the digest-pinned images file, the router and ax-server addresses (empty: tunnel through the +Kubernetes API), the memory store, the atespace, ax's snapshot bucket, the worker pools and the image pull secret. +:func:`load_profile` reads the file ``OUSAST_PLANE_PROFILE`` names (a profile name or a path; default ``kind``) and +then applies the environment: every scalar field is overridable by ``OUSAST_`` (``OUSAST_KUBE_CONTEXT``, +``OUSAST_REGISTRY``, ``OUSAST_MEMORY``, ``OUSAST_ROUTER_URL``, ...), a pool by ``OUSAST_POOL__``. + +A profile holds no secret: a key whose name ends in ``key``, ``secret``, ``token`` or ``password`` is refused, and so +is any unknown key, an ``exec`` outside ``local``/``remote``, and an images file whose references are not pinned by +digest (Agent Substrate rejects tags). Provider and store credentials stay in the environment or ``.env``. + +``python -m openultrasast.plane.profile --print FIELD`` prints one value for shell scripts (``ops/ax/up.sh``): +``registry``, ``kube_context``, ``images.runner``, ``pools.default.replicas``, ... +""" + +from __future__ import annotations + +import json +import os +import re +import sys +import tomllib +from collections.abc import Mapping +from dataclasses import dataclass, field, fields +from pathlib import Path +from typing import Any + +__all__ = ["DEFAULT_PROFILE", "PROFILES_DIR", "PlaneProfile", "Pool", "ProfileError", "load_images", "load_profile", "profiles_dir"] + +DEFAULT_PROFILE = "kind" +PROFILES_DIR = Path("ops/k8s/profiles") +EXEC_MODES = ("local", "remote") +_DIGEST = re.compile(r"\S+@sha256:[0-9a-f]{64}") +_SECRET_KEY = re.compile(r"(key|secret|token|password)$", re.IGNORECASE) +_SECRET_VALUE = re.compile(r"(sk-[A-Za-z0-9]{8,}|AKIA[0-9A-Z]{12,}|[A-Za-z0-9+/]{40,}={0,2}$)") +_ENV_PREFIX = "OUSAST_" + + +class ProfileError(ValueError): + """A profile that cannot be used; the message names the file and the field.""" + + +@dataclass(frozen=True) +class Pool: + """One WorkerPool's size: how many warm workers, and each worker's CPU and memory limit.""" + + replicas: int + cpu: str + memory: str + + +@dataclass(frozen=True) +class PlaneProfile: + """The values of one cluster. ``images`` is resolved against the profile file's directory.""" + + name: str + path: Path + exec: str = "local" + kube_context: str = "" + registry: str = "" + images: Path = Path("images.json") + router_url: str = "" + ax_server: str = "" + memory: str = "" + atespace: str = "default" + snapshots_bucket: str = "" + image_pull_secret: str = "" + pools: Mapping[str, Pool] = field(default_factory=dict) + + def load_images(self) -> dict[str, str]: + """The digest-pinned image references of ``images`` (``{"runner": ..., "engine": ...}``).""" + return load_images(self.images) + + def addresses(self) -> dict[str, str]: + """Every configured address, for ``ousast plane doctor`` (Req 1.2); an empty router or ax-server address + means the Kubernetes API tunnel.""" + return { + "exec": self.exec, + "kube_context": self.kube_context, + "registry": self.registry, + "images": str(self.images), + "router_url": self.router_url or "(tunnel through the kube context)", + "ax_server": self.ax_server or "(tunnel through the kube context)", + "memory": self.memory, + "atespace": self.atespace, + "snapshots_bucket": self.snapshots_bucket, + "image_pull_secret": self.image_pull_secret or "(none)", + } + + +_SCALARS = {f.name: str(f.type) for f in fields(PlaneProfile) if f.name not in ("name", "path", "pools")} +_POOL_FIELDS = {f.name: str(f.type) for f in fields(Pool)} + + +def profiles_dir() -> Path: + """``ops/k8s/profiles`` of this checkout (``OUSAST_PLANE_PROFILES`` overrides; an installed package falls back + to the working directory).""" + override = os.environ.get("OUSAST_PLANE_PROFILES") + if override: + return Path(override) + checkout = Path(__file__).resolve().parents[3] / PROFILES_DIR + return checkout if checkout.is_dir() else PROFILES_DIR + + +def load_images(path: Path) -> dict[str, str]: + """``{"runner": "/@sha256:<64 hex>", ...}`` from a JSON images file; every value must be + digest-pinned. A one-line file holding a single reference (``ops/ax/up.sh`` before this profile) reads as + ``{"runner": }``.""" + try: + text = path.read_text(encoding="utf-8").strip() + except OSError as exc: + raise ProfileError(f"images file {path}: {exc.strerror or exc}") from None + try: + loaded: Any = json.loads(text) if text.startswith("{") else {"runner": text} + except ValueError as exc: + raise ProfileError(f"images file {path}: not JSON ({exc})") from None + if not isinstance(loaded, dict) or not loaded or not all(isinstance(k, str) and isinstance(v, str) for k, v in loaded.items()): + raise ProfileError(f"images file {path}: expected an object of image names to references") + for name, reference in loaded.items(): + if not _DIGEST.fullmatch(reference): + raise ProfileError(f"images file {path}: {name} is not digest-pinned ({reference!r}); Agent Substrate rejects tags") + return dict(loaded) + + +def _reject_secret(where: str, key: str, value: object, declared: bool = False) -> None: + """A key that is not a declared field and is named like a credential, or any value shaped like one, is refused. + ``image_pull_secret`` is declared: its value is the *name* of a Kubernetes Secret, never its content.""" + if not declared and _SECRET_KEY.search(key): + raise ProfileError(f"{where}: {key} looks like a credential; profiles hold no secret (use the environment or .env)") + if isinstance(value, str) and _SECRET_VALUE.search(value): + raise ProfileError(f"{where}: the value of {key} looks like a credential; profiles hold no secret") + + +def _coerce(where: str, key: str, value: object, kind: str) -> Any: + if kind == "int": + if isinstance(value, bool) or not isinstance(value, int): + raise ProfileError(f"{where}: {key} must be an integer, got {value!r}") + return value + if kind == "Path": + if not isinstance(value, str) or not value: + raise ProfileError(f"{where}: {key} must be a file name") + return Path(value) + if not isinstance(value, str): + raise ProfileError(f"{where}: {key} must be a string, got {value!r}") + return value + + +def _pools(where: str, raw: object) -> dict[str, Pool]: + if not isinstance(raw, dict): + raise ProfileError(f"{where}: pools must be a table of pool names") + out: dict[str, Pool] = {} + for name, spec in raw.items(): + if not isinstance(spec, dict): + raise ProfileError(f"{where}: pools.{name} must be a table") + unknown = sorted(set(spec) - set(_POOL_FIELDS)) + if unknown: + raise ProfileError(f"{where}: pools.{name} has unknown keys: {', '.join(unknown)}") + missing = sorted(set(_POOL_FIELDS) - set(spec)) + if missing: + raise ProfileError(f"{where}: pools.{name} lacks {', '.join(missing)}") + for key, value in spec.items(): + _reject_secret(where, f"pools.{name}.{key}", value, declared=True) + out[str(name)] = Pool(**{k: _coerce(where, f"pools.{name}.{k}", spec[k], _POOL_FIELDS[k]) for k in _POOL_FIELDS}) + return out + + +def _env_overrides(environ: Mapping[str, str], where: str, values: dict[str, Any], pools: dict[str, Pool]) -> None: + for name, kind in _SCALARS.items(): + raw = environ.get(_ENV_PREFIX + name.upper()) + if raw is not None and raw != "": + value: Any = raw + if kind == "int": + try: + value = int(raw) + except ValueError: + raise ProfileError(f"{where}: {_ENV_PREFIX}{name.upper()} must be an integer, got {raw!r}") from None + values[name] = _coerce(where, _ENV_PREFIX + name.upper(), value, kind) + pattern = re.compile(rf"^{_ENV_PREFIX}POOL_([A-Z0-9]+)_(REPLICAS|CPU|MEMORY)$") + for variable, raw in environ.items(): + found = pattern.match(variable) + if not found or raw == "": + continue + pool_name, key = found.group(1).lower(), found.group(2).lower() + current = pools.get(pool_name) + if current is None: + raise ProfileError(f"{where}: {variable} names a pool the profile does not declare ({pool_name})") + value = int(raw) if key == "replicas" and raw.isdigit() else raw + pools[pool_name] = Pool(**{**vars(current), key: _coerce(where, variable, value, _POOL_FIELDS[key])}) + + +def load_profile(name: str | None = None, environ: Mapping[str, str] | None = None) -> PlaneProfile: + """The profile ``name`` (a name under :func:`profiles_dir` or a path to a ``.toml``; default + ``OUSAST_PLANE_PROFILE``, else ``kind``), with the environment's overrides applied; :class:`ProfileError` + names what is wrong.""" + env = os.environ if environ is None else environ + chosen = name or env.get("OUSAST_PLANE_PROFILE") or DEFAULT_PROFILE + path = Path(chosen) if chosen.endswith(".toml") or "/" in chosen else profiles_dir() / f"{chosen}.toml" + where = str(path) + try: + raw = tomllib.loads(path.read_text(encoding="utf-8")) + except OSError as exc: + raise ProfileError(f"profile {where}: {exc.strerror or exc}") from None + except tomllib.TOMLDecodeError as exc: + raise ProfileError(f"profile {where}: {exc}") from None + for key, value in raw.items(): # a credential-shaped key is named as such before it is merely unknown + _reject_secret(where, key, value, declared=key in _SCALARS) + unknown = sorted(set(raw) - set(_SCALARS) - {"pools"}) + if unknown: + raise ProfileError(f"{where}: unknown keys: {', '.join(unknown)}") + values: dict[str, Any] = {} + for key, value in raw.items(): + if key != "pools": + values[key] = _coerce(where, key, value, _SCALARS[key]) + pools = _pools(where, raw.get("pools", {})) + _env_overrides(env, where, values, pools) + if values.get("exec", "local") not in EXEC_MODES: + raise ProfileError(f"{where}: exec must be one of {', '.join(EXEC_MODES)}, got {values.get('exec')!r}") + images = Path(values.get("images", "images.json")) + if not images.is_absolute(): + images = path.resolve().parent / images + values["images"] = images + if images.is_file(): + load_images(images) # a committed file must be pinned; a file the release has not published yet is doctor's finding + return PlaneProfile(name=path.stem, path=path, pools=pools, **values) + + +def _lookup(profile: PlaneProfile, dotted: str) -> str: + head, _, rest = dotted.partition(".") + if head == "images": + images = profile.load_images() + if rest not in images: + raise ProfileError(f"{profile.images}: no image named {rest!r} (have {', '.join(sorted(images))})") + return images[rest] + if head == "pools": + pool_name, _, key = rest.partition(".") + if pool_name not in profile.pools or key not in _POOL_FIELDS: + raise ProfileError(f"{profile.path}: no such pool field {dotted!r}") + return str(getattr(profile.pools[pool_name], key)) + if head not in _SCALARS or rest: + raise ProfileError(f"{profile.path}: no such field {dotted!r}") + return str(getattr(profile, head)) + + +def main(argv: list[str] | None = None) -> int: + """``python -m openultrasast.plane.profile [--profile NAME] --print FIELD [FIELD...]``: one value per line.""" + import argparse + + parser = argparse.ArgumentParser(prog="openultrasast.plane.profile", description=main.__doc__) + parser.add_argument("--profile", help="profile name or path (default OUSAST_PLANE_PROFILE, else kind)") + parser.add_argument("--print", dest="fields", nargs="+", required=True, metavar="FIELD") + args = parser.parse_args(argv) + try: + profile = load_profile(args.profile) + for dotted in args.fields: + print(_lookup(profile, dotted)) + except ProfileError as exc: + print(f"profile: {exc}", file=sys.stderr) + return 2 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/test_plane_profile.py b/tests/test_plane_profile.py new file mode 100644 index 0000000..f592203 --- /dev/null +++ b/tests/test_plane_profile.py @@ -0,0 +1,156 @@ +"""plane-on-kubernetes task 1.1: the ``PlaneProfile`` (Req 1.1). Defaults, file-then-environment precedence, every +rejection naming its field, both committed profiles loading, and no committed value that looks like a credential.""" + +from __future__ import annotations + +import json +import re +from pathlib import Path + +import pytest + +from openultrasast.plane import profile as profile_module +from openultrasast.plane.profile import PlaneProfile, Pool, ProfileError, load_images, load_profile, profiles_dir + +ROOT = Path(__file__).resolve().parents[1] +DIGEST = "sha256:" + "ab" * 32 +MINIMAL = 'exec = "local"\nkube_context = "kind-test"\nregistry = "registry.test:5000"\nimages = "images.json"\nmemory = "s3://m"\n' + + +def write_profile(tmp_path: Path, text: str = MINIMAL, name: str = "test") -> Path: + path = tmp_path / f"{name}.toml" + path.write_text(text, encoding="utf-8") + (tmp_path / "images.json").write_text(json.dumps({"runner": f"registry.test:5000/ousast-runner@{DIGEST}"}), encoding="utf-8") + return path + + +def test_defaults_and_the_images_path_resolve_against_the_profile_file(tmp_path: Path) -> None: + loaded = load_profile(str(write_profile(tmp_path)), environ={}) + assert loaded.name == "test" and loaded.exec == "local" and loaded.kube_context == "kind-test" + assert loaded.images == tmp_path / "images.json" and loaded.load_images() == {"runner": f"registry.test:5000/ousast-runner@{DIGEST}"} + assert loaded.atespace == "default" and loaded.router_url == "" and loaded.pools == {} + assert "(tunnel through the kube context)" in loaded.addresses()["router_url"] + + +def test_the_environment_overrides_the_file_and_names_the_profile(tmp_path: Path) -> None: + path = write_profile(tmp_path, MINIMAL + '[pools.default]\nreplicas = 2\ncpu = "1"\nmemory = "1536Mi"\n') + env = { + "OUSAST_PLANE_PROFILE": str(path), + "OUSAST_KUBE_CONTEXT": "other-cluster", + "OUSAST_MEMORY": "s3://elsewhere/prefix", + "OUSAST_ROUTER_URL": "https://router.test", + "OUSAST_POOL_DEFAULT_REPLICAS": "5", + "OUSAST_POOL_DEFAULT_MEMORY": "2Gi", + "OUSAST_EXEC": "", # an empty variable is not an override + } + loaded = load_profile(environ=env) + assert (loaded.kube_context, loaded.memory, loaded.router_url, loaded.exec) == ( + "other-cluster", + "s3://elsewhere/prefix", + "https://router.test", + "local", + ) + assert loaded.pools == {"default": Pool(replicas=5, cpu="1", memory="2Gi")} + assert load_profile(str(path), environ={}).kube_context == "kind-test", "the file is unchanged" + + +@pytest.mark.parametrize( + ("text", "field"), + [ + (MINIMAL + 'colour = "blue"\n', "unknown keys: colour"), + (MINIMAL + 'aws_secret_key = "x"\n', "aws_secret_key looks like a credential"), + (MINIMAL + 'api_token = "x"\n', "api_token looks like a credential"), + (MINIMAL.replace('exec = "local"', 'exec = "cluster"'), "exec must be one of local, remote"), + (MINIMAL + '[pools.default]\nreplicas = "two"\ncpu = "1"\nmemory = "1Gi"\n', "pools.default.replicas must be an integer"), + (MINIMAL + '[pools.default]\nreplicas = 1\ncpu = "1"\n', "pools.default lacks memory"), + (MINIMAL + '[pools.default]\nreplicas = 1\ncpu = "1"\nmemory = "1Gi"\nnodes = 3\n', "pools.default has unknown keys: nodes"), + (MINIMAL.replace('kube_context = "kind-test"', "kube_context = 3"), "kube_context must be a string"), + ], +) +def test_each_rejection_names_the_field(tmp_path: Path, text: str, field: str) -> None: + path = write_profile(tmp_path, text) + with pytest.raises(ProfileError, match=re.escape(field)) as exc: + load_profile(str(path), environ={}) + assert str(path) in str(exc.value) + + +def test_a_credential_shaped_value_is_refused(tmp_path: Path) -> None: + path = write_profile(tmp_path, MINIMAL.replace("s3://m", "sk-" + "a1b2c3d4" * 3)) + with pytest.raises(ProfileError, match="value of memory looks like a credential"): + load_profile(str(path), environ={}) + + +def test_an_images_file_without_digest_pins_is_refused(tmp_path: Path) -> None: + path = write_profile(tmp_path) + (tmp_path / "images.json").write_text(json.dumps({"runner": "registry.test:5000/ousast-runner:dev"}), encoding="utf-8") + with pytest.raises(ProfileError, match="runner is not digest-pinned"): + load_profile(str(path), environ={}) + (tmp_path / "images.json").write_text(f"registry.test:5000/ousast-runner@{DIGEST}\n", encoding="utf-8") + assert load_images(tmp_path / "images.json") == {"runner": f"registry.test:5000/ousast-runner@{DIGEST}"}, "the one-line form" + (tmp_path / "images.json").unlink() + loaded = load_profile(str(path), environ={}) # a file the release has not published yet: doctor's finding, not a load error + with pytest.raises(ProfileError, match="images file"): + loaded.load_images() + + +def test_an_environment_override_of_the_wrong_shape_is_refused(tmp_path: Path) -> None: + path = write_profile(tmp_path, MINIMAL + '[pools.default]\nreplicas = 1\ncpu = "1"\nmemory = "1Gi"\n') + with pytest.raises(ProfileError, match="OUSAST_POOL_DEFAULT_REPLICAS must be an integer"): + load_profile(str(path), environ={"OUSAST_POOL_DEFAULT_REPLICAS": "many"}) + with pytest.raises(ProfileError, match="names a pool the profile does not declare"): + load_profile(str(path), environ={"OUSAST_POOL_ENGINE_REPLICAS": "1"}) + with pytest.raises(ProfileError, match="exec must be one of"): + load_profile(str(path), environ={"OUSAST_EXEC": "both"}) + + +def test_a_missing_profile_names_its_path(tmp_path: Path) -> None: + with pytest.raises(ProfileError, match="no-such.toml"): + load_profile(str(tmp_path / "no-such.toml"), environ={}) + with pytest.raises(ProfileError, match="unknown"): + load_profile("unknown", environ={"OUSAST_PLANE_PROFILES": str(tmp_path)}) + + +@pytest.mark.parametrize("name", ["kind", "k3s"]) +def test_both_committed_profiles_load_with_the_designs_values(name: str) -> None: + loaded = load_profile(name, environ={}) + assert loaded.path == profiles_dir() / f"{name}.toml" and profiles_dir() == ROOT / "ops/k8s/profiles" + assert loaded.exec == {"kind": "local", "k3s": "remote"}[name] + assert loaded.memory == "s3://sast-memory" and loaded.atespace == "default" and loaded.snapshots_bucket == "s3://ax-snapshots" + assert set(loaded.pools) == {"default", "engine"} and loaded.pools["engine"].memory == "4Gi" + assert loaded.router_url == "" and loaded.ax_server == "", "both profiles tunnel through the Kubernetes API" + if name == "kind": + assert loaded.image_pull_secret == "" and loaded.images.name == "kind-images.json" and loaded.images.is_file() + assert set(loaded.load_images()) == {"runner"} and loaded.load_images()["runner"].startswith(loaded.registry + "/") + else: + assert loaded.image_pull_secret == "ghcr-pull" and loaded.registry.startswith("ghcr.io/") and loaded.images.name == "images.json" + assert not loaded.images.is_file(), "the release asset is downloaded, never committed (design section 8)" + + +def test_no_committed_profile_value_matches_a_credential_pattern() -> None: + patterns = ( + re.compile(r"(?i)(api_key|secret_key|access_key|token|password)\s*="), # image_pull_secret names a Secret, holds none + re.compile(r"sk-[A-Za-z0-9]{8,}"), + re.compile(r"AKIA[0-9A-Z]{12,}"), + ) + for path in sorted(profiles_dir().glob("*.toml")): + for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + code = line.split("#", 1)[0] + for pattern in patterns: + assert not pattern.search(code), f"{path}:{number}: {line.strip()}" + + +def test_print_for_shell_scripts(capsys: pytest.CaptureFixture[str], tmp_path: Path) -> None: + path = write_profile(tmp_path, MINIMAL + '[pools.default]\nreplicas = 3\ncpu = "1"\nmemory = "1Gi"\n') + fields = ["registry", "kube_context", "images.runner", "pools.default.replicas"] + assert profile_module.main(["--profile", str(path), "--print", *fields]) == 0 + assert capsys.readouterr().out.splitlines() == ["registry.test:5000", "kind-test", f"registry.test:5000/ousast-runner@{DIGEST}", "3"] + assert profile_module.main(["--profile", str(path), "--print", "images.engine"]) == 2 + assert "no image named 'engine'" in capsys.readouterr().err + assert profile_module.main(["--profile", str(path), "--print", "nothing"]) == 2 + + +def test_profile_is_frozen_and_typed() -> None: + loaded = load_profile("kind", environ={}) + assert isinstance(loaded, PlaneProfile) + with pytest.raises(AttributeError): + loaded.exec = "remote" # type: ignore[misc]