QA plan: Content provenance and classification gate (#66)
Feature: Content provenance and classification (#66)
Capability: Security (#45)
Related: Story #67 (Pre-deploy corpus classification gate); security per-feature requirements (#66/#67)
Test scope
Section titled “Test scope”| Type | Coverage |
|---|---|
| Unit (#66) | Ingestion writer creates a corpus_provenance row containing: dataset name, revision SHA, license string, ingestion timestamp |
| Unit (#66) | corpus_provenance row is created even when --limit restricts the row count |
| Integration (#66) | Full rag-ingest load run: corpus_provenance table exists and contains exactly one row per ingestion run with a non-null revision_sha |
| IaC / CODEOWNERS (#66) | CODEOWNERS maps the corpus reference paths (pyproject.toml, ingestion config) to the data-classification owner; a PR changing the corpus reference without that review is rejected |
| CI gate (#67) | CI step parsing the PR diff: if the corpus reference in pyproject.toml or ingestion config changes without a matching update to the data-classification page, the job fails |
| CI gate (#67) | CI step passes when both the corpus reference and the data-classification page are updated in the same PR |
| Contract | corpus_provenance table schema is documented (column names and types) in the migration file and matches the ingestion writer |
Entry criteria
Section titled “Entry criteria”- Story #8 (load and chunk) merged (ingestion pipeline exists)
- Story #10 (local DB schema) with migrations in place
- Data classification document exists
CODEOWNERSfile exists in the app repo
Exit criteria
Section titled “Exit criteria”- Unit tests: provenance writer creates a row with all required fields; 70% per-file floor met for the provenance module
- Integration:
corpus_provenancerow exists afterrag-ingest load;revision_shais non-null - CI gate: a test PR changing the corpus reference without updating data-classification fails the classification gate job; a PR updating both passes
CODEOWNERSentry verified bycat CODEOWNERS | grep corpusin CI- Schema in migration matches the model class
Environments
Section titled “Environments”Unit and integration: CI (Docker Postgres). CI gate: GitHub Actions (PR diff parser). CODEOWNERS: CI check.
Tooling
Section titled “Tooling”pytest, pytest-cov, Docker Postgres fixture, GitHub Actions (PR diff step), git diff.
How tests are written
Section titled “How tests are written”def test_provenance_row_created_on_ingest(db_session, parquet_seed_fixture): # Given the ingestion pipeline and a seed corpus file runner = IngestRunner(db_session, corpus_path=parquet_seed_fixture) # When the load command runs runner.load(limit=10) # Then a corpus_provenance row exists with all required fields populated row = db_session.execute( "SELECT dataset_name, revision_sha, license_string, ingested_at FROM corpus_provenance" ).fetchone() assert row is not None assert row.dataset_name == "neural-bridge/rag-dataset-12000" assert row.revision_sha is not None assert row.license_string == "apache-2.0" assert row.ingested_at is not NoneCI gate (shell, not pytest):
# Given a PR diff that changes pyproject.toml corpus referenceCHANGED=$(git diff origin/main --name-only | grep -E 'pyproject.toml|ingestion.ya?ml')if [ -n "$CHANGED" ]; then # When the data-classification page is checked for a matching update CLASSIF_CHANGED=$(git diff origin/main --name-only | grep 'data-classification') # Then the classification page must also be updated [ -n "$CLASSIF_CHANGED" ] || (echo "ERROR: corpus reference changed without data-classification update" && exit 1)fi- HuggingFace dataset revision SHA is not always stable; the loader must explicitly call
hf_hub.snapshot_downloadwithrevision="main"and record the resolved SHA, not the branch name. - The CI diff parser for the classification gate is fragile if file paths change; anchor it to glob patterns defined in a config rather than hardcoded paths.
Capability acceptance criteria link
Section titled “Capability acceptance criteria link”Security per-feature requirements (#66/#67): provenance table with dataset name, revision SHA, license, timestamp; CODEOWNERS review gate; CI classification gate. Exit criteria map directly to each row.
© 2026 Benjamin Arunski