Auto-stamp App + Action
What it is
Section titled “What it is”The auto-stamp tooling is the bridge between PR approval and the regulated audit record. When a PR that touched regulatory-docs/**/*.sdoc is merged to main, the auto-stamp Action:
- Reads who approved the PR (via the GitHub API)
- Verifies the approver matches
.github/CODEOWNERSfor the touched paths - Computes the SHA-256 fingerprint of each touched requirement’s reviewable content
- Writes
REVIEWED_HASH+REVIEWED_BYinto the.sdocfiles - Pushes the stamping commit back to
main
The push to main uses a dedicated GitHub App identity — bioflow-autostamp for this repo — added to the branch-protection bypass list. No other workflow has that permission.
This is the mechanism that closes the regulated review trail: every stamped REVIEWED_HASH is cryptographic evidence that this exact content was approved by this exact reviewer at this exact time.
Why we chose this design (Option 1 + dedicated GitHub App)
Section titled “Why we chose this design (Option 1 + dedicated GitHub App)”Three approaches were considered. Their full trade-off discussion is in the Auto-stamp App setup playbook — short version:
- Option 1: post-merge auto-stamp pushed directly to main — clean atomic record, but requires push permission to a protected
main. Solved by giving a dedicated, narrowly-scoped App that permission. This is what we built. - Option 2: stamp on approval, push to PR branch — no bypass needed, but our existing dismiss-stale-on-new-commits rule would dismiss the approval, requiring the reviewer to approve twice per regulated PR.
- Option 3: merge-queue stamping — uses GitHub’s merge queue. Boss approves once, no double approval. But “Require merge queue” applies to every PR targeting
main, adding ~13 min per merge for the whole team — the cost lands on PRs that don’t touchregulatory-docs/.
Option 1 won because it imposes friction only on regulated PRs, gives the cleanest audit semantics, and the bypass-scope concern is fully addressed by the dedicated-App pattern (only bioflow-autostamp can push to main, not every workflow).
Where it lives
Section titled “Where it lives”- Python implementation:
tools/auto_stamp/main.py— CLI entry pointstamper.py— reads.sdocfiles, computes hashes (re-using the post-processor’s_compute_content_hash), writes backpr_metadata.py—gh apiwrappers for fetching approvers + changed filescodeowners.py— CODEOWNERS parser + path-glob matchertests/—test_main_end_to_end.py(full chain) +test_pr_metadata.py(review-payload distillation)
- Workflow:
.github/workflows/auto-stamp.yml— runs onpull_request: closed, paths-filtered toregulatory-docs/**/*.sdoc - App identity:
bioflow-autostamp(created in the Corona-Project-Life org). See the setup playbook for the exact creation steps and field values used.
How to operate it
Section titled “How to operate it”CI (production)
Section titled “CI (production)”The workflow runs automatically when a regulated PR merges. Nothing manual.
Manual / emergency stamping
Section titled “Manual / emergency stamping”The workflow is also workflow_dispatch-triggerable. Use this to retroactively stamp a past PR or to retry after a failed automatic run:
- Go to Actions → Auto-stamp REVIEWED_HASH → Run workflow
- Enter the PR number to stamp
- Click Run workflow
The Action will fetch that PR’s approvers, validate them against CODEOWNERS, and stamp the current state of any .sdoc files the PR touched. Only use this when you understand exactly what content the stamping will record.
Local development
Section titled “Local development”Run the script against the live repo with fixture metadata (no GitHub API call):
uv run python -m tools.auto_stamp.main \ --pr 42 \ --mock-approver "@DougYoungberg" \ --mock-changed-files regulatory-docs/10_system.sdoc regulatory-docs/11_srs.sdoc regulatory-docs/20_architecture.sdoc regulatory-docs/31_integration_tests.sdoc \ --dry-run(--mock-changed-files accepts any subset of touched regulatory-docs/**/*.sdoc files (the three top-level spec tiers and any test spec under regulatory-docs/); the example above exercises all three spec tiers plus an integration-test spec.)
--dry-run reads metadata, validates the approver, and prints what would be stamped — but does not modify any files. Drop --dry-run to actually write.
Run the unit tests:
uv run pytest tools/auto_stamp/tests/ -p no:visiontrace(-p no:visiontrace silences VisionTrace’s autouse screen-recording fixture, which is irrelevant here.)
Versioning / update policy
Section titled “Versioning / update policy”- The auto-stamp tool is regulated software in its own right — a bug here directly produces wrong audit evidence
- CODEOWNERS-gated path:
tools/auto_stamp/**requires qualified review for any change - The hash function is shared with the post-processor (imported directly from
tools/post_processor/rtm.py) — keeping them in lockstep is mandatory; never re-implement the hash here - Tool qualification: a CSA package for the auto-stamp Action is on the regulated roadmap (alongside the post-processor’s qualification). Outlined in the skill file. Status: pending.
External documentation
Section titled “External documentation”Auto-stamp is internal; this page is its canonical documentation. See also:
- The auto-stamp App setup playbook — exact GitHub App creation steps
- The “How to add a new requirement” workflow — where auto-stamp fits in the round-trip
- The 21 CFR Part 11 signature strategy — how the auto-stamp
REVIEWED_HASHsatisfies §11.10