Corpus Census — population, duplication, distinct vulnerabilities
Paths. analysis-results/… and progress-tracker/… in this skill are the
default workspace layout. They resolve through locations.yaml in
$TRAUST_CONFIG_HOME (docs/setup.md, Storage locations); substitute your
configured roots.
One deterministic pass that answers "what do we actually have": the report
population per tree and ownership cut, the duplication vectors that inflate
naive counts, and the distinct vulnerabilities headline (Lens 2 of the
three-lens taxonomy — see
progress-tracker/plans/metrics-improvement-plan.md).
Input
$ARGUMENTS is optional and may be any combination of:
| Token |
Meaning |
| (none) |
Default run — resolve the corpus, write all four outputs. |
summary |
Also print the census Markdown to the conversation. |
open |
Open the HTML dashboard in the default browser when done. |
<path> |
Override the workspace root (directory containing analysis-results/ and progress-tracker/). |
Procedure
Step 1 — Locate the workspace root
Resolution order:
- A path passed in
$ARGUMENTS.
- The parent of the
traust checkout (the normal sibling
layout with analysis-results/ and progress-tracker/).
If neither contains analysis-results/, ask the user for the path.
Step 2 — Run the harness script
python3 harnessing/census/scripts/build_census.py \
[--workspace-root <resolved-root>] \
[--summary]
What the script does:
- Resolves the population via python3 -m traust.cli corpus against
$TRAUST_CONFIG_HOME/corpus-config.yaml: depth-tolerant walk of every registered
tree, symlink aliases mapped to canonical reports (never counted),
branch-ref identity (declared metadata.ref preferred, legacy
__release-X.Y slug fallback → base slug + ref; a compact per-ref
breakdown table lands under "Population by tree"), ownership
tags (owned / upstream / external-bu). Registered engagement trees
(any registered <engagement>-findings/) activate automatically
when they appear on disk; unregistered trees holding audit reports
raise drift warnings.
- Loads every report's preferred layer —
*-findings-current.json
(disposition-aware) when present, else the audit JSON — and classifies
each finding: false positives dropped, hardening bucketed separately,
everything else a vulnerability keyed by its fingerprint (computed
via python3 -m traust.cli corpus finding-identity when absent).
- Computes distinct vulnerabilities per ownership cut: unique
fingerprints at HEAD (branch re-audits excluded from the headline),
severity = max across occurrences, open = any occurrence not
resolved/risk-accepted.
- Quantifies the five duplication vectors: symlink aliases (incl.
ledgers attached to aliased reports), branch re-audits (findings that
fingerprint-match a HEAD finding count as confirmations, not new
exposure — strict tier-1 matching, so branch-only counts are an upper
bound), layered-artifact restatement, cross-tree slug overlap, and
duplicate basenames.
- Writes the four outputs and appends a snapshot row to the central
metrics ledger (python3 -m traust.cli metrics history, source
census;
--skip-ledger to suppress), injecting a trend-vs-previous line into
the Markdown.
Step 2b — Refresh repo liveness (archive-awareness)
The census owns repo liveness — population metadata recording which
repos can still act on findings (metrics-improvement plan, Phase 7):
python3 harnessing/census/scripts/check_repo_liveness.py \
--spine <workspace-root>/analysis-results/graph/repo-graph.json \
--out <workspace-root>/progress-tracker/metrics/repo-liveness.json
One GitHub API call per repo (gh must be authenticated; ~10 minutes on
a full sweep) emitting a status per repo — active | archived | moved | missing | unknown — with status_since ratcheted from the previous
artifact. Skip when gh is unauthenticated or the spine is absent, and
say so in the report-back; a stale liveness artifact means downstream
consumers (audit stamps, verify-remediation sweep flags, graph attrs,
dashboard segments) are reading old status. Include the non-active
counts in the report-back — archived-but-shipping components are a
distinct risk segment, not burndown residue.
Step 2c — Rebuild the findings database (projection)
The census refresh is what keeps the queryable findings DB current
(C9 — /findings-db): rebuild it from the same corpus resolution so the
projection can never lag the denominator authority by more than one
census run:
python3 -m traust.cli corpus findings-db \
--results-root <workspace-root>/analysis-results
Full deterministic rebuild (< 1 min), output
analysis-results/graph/findings.db (gitignored, rebuildable). The DB is
a projection of this census — its meta table records the build time
and the authority rule; when DB and census numbers disagree, rebuild the
DB and trust the census. Skip (and say so) only if the script or the
corpus config is unavailable.
Step 2d — Compliance-assessed coverage line
The compliance posture cut lives in
progress-tracker/metrics/dashboards/compliance/ (built by
python3 -m traust.cli compliance dashboard on the census's own corpus
resolver, so its population block reconciles with this census by
construction). When reporting census results, include the
"compliance-assessed targets" line from that dashboard — it is a
coverage cut over this census's denominators, never a separate
population.
Step 3 — Report back
Always tell the user:
- The executive view: distinct owned vulnerabilities (total + open, with
C/H/M/L/I split), the adjacent upstream line, and the external-BU
work-performed count.
- The duplication-vector summary (the five numbered lines).
- Any drift warnings (unregistered trees, md-only parse gaps, parse
errors).
- The four output paths.
If open was requested:
open progress-tracker/metrics/dashboards/census/census.html
Outputs
| File |
Purpose |
census/census.md |
Executive view, population-by-tree table, duplication vectors, parse gaps/drift, standard population block. |
census/census.html |
Self-contained dashboard: stat cards, ownership severity chips, tree + duplication tables, drift warnings. |
census/census.json |
Machine-readable census: population, ownership cuts, duplication vectors, warnings, timings. |
findings.db repos |
Full resolved record list — the denominator source other dashboards cite. Written by traust_engine.corpus.findings_db, not here; corpus-manifest.json was retired 2026-08-20 as a write-only artifact nothing read. |
Reading the numbers
- Distinct vulnerabilities (owned) is the canonical Lens 2 headline:
findings/ only, HEAD only, fingerprint-deduped,
disposition-adjusted. Never average it with occurrence counts from
other dashboards — reconcile via each dashboard's population block.
- Upstream (oss-findings/) is reported adjacent to the owned
headline and is never folded in.
- External-BU trees (ecoengg, OpenStack, future engagements)
appear only as work performed (Lens 1).
- Branch re-audit confirmations are Lens 1 coverage evidence
("N HEAD findings confirmed present on release branches"), not new
exposure.
Failure modes
| Symptom |
Likely cause |
Fix |
error: <ws>/analysis-results not found |
Wrong workspace root |
Pass the directory that contains analysis-results/. |
| Unregistered-tree warning |
New campaign tree on disk not in corpus-config.yaml |
Register it via /corpus-intake (until that skill ships: edit $TRAUST_CONFIG_HOME/corpus-config.yaml). |
| Distinct counts moved sharply with no new audits |
Disposition ledgers landed (findings-current now preferred) |
Expected — the census is disposition-aware by design. |
metrics-ledger append skipped on stderr |
progress-tracker not writable/missing |
Census still writes all four outputs; fix the sibling checkout to restore trending. |
1---2name: census3description: Use when the user asks for the corpus census, the report population, "how many repos/reports do we actually have", how much duplication is in the numbers, the distinct-vulnerability count, ownership cuts (owned vs upstream vs external-BU), or asks to (re)build the census or corpus manifest. Deterministically resolves the report population via python3 -m traust.cli corpus + $TRAUST_CONFIG_HOME/corpus-config.yaml, quantifies the five duplication vectors, computes distinct vulnerabilities (fingerprint-deduped at HEAD, disposition-adjusted, FP-excluded, hardening separate) per ownership cut, and writes census.{json,md,html} — the denominator authority every other dashboard cites.4---56# Corpus Census — population, duplication, distinct vulnerabilities78> **Paths.** `analysis-results/…` and `progress-tracker/…` in this skill are the9> default workspace layout. They resolve through `locations.yaml` in10> `$TRAUST_CONFIG_HOME` (`docs/setup.md`, Storage locations); substitute your11> configured roots.121314One deterministic pass that answers "what do we actually have": the report15population per tree and ownership cut, the duplication vectors that inflate16naive counts, and the **distinct vulnerabilities** headline (Lens 2 of the17three-lens taxonomy — see18`progress-tracker/plans/metrics-improvement-plan.md`).1920## Input2122`$ARGUMENTS` is optional and may be any combination of:2324| Token | Meaning |25|---|---|26| *(none)* | Default run — resolve the corpus, write all four outputs. |27| `summary` | Also print the census Markdown to the conversation. |28| `open` | Open the HTML dashboard in the default browser when done. |29| `<path>` | Override the workspace root (directory containing `analysis-results/` and `progress-tracker/`). |3031---3233## Procedure3435### Step 1 — Locate the workspace root3637Resolution order:38391. A path passed in `$ARGUMENTS`.402. The parent of the `traust` checkout (the normal sibling41 layout with `analysis-results/` and `progress-tracker/`).4243If neither contains `analysis-results/`, ask the user for the path.4445### Step 2 — Run the harness script4647```bash48python3 harnessing/census/scripts/build_census.py \49 [--workspace-root <resolved-root>] \50 [--summary]51```5253What the script does:54551. Resolves the population via python3 -m traust.cli corpus against56 `$TRAUST_CONFIG_HOME/corpus-config.yaml`: depth-tolerant walk of every registered57 tree, symlink aliases mapped to canonical reports (never counted),58 branch-ref identity (declared `metadata.ref` preferred, legacy59 `__release-X.Y` slug fallback → base slug + ref; a compact per-ref60 breakdown table lands under "Population by tree"), ownership61 tags (owned / upstream / external-bu). Registered engagement trees62 (any registered `<engagement>-findings/`) activate automatically63 when they appear on disk; unregistered trees holding audit reports64 raise drift warnings.652. Loads every report's preferred layer — `*-findings-current.json`66 (disposition-aware) when present, else the audit JSON — and classifies67 each finding: false positives dropped, hardening bucketed separately,68 everything else a vulnerability keyed by its `fingerprint` (computed69 via python3 -m traust.cli corpus finding-identity when absent).703. Computes **distinct vulnerabilities** per ownership cut: unique71 fingerprints at HEAD (branch re-audits excluded from the headline),72 severity = max across occurrences, open = any occurrence not73 resolved/risk-accepted.744. Quantifies the five duplication vectors: symlink aliases (incl.75 ledgers attached to aliased reports), branch re-audits (findings that76 fingerprint-match a HEAD finding count as confirmations, not new77 exposure — strict tier-1 matching, so branch-only counts are an upper78 bound), layered-artifact restatement, cross-tree slug overlap, and79 duplicate basenames.805. Writes the four outputs and appends a snapshot row to the central81 metrics ledger (python3 -m traust.cli metrics history, source `census`;82 `--skip-ledger` to suppress), injecting a trend-vs-previous line into83 the Markdown.8485### Step 2b — Refresh repo liveness (archive-awareness)8687The census owns **repo liveness** — population metadata recording which88repos can still act on findings (metrics-improvement plan, Phase 7):8990```bash91python3 harnessing/census/scripts/check_repo_liveness.py \92 --spine <workspace-root>/analysis-results/graph/repo-graph.json \93 --out <workspace-root>/progress-tracker/metrics/repo-liveness.json94```9596One GitHub API call per repo (`gh` must be authenticated; ~10 minutes on97a full sweep) emitting a status per repo — `active | archived | moved |98missing | unknown` — with `status_since` ratcheted from the previous99artifact. Skip when `gh` is unauthenticated or the spine is absent, and100say so in the report-back; a stale liveness artifact means downstream101consumers (audit stamps, verify-remediation sweep flags, graph attrs,102dashboard segments) are reading old status. Include the non-`active`103counts in the report-back — archived-but-shipping components are a104distinct risk segment, not burndown residue.105106### Step 2c — Rebuild the findings database (projection)107108The census refresh is what keeps the queryable findings DB current109(C9 — `/findings-db`): rebuild it from the same corpus resolution so the110projection can never lag the denominator authority by more than one111census run:112113```bash114python3 -m traust.cli corpus findings-db \115 --results-root <workspace-root>/analysis-results116```117118Full deterministic rebuild (< 1 min), output119`analysis-results/graph/findings.db` (gitignored, rebuildable). The DB is120a **projection of this census** — its `meta` table records the build time121and the authority rule; when DB and census numbers disagree, rebuild the122DB and trust the census. Skip (and say so) only if the script or the123corpus config is unavailable.124125### Step 2d — Compliance-assessed coverage line126127The compliance posture cut lives in128`progress-tracker/metrics/dashboards/compliance/` (built by129python3 -m traust.cli compliance dashboard on the census's own corpus130resolver, so its population block reconciles with this census by131construction). When reporting census results, include the132"compliance-assessed targets" line from that dashboard — it is a133coverage cut over this census's denominators, never a separate134population.135136### Step 3 — Report back137138Always tell the user:139140- The executive view: distinct owned vulnerabilities (total + open, with141 C/H/M/L/I split), the adjacent upstream line, and the external-BU142 work-performed count.143- The duplication-vector summary (the five numbered lines).144- Any drift warnings (unregistered trees, md-only parse gaps, parse145 errors).146- The four output paths.147148If `open` was requested:149`open progress-tracker/metrics/dashboards/census/census.html`150151---152153## Outputs154155| File | Purpose |156|---|---|157| `census/census.md` | Executive view, population-by-tree table, duplication vectors, parse gaps/drift, standard population block. |158| `census/census.html` | Self-contained dashboard: stat cards, ownership severity chips, tree + duplication tables, drift warnings. |159| `census/census.json` | Machine-readable census: population, ownership cuts, duplication vectors, warnings, timings. |160| `findings.db` `repos` | Full resolved record list — the denominator source other dashboards cite. Written by `traust_engine.corpus.findings_db`, not here; `corpus-manifest.json` was retired 2026-08-20 as a write-only artifact nothing read. |161162---163164## Reading the numbers165166- **Distinct vulnerabilities (owned)** is the canonical Lens 2 headline:167 `findings/` only, HEAD only, fingerprint-deduped,168 disposition-adjusted. Never average it with occurrence counts from169 other dashboards — reconcile via each dashboard's population block.170- **Upstream (oss-findings/)** is reported adjacent to the owned171 headline and is never folded in.172- **External-BU trees** (ecoengg, OpenStack, future engagements)173 appear only as work performed (Lens 1).174- **Branch re-audit confirmations** are Lens 1 coverage evidence175 ("N HEAD findings confirmed present on release branches"), not new176 exposure.177178## Failure modes179180| Symptom | Likely cause | Fix |181|---|---|---|182| `error: <ws>/analysis-results not found` | Wrong workspace root | Pass the directory that contains `analysis-results/`. |183| Unregistered-tree warning | New campaign tree on disk not in `corpus-config.yaml` | Register it via `/corpus-intake` (until that skill ships: edit `$TRAUST_CONFIG_HOME/corpus-config.yaml`). |184| Distinct counts moved sharply with no new audits | Disposition ledgers landed (findings-current now preferred) | Expected — the census is disposition-aware by design. |185| `metrics-ledger append skipped` on stderr | progress-tracker not writable/missing | Census still writes all four outputs; fix the sibling checkout to restore trending. |