This file is the formal release-blocker controlled by V1/TODOS.md §28.20. A GA
release is not authorised unless every cell below is filled in with a real
engagement record. CI parses this file (see
scripts/security/pentest/verify-signoff.mjs) and rejects a release tag when
any of the required fields are missing or stale.
Engagement record schema#
Each engagement adds a new row to the table below. Rows are append-only — never edit a past row. The fields:
| Field | Notes |
|---|---|
engagement_id |
Vendor's reference for the engagement (e.g. ACME-2026-04). |
scope_version |
Version of pre-ga-scope.md the engagement was run against. |
window_start |
ISO date the active testing window opened. |
window_end |
ISO date the active testing window closed. |
findings_count |
critical/high/medium/low/info counts at engagement close. |
open_at_release |
Count still open at the release tag (must be 0 critical, ≤1 high, no AuthN/RBAC/privacy regardless of severity). |
release_tag |
The release this engagement signs off. Must match the Git tag being cut, e.g. vN.M.P. |
signoff_sec |
Name + ISO date – head of security. |
signoff_eng |
Name + ISO date – head of engineering. |
signoff_priv |
Name + ISO date – head of privacy (required if any privacy-tagged finding). |
Engagements#
| engagement_id | scope_version | window_start | window_end | findings_count | open_at_release | release_tag | signoff_sec | signoff_eng | signoff_priv |
|---|---|---|---|---|---|---|---|---|---|
| none yet | – | – | – | – | – | – | – | – | – |
Release gate rules#
A release tag is valid only if the most recent engagement row satisfies all of the following:
release_tagexactly matches the tag being cut.open_at_release.critical == 0.open_at_release.high <= 1.- No open finding in the AuthN, RBAC, or privacy categories regardless of
severity. This cannot be read off the row, so it is carried by a mandatory
tags-summary:footer below the table (see "Category footer"). A missing, empty, or incomplete footer fails the gate — absence is not a clean result. signoff_secandsignoff_engare non-empty.signoff_privis required whenever the footer declares any privacy state other thanprivacy:none.- Each non-empty signoff date is within 14 days of
window_end. The gate runs while the tag is being cut and so cannot read a tag date;window_endis the anchor it enforces, and it is what keeps a signature from being older than the engagement it signs.
Failing any rule above prints the failing rule to stderr and exits with a non-zero code — the release pipeline then refuses to flip the traffic gate.
Category footer#
Every engagement row must be followed by one machine-readable line declaring a state for each of the three blocking categories:
tags-summary: authn:closed, rbac:closed, privacy:none
Permitted states are open, closed, and none (no finding in that category
was raised). <category>:open blocks the release under rule 4. Any privacy
state other than privacy:none makes signoff_priv mandatory under rule 5.
Red-team exercises#
See ../red-team/scenarios.md for the matching red-team scenario set; the
red-team result file references this record by engagement_id.