This document describes the full workflow used to perform AI-assisted security assessments across a portfolio of components, from repository discovery through triage, validation, remediation, and defect filing — and the standing loop that keeps it all current.
The stage numbering here is the campaign numbering — README.md's
pipeline diagram, docs/skills.md's Stage column, and this page all use
the same ①–⑨ (the 2026-07-31 docs-verification found three
incompatible schemes; this page was the worst offender, documenting
the retired pre-ledger campaign). Current campaign numbers are never
quoted here — they live in the dashboards
(/census for the population authority,
progress-tracker/metrics/dashboards/ for the rest).
Inputs ─▶ ① Inventory ─▶ ② Threat model ─▶ ③ Audit ─▶ ④ Triage ─┬─▶ ⑧ Package ─▶ ⑨ Assign ─▶ Share/Notify ─▶ File defects
(inputs) (analysis-results) │ (progress-tracker → Drive, Slack, Jira)
├─▶ ⑤ Live validation (validate-* / deploy-operator)
├─▶ ⑥ Fuzzing (create-fuzzing)
└─▶ ⑦ Remediation (remediate-finding / patch / verify)
Stages ③–⑦ feed the append-only disposition ledger (track-findings), gated by human countersign;
follow-up scans (vuln-scan, verify-remediation regressions, dependency-watch) enter it directly.
Standing loop (daily, after the first pass — docs/continuous-operations.md):
fleet refresh ─▶ router ─▶ lanes: diff-scan │ deps (/dependency-watch) │ IaC │ verify treadmill │ rule mining (/mine-ledger)
└─ findings re-enter ④ Triage / the ledger; dashboards & drift-watch rebuild on cadence
Each stage uses skills from this repository and produces artifacts that feed the next; the disposition ledger and the metrics spine run across all of them.
Model routing & spend: every stage's model comes from
config/model-registry.yamland spend lands on the spend dashboard — see docs/model-routing.md.
Skill: inventory-repositories (internal extension repo) (automated discovery) · add-inputs (ad-hoc registration)
Discover every source code repository belonging to each product segment in the Hybrid Platforms portfolio: OpenShift release payloads, OLM operator bundles, and managed services.
For repos or whole orgs outside those discovery paths, add-inputs
appends them (plus owners.csv rows) to a segment CSV directly —
default segment adhoc/. When the addition is a new scan engagement,
corpus-intake registers the matching output tree in
$TRAUST_CONFIG_HOME/corpus-config.yaml and records the inventory path, linking the
two sides. The full sequence — both registrations, census confirmation,
repo-graph rebuild — is a deployment procedure; each step is its own skill
(/add-inputs, /corpus-intake, /census, /repo-graph).
Output: CSV inventories, Markdown analysis reports, and owners.csv
files pushed to the inventory repository (see Repository Map).
Skill: threat-model
Build the target's threat model before (or alongside) its first audit —
the map of what could go wrong independent of any specific bug.
bootstrap derives one from code plus past vulnerabilities; interview
walks an application owner through the four-question framework;
maintenance modes (review/update/pr) keep it current as the code
moves. The emitted <repo>-threat-model.md is copied into the
findings tree (analysis-results/findings/<product>/<repo>/) by
contract — that copy is what ③ Audit's coverage diff, /vuln-scan,
/triage, and /threat-register consume.
Skill: secure-code-audit · profile variants secure-rpm-audit, secure-container-audit, cloud-config-audit, vuln-scan (supplement/diff scans)
Run multi-framework security audits against the inventoried
repositories. Each repository is assessed against eight
industry-standard frameworks: OWASP ASVS v5.0, OWASP Kubernetes Top 10
(2025), CIS Kubernetes Benchmark v2.0, DISA STIG V2R6, SLSA v1.2,
OpenSSF Scorecard, the SEI CERT coding standards
(language-conditional), and the PEACH tenant-isolation framework.
Dual-pass execution is the campaign default; deterministic pre-scans
(opengrep, gitleaks, osv-scanner, syft/grype, checkov, k8s-hardening)
seed both passes and are recorded under deterministic_steps.
Batch orchestration is driven by the continuous-operations router's
worklist and the findings manifest
(analysis-results/findings/_manifest/) — there is no fixed
parallel-agent width; the manifest system handles deduplication (one
canonical report per repo+ref, symlinks from sibling product
directories).
Output: schema-validated JSON reports (<repo>-security-audit.json,
with rendered Markdown companions via python3 -m traust.cli reporting render —
auditors never hand-write the Markdown) pushed to the results
repository (see Repository Map).
Skill: triage
Adversarially verify every audit finding: N-vote verification with
cited evidence, duplicate collapse, derived-exploitability re-ranking,
and owner tagging. Verdicts land as <repo>-triage.{json,md} next to
the audit report and flow into the disposition ledger automatically
(Phase 6e → python3 -m traust.cli.emit_triage_ledger_events): true positives
become machine-confirmed events, false positives enter the tiered
countersign flow, hardening findings take the hardening validity.
The full contract: docs/disposition-ledger.md §6b.
Skills: validate-operator-live (internal extension repo) (orchestration) → deploy-operator (internal extension repo) + validate-findings · validate-browser-finding (web UI vulns) · validate-core-ocp (internal extension repo)
Prove or refute audit findings against an authorized live
environment, then chain confirmed findings into multi-step
kill-chains and hunt for novel attacks the static analysis missed. For
OLM-layered operators the campaign runs on disposable ephemeral ROSA
HCP clusters (provision_rosa.sh, always torn down); for
browser-exploitable web vulnerabilities, validate-browser-finding
drives Playwright against lab targets.
The campaign manifest lives at analysis-results/validations/_manifest/
(validation-manifest.csv + next_pending_validation.py +
update_validation_progress.py; product resolution via
the internal extension's deploy-operator product map). Every verdict passes the
fail-closed gate stack in
docs/validation-process.md; results emit
<slug>-validation.{json,md} + validation-audit.jsonl and enter the
ledger via python3 -m traust.cli.emit_validation_ledger_events (execution-graded
evidence — E0/E1 carries class-1 override power).
Skill: create-fuzzing
Offline fuzz harnesses for parser/decoder/templater entry points flagged in audit reports — Go-native by default, plus Python, JS/TS, Rust, and Java templates. The refuted register is a standing input: FP-dismissed findings in fuzzable classes are priority targets (falsifiability discipline — see docs/disposition-ledger.md §6). Crashers triage into follow-up findings through the same pipeline.
Skills: remediate-finding (fork-flow patches with the repo's own checks) · patch (inert candidate diffs for human review) · fleet-fix (one transform × N repos) · verify-remediation
Produce fixes and verify them. /patch writes an inert human-review
packet (terminal by design — no auto-apply path exists);
/remediate-finding builds reviewable patches on private forks and
runs the repo's own build/test suite in containment;
/verify-remediation re-audits each original finding against the
patched code (single repo, or the weekly full-sweep treadmill) and its
verdicts resolve ledger entries via /track-findings.
Skill: generate-team-report
Generate self-contained report packages for each operator, product, or service — the relevant component-level reports bundled with an executive-summary HTML dashboard.
Output: findings folders under processed-results/ in the
tracker repository (see Repository Map).
Skill: assign-findings-owners (internal extension repo)
For each findings package, identify two Red Hat lead developer owners who will receive the findings and delegate remediation. The skill resolves owners through a chain of data sources:
- Product Security product registry (python3 -m traust.cli registry products) — non-authoritative context resolved first: Kerberos-ID candidate seeds, the accountable escalation contact, and the product lifecycle window, so an EOL or unreleased product is caught before the rest of the chain runs. Never names an owner.
owners.csvfiles from the inventory stage- Org repo team structures (
org/config/structures/YAML) - OpenShift CI
OWNERSfiles - Corporate directory identity verification (LDAP; see
HARNESS_LDAP_SERVER) - Git commit email resolution as fallback
Confirmed assignments are recorded in
progress-tracker/owners-mapping.md and
progress-tracker/tracking/routed-results.md. Ownership changes after
initial routing go through
reassign-findings-owners (internal extension repo),
which re-verifies via LDAP, re-resolves registry context, maintains the
Escalation column, and updates Drive permissions. Escalation contacts
receive the findings folder as commenters unless LDAP marks them
contingent (2026-07-30 decision), and never hold an owner slot.
Google Drive. The team's Shared Drive (id in the deployment's sharing config) carries
a copy of every findings folder from processed-results/, shared with
its assigned owners via progress-tracker/scripts/drive/share_results.py
(commenter grants, OAuth desktop-app credentials). Sharing plans are
YAML configs under progress-tracker/configs/ (per-campaign files —
see that directory; escalation contacts share through
configs/escalation-contacts-share.yaml); folder IDs come from
progress-tracker/tracking/drive-sharing-plan.md, recipients from
tracking/routed-results.md.
python share_results.py --config configs/<campaign>.yaml --dry-run # preview
python share_results.py --config configs/<campaign>.yaml # share for realSlack. Once Drive access is granted, each owner receives a DM via the Chai bot directing them to their findings folder and the engineering process document.
Jira. For each Critical (and optionally High) finding,
file-security-defect (internal extension repo)
creates an embargoed Jira defect (finding data extracted from the audit
report, owning team/project resolved from org/config/structures/,
Security Level "Embargoed Security Issue"). Filed defects are tracked
in progress-tracker/tracking/opened-tickets.md.
Everything from ③ on feeds one append-only evidence stream per repo
(<repo>-findings-layer.json), merged deterministically into
<repo>-findings-current.{json,md} by
track-findings. Machine
refutations pend until an LDAP-verified human signs them at the
countersign workbench
(two-person rule against execution proof; salted audit valve on the
auto-accept tier); every false positive enters a refuted register that
feeds ⑥ Fuzzing and ⑤ Live-validation target selection. Design
rationale: docs/disposition-ledger.md;
artifact chain: docs/artifacts.md;
error doctrine: docs/error-model.md.
The stages above describe one pass. After the initial baseline, rescan
frequency is decided deterministically by the continuous-operations
router (python3 -m traust.cli.build_rescan_worklist
→ analysis-results/findings/_manifest/rescan-worklist.json), not by a
calendar: heavy or security-sensitive code churn and injected events
(external reports, methodology releases, CVE reachability, artifact
releases) route repos back into ③ as full audits or
/vuln-scan --diff scans; age ceilings backstop slow drift; /drift-watch
flags a worklist older than 3 days. The full loop — lanes, cadences,
arrival-channel coverage, and budget knobs — is documented in
docs/continuous-operations.md.
Dashboards rebuild on cadence via
refresh-dashboards.
Every campaign number flows through one consistency spine
(progress-tracker/plans/metrics-improvement-plan.md):
$TRAUST_CONFIG_HOME/corpus-config.yamlregisters every output tree with an ownership tag —owned(findings/, Hybrid Platforms),upstream(oss-findings/, community code HP depends on; reported adjacent, never folded into the owned headline),external-bu(scan engagements for other business units; work-performed only). Updated only via/corpus-intake.- python3 -m traust.cli corpus is the one implementation of report discovery,
identity (branch-ref stripping, symlink aliasing), and dedup that every
dashboard builder uses;
/censuspublishes the resolved population, the five duplication vectors, repo liveness, and the distinct-vulnerabilities headline as the denominator authority (findings.dbrepos). - Three-lens taxonomy — every reported number carries a lens: Lens 1 work performed (occurrences/throughput; a defect shipped in N products counts N times), Lens 2 distinct exposure (unique finding fingerprints at HEAD, disposition-adjusted — the canonical headline), Lens 3 systemic patterns (fleet leverage). Never average across lenses.
- Ref semantics (branch-awareness plan —
progress-tracker/plans/branch-awareness-plan.md): a report'smetadata.ref/metadata.ref_kindis the authoritative statement of the ref it was audited from;__release-X.Yslug suffixes are a legacy fallback only (corpus.py prefers declared refs and never extends the slug whitelist). The finding fingerprint stays branch-blind — that is what folds a release-branch confirmation into its HEAD finding — and Lens 2 stays HEAD-canonical: branch audits confirm shipped exposure, they never add headline units. - Every generated dashboard embeds a population block (roots + ownership, unit, filters, denominator source) so any two headline numbers reconcile on paper.
The process needs four repositories. Only the first is this one; the other three hold output and are yours to create — on any forge, under any names. They are checked out as siblings of the harness, and the names below are the sibling directory names the tooling expects.
| Repository | Role in Process |
|---|---|
traust |
Skills, commands, and prompts that drive every stage |
the inputs inventory (locations.inputs) |
① output — repository inventories and ownership data |
analysis-results |
②–⑦ outputs — threat models, audit reports, triage, validations, ledgers |
progress-tracker |
⑧–⑨ + delivery — packaged reports, owner assignments, sharing plans, filed defects, dashboards |