# v0.71.0 Full Details — CI Truthfulness, Test Harness & Documentation Cleanup

> **Summary:** [v0.71.0.md](v0.71.0.md)
> **Assessment source:** [plans/PLAN_OVERALL_ASSESSMENT_13.md](../plans/PLAN_OVERALL_ASSESSMENT_13.md)

---

## Motivation

This is the closeout release for the Assessment-13 hardening arc. The items
here are low-risk, high-leverage hygiene changes: correct the gates developers
and CI use to detect problems, generate artefacts rather than maintain them by
hand, and remove historical confusion from the planning hierarchy.

The principle is: every CI check must mean something, every generator must
produce correct output, and every planning document must be authoritative or
clearly labelled as historical.

---

## Detailed Implementation

### CI-001: Dynamic Fuzz Target Matrix

**Files changed:**
- `.github/workflows/fuzz-smoke.yml` — replace hard-coded matrix with
  `$(ls fuzz/fuzz_targets/*.rs | xargs -I{} basename {} .rs)` in a matrix
  `include` step generated by a `prepare-matrix` job
- `scripts/check_fuzz_targets.py` — new script; reads `fuzz/fuzz_targets/*.rs`
  and fails if any file is missing from the workflow target list
- `.github/workflows/fuzz-smoke.yml` — add `check-fuzz-targets` step before
  the fuzz matrix that runs `scripts/check_fuzz_targets.py`
- `justfile` — `fuzz-smoke` recipe updated to match

**Acceptance criteria:**
1. `fuzz-smoke.yml` runs all nine targets in CI.
2. Adding a new `fuzz/fuzz_targets/new_target.rs` without updating the workflow
   fails CI.

---

### CI-002: `just fuzz-all` Failure Propagation

**Files changed:**
- `justfile` — `fuzz-all` recipe: remove `|| true`; introduce `FAILED_TARGETS`
  accumulator variable; print summary; `exit 1` if non-empty
- `justfile` — new `fuzz-all-best-effort` recipe that restores old behaviour

---

### CI-003: E2E Coverage Schedule

**Files changed:**
- `.github/workflows/coverage.yml` — add `schedule: [{cron: '0 2 * * 0'}]`
  trigger; gate E2E job on
  `github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'`;
  increase timeout to 90 minutes; update comments

---

### CI-004: `docs-lint` in `just lint`

**Files changed:**
- `justfile` — `lint` recipe: append `docs-lint` as a final dependency
- `AGENTS.md` — note that `just lint` now includes doc drift checking
- `CONTRIBUTING.md` — update local workflow section

---

### DEP-001: Advisory Expiry Metadata

**Files changed:**
- `deny.toml` — add structured comment block for all five RUSTSEC ignores:
  ```
  # Owner: <github-username>
  # Added: 2026-05-21
  # Review-By: 2026-08-21
  # Upstream: https://github.com/...
  # Remove-When: upstream fixed and dependency upgraded
  ```
- `scripts/check_deny_expiry.py` — new script; parses `deny.toml`; finds all
  `# Review-By:` lines; fails if any date is in the past
- `.github/workflows/dependency-policy.yml` — add step that runs
  `check_deny_expiry.py`

---

### DOC-001 / CODE-001: Generator Rewrite

**Files changed:**
- `scripts/gen_catalogs.py` — rewrite `extract_sql_functions()` to:
  1. Run `cargo pgrx schema --pg-config $(pg_config)` (or read cached output)
  2. Parse resulting `CREATE FUNCTION` statements with a lightweight
     context-free parser tracking parenthesis depth
  3. Extract `(schema, name, arg_list, return_type)` tuples
- `scripts/gen_catalogs.py` — add `validate_catalog(rows)`: fail on
  return type ending with `<`, empty non-void return, unbalanced `<>`
- `docs/SQL_API_CATALOG.md` — regenerated (committed output)
- `.github/workflows/docs-drift.yml` — the existing reproducibility check
  continues to work; the quality gate is an additional step

**Acceptance criteria:**
1. All 121 functions in the generated catalog have complete, parseable return
   types.
2. `gen_catalogs.py --check` fails on the old `docs/SQL_API_CATALOG.md`
   (regression test).

---

### CODE-002: Tarjan SCC Error Propagation

**Files changed:**
- `src/dag.rs` — `tarjan_strongconnect()`: replace two `unwrap()` calls
  with `ok_or_else(|| PgTrickleError::InternalError(...))`;
  propagate `Result` up to `tarjan_scc()` and callers
- `src/dag.rs` — add `debug_assert!(lowlinks.contains_key(&v), "SCC invariant")` 
  above each converted call
- Remove `// nosemgrep` comments now that the runtime path is non-panicking

---

### TEST-005: Generated Test Harness Schema

**Files changed:**
- `scripts/gen_test_schema.py` — new script; reads
  `sql/archive/pg_trickle--<version>.sql`; extracts DDL for tables referenced
  in `tests/common/mod.rs` by name; emits a Rust `const TEST_SCHEMA_SQL: &str`
  to stdout
- `tests/common/mod.rs` — replace hand-written DDL constants with
  `include!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/generated/schema.rs"))`
- `build.rs` — add build step that runs `gen_test_schema.py` and writes
  `tests/generated/schema.rs`; fails the build if the script returns non-zero
- `.github/workflows/ci.yml` — add `check-test-schema` step that runs
  `gen_test_schema.py --check` against the committed generated file

---

### ARCH-003 / DOC-002: PLAN.md Archival

**Files changed:**
- `plans/PLAN.md` — replaced with short Architecture & Roadmap Index
- `plans/archive/PLAN_HISTORICAL.md` — historical content (moved verbatim)
- `plans/archive/` directory created if not present

---

### DOC-003: INDEX.md Generation

**Files changed:**
- `scripts/gen_plans_index.py` — new script; scans `plans/**/*.md`;
  categorises by prefix; sorts by mtime; emits `plans/INDEX.md`
- `plans/INDEX.md` — regenerated
- `.github/workflows/docs-drift.yml` — add step that runs
  `gen_plans_index.py --check`

---

## Upgrade Notes

No schema changes in v0.71.0. The upgrade migration is a version bump only.

Build tooling note: `build.rs` now generates `tests/generated/schema.rs` during
`cargo build`. This file is `.gitignore`d in the test directory; `cargo build`
must run before `cargo test` in fresh checkouts. The CI matrix already runs
`cargo test` which triggers `build.rs`, so no CI changes are required.

---

## Implementation Status

| Item | Status | Notes |
|------|--------|-------|
| CI-001: Dynamic Fuzz Target Matrix | ✅ Done | `scripts/check_fuzz_targets.py` + `fuzz-smoke.yml` 9-target matrix |
| CI-002: `just fuzz-all` Failure Propagation | ✅ Done | `justfile` — failure accumulator; `fuzz-all-best-effort` alias added |
| CI-003: E2E Coverage Schedule | ✅ Done | `coverage.yml` — weekly cron + 90-min timeout |
| CI-004: `docs-lint` in `just lint` | ✅ Done | `justfile` + `AGENTS.md` + `CONTRIBUTING.md` updated |
| DEP-001: Advisory Expiry Metadata | ✅ Done | `deny.toml` structured comments + `scripts/check_deny_expiry.py` + `dependency-policy.yml` |
| DOC-001 / CODE-001: Generator Rewrite | ✅ Done | `scripts/gen_catalogs.py` rewritten; `docs/SQL_API_CATALOG.md` regenerated; quality gate passes |
| CODE-002: Tarjan SCC Error Propagation | ✅ Done | `src/dag.rs` — `tarjan_strongconnect`/`compute_sccs`/`condensation_order` return `Result` |
| TEST-005: Generated Test Harness Schema | ✅ Done | `scripts/gen_test_schema.py` + `build.rs` + `tests/generated/schema.rs` + `ci.yml` check |
| ARCH-003 / DOC-002: PLAN.md Archival | ✅ Done | `plans/archive/PLAN_HISTORICAL.md` + `plans/PLAN.md` rewritten as index |
| DOC-003: INDEX.md Generation | ✅ Done | `scripts/gen_plans_index.py` + `plans/INDEX.md` regenerated (156 files) + `docs-drift.yml` check |
