Release evidence¶
This workflow separates candidate history, already-shipped work, and behavior present at release head. Use native Git for local evidence, packaged github_read.py for GitHub reads. No sibling plugin, default branch name, installed extra, network fetch, or live credential availability assumed.
Pin scope before classification¶
- Record current branch, exact head SHA, working-tree changes, shallow status, relevant project/component, version convention, release channel. An explicit range's head is authoritative, even when it differs from checked-out HEAD; inspect source with
git show <head>:<path>or another verified snapshot. Never silently incorporate unrelated worktree changes. Source inspection alone doesn't bind executable checks: use clean checkout/worktree at pinned commit for execution, verify import/environment targets, pass--expected-headto gate runner as required by step 06. Preserve caller's checkout and edits; never certify uncommitted changes as part of pinned commit. - Honor supplied range. Otherwise inspect local tags and first-parent ancestry to identify previous release for this component/channel. A prerelease isn't automatically the baseline for a stable release. Never use globally largest version or most recent tag from an unrelated maintenance branch. Record why chosen tag represents what these users already received. A tag alone doesn't prove remote publication; label local-only evidence or use already-authorized release metadata.
- When relevant published tag is off-branch, inspect
git merge-base <head> <release-tag>and branch/tag ancestry. Use common ancestor as candidate lower bound, retain actual release tag separately, compare released history against candidate patches. Multiple plausible baselines, unrelated histories, incomplete/shallow history, missing refs or failed commands are evidence gaps: stop scope-dependent writing until resolved. Never fetch automatically without owning command's network approval. - For a verified initial release with no earlier release, enumerate all reachable commits including root (
git rev-list <head>), compare against Git's empty tree when diff needed.root..headexcludes initial commit, isn't full history. Confirm initial-release intent when local tag absence could mean missing history. An empty delta may be a valid no-new-changes audit, but can't be described as a new substantive release. - Save
release-scope.mdsectionsHead and baseline,Released comparison,Candidate accounting,Limits. Include exact SHAs, local/live evidence freshness, component/channel reasoning, explicit-range interpretation, candidate/included/excluded/unresolved counts. Reconcile counts before drafting.
Commands must use argument arrays or safe literal quoting. Check exit status; never convert command failure into an empty set. Keep evidence-producing Git output lossless even when terminal summaries are compressed.
Establish already-released membership¶
- Enumerate all commits reachable from pinned head but not candidate baseline. Include merge commits in inventory: first-parent-only or
--no-mergesenumeration can omit conflict-resolution behavior and merged-in changes. Inspect merge effects against parents, avoid double-counting constituent commits. - Reachability from relevant released tag proves shared commit identity. For divergent/rebased/cherry-picked history use
git cherry <released-tag> <head> <candidate-base>or stable patch IDs from complete diffs; retain both candidate and matching released SHA with supporting output. Compare only releases relevant to this release line, not every tag in repository. A feature shipped on a newer line can still be new to an older supported line. - Patch equivalence is supporting evidence, not a net-state oracle. Confirm equivalent change remains present in released tree: a patch introduced then reverted before that release wasn't shipped as active behavior. Merge commits, empty commits, squash combinations, manual reverts and context-modified backports require explicit diff/tree inspection; no exact patch match doesn't prove novelty. Never subtract based only on matching commit subjects, PR titles, timestamps, labels, or a PR being merged elsewhere.
- Find associated PRs from candidate commit evidence and, when needed, repository's commit-associated PR endpoint through
github_read.py; retrieve every page, record unavailable/truncated results. Restrict association by repository, actual commits/diffs, release head. Default-branch merged-PR lists are neither complete for maintenance branches nor proof a PR landed here. Local-only work uses commit links and verified names with explicit metadata limits. - Read full messages, PR bodies, significant diffs. Preserve
BREAKING CHANGE:signals, but verify actual compatibility and project SemVer policy. Deduplicate by implemented feature/change while retaining all contributing PRs and commits. Keep omission reason for internal-only work; those contributors still count. - Detect revert chains using original commit identity/trailers and final tree state, not title matches. Added then fully reverted before shipment contributes no active feature or migration. A revert of previously shipped behavior has user impact, may be breaking. Partial reverts, reapplications, and pivots need final-state classification. Check highlights, summaries, examples against same final state.
Every candidate row in change-table.md needs a disposition: included, already released, internal-only, reverted/superseded, merge-only with no unique effect, or unresolved. Exclusions retain evidence, not just labels. Unresolved membership blocks a complete-coverage claim. Record breakingness separately from category: prior deprecation doesn't automatically make removal SemVer-compatible; cite project's published deprecation/removal policy, give migration guidance.
Account for contributors independently of prose filtering¶
Collect authors from included release work, including documentation, tests, maintenance, reverted-in-range work. Inspect merge-only contributions and Co-authored-by trailers. Reconcile PR author identity for squash/merge commits; merge committer isn't automatically the contributor. Already-released-only contributors are excluded unless they also contributed new work in scope.
Use mailmap-aware identities (%aN, %aE, git check-mailmap) and verified account associations. Never collapse unrelated people by display name alone. Deduplicate aliases with evidence; retain ambiguous identities for resolution rather than dropping them. A human users.noreply.github.com address isn't a bot. Separate automation from human credits only with verified account/Git bot identity or documented project policy; retain every bot identity and its classification evidence for aggregate credit rather than discarding it. Keep email addresses only in <run-directory>/private/, and only after git check-ignore -q <run-directory>/private/ confirms Git ignores it; when it does not, write no raw email to disk and keep identity hashes only. Never put email addresses in a public draft or tracked report.
Write contributors.md sections Coverage, Credits, Exclusions, Unresolved. Reconcile each relevant author/coauthor/PR author with a public credit or evidenced exclusion, including people whose work has no notable-change bullet. Credit each human once, with up to three concrete contributions; no PR-number-only descriptions. Use a clear Markdown credit containing the verified display name, optional verified handle/profile link, and contribution; equivalent emphasis or link syntax is acceptable. Preserve exact identity, credit counts, and excerpt text in evidence records. Unresolved handles keep the verified name. Never invent handles, affiliations or social links.
Optional LinkedIn links follow verified identity only: GitHub social accounts, an explicitly linked profile/blog, an unambiguous link on that person's linked page, or an earlier credit for exact verified GitHub handle. Never search/match by name or guess a URL. Missing optional profile evidence can't justify omitting the contributor. Never perform profile enrichment when user requests local-only work.
Audit the changelog without pruning¶
Discover canonical project changelog, read its current target/Unreleased section, historical version boundaries, reference links, existing release artifacts. Capture exact pre-edit bytes outside active source files. Match project conventions; a draft release page and a cumulative changelog have different jobs.
Write changelog-audit.md sections Scope, Preservation, Added and flagged, Limits. Reconcile release changes against target section; detect already-shipped entries accidentally imported by branch merges. Add missing items, flag uncertain extras, preserve every older version and its links. ATX and Setext Markdown headings are equivalent when their level and heading text match; preserve the exact target heading text and all historical bytes. Never rewrite changelog into only current release, drop historical headings, or silently replace detailed entries with short draft bullets.
Preserve technical substance in existing entries: affected APIs/components, conditions, before/after behavior, compatibility, caveats, significant measurements, provenance. Correct only facts disproved by current evidence; cite that correction. Rephrasing allowed when meaning stays complete; detailed notes can stay in changelog while draft gives concise explanation. When selecting only some Unreleased items for a release, leave unrelated items under Unreleased. A release-local CHANGELOG.md is a target-version excerpt, never a replacement for canonical file.
Verify exact historical bytes and reference definitions against saved pre-edit version, accounting only for explicitly authorized additions to comparison links. Review target section semantically for preserved information. Report additions, discrepancies, scope exclusions, corrections separately. Missing/unreadable baseline is a preservation gap, not permission to regenerate.
Record the validation receipt¶
For every passing release_contract_version=1 result, add metadata.release_evidence with schema_version: 1. Record the absolute local repository, pinned head's final_tree, optional baseline and released head, and every exact candidate SHA with disposition and reason. Repeat the pinned head, tree, baseline when present, and every candidate SHA in release-scope.md; headings alone are insufficient. An already-released row names a reachable released_sha and uses either exact identity or nonempty stable-patch comparison; keep the supporting review output in release-scope.md. Each included SHA needs a claim record naming retained artifact, exact excerpt, artifact digest, and contributing candidate SHAs. Record normalized identity hashes and credited/excluded dispositions for every eligible author, coauthor, and retained PR author; record known PR authors in pr_authors by candidate SHA. Keep raw emails only in ignored private evidence.
For every already-released subtraction, record published_tree and published_paths rows containing literal path, blob sha256, mode, and object_type. Deleted paths use null digest/mode/type. Gitlinks compare actual object identity as well as mode/type. The validator requires exact candidate/published entries, so an introduced-then-reverted patch is not treated as shipped. This is deliberately conservative: later same-file edits preserving behavior still fail closed when bytes differ. Report unresolved equivalence rather than bypassing the check.
Record every selected deliverable's retained name, final absolute destination, and shared SHA-256. Claims use nonempty exact excerpts, a retained artifact basename, and at least one included candidate; never use a path outside deliverables/. A credited contributor record includes an exact public credit excerpt; an excluded contributor records an exact exclusion reason in contributors.md and one supported exclusion_basis: legal-restriction, privacy-request, or verified-duplicate. The changelog record binds immutable pre-edit backup and canonical final destination/digests; every historical section and reference definition must survive byte-for-byte unless an authorized correction is separately blocked for review.
Exact receipt fields¶
Credit identity comes from the complete visible name label, normalized before exact comparison. Balanced partial and nested emphasis and inline/full-reference/collapsed-reference/shortcut links are supported; shared closing-marker runs must preserve nested emphasis semantics and unmatched markers remain literal name text. An emphasized substring inside a longer linked label is not the verified person. Resolve references from complete definitions in the same actual artifact, at Markdown paragraph boundaries and outside fenced examples; the retained excerpt may omit those definitions. Link destinations must have balanced syntax and any optional title must be quoted or parenthesized, including valid continued titles; unquoted trailing words do not form a definition or link. Definitions alone and fenced example names cannot supply credit. A separate parenthesized handle is an annotation, not part of the linked name label. The same identity reader rejects equivalent individual bot credits. Preserve excerpt bytes and artifact hashes; syntax recognition does not authenticate a link destination or waive existing profile-provenance review.
- Contributors:
identity_hash, deriveddisplay_name,disposition, andcredit_excerptcontaining the exact credit excerpt from the actual public output (orcontributors.mdfor audit/demo-only), with the derived display name preserved under equivalent Markdown emphasis or link syntax. A linked name must have the exact verified display name as its visible label; a longer or different name cannot supply credit through a substring. Link recognition does not authenticate its destination or waive the existing optional-profile provenance review. Hash UTF-8name + NUL + casefold(email)after mailmap handling. Retainedpr_authorsrows contain candidatesha,identity_hash, anddisplay_name. Already-released-only work isn't new contributor work. Resolve conflicting names explicitly; never silently merge aliases. - Bots: separate
botsarray (empty or omitted only when none contributed). Each row contains eligibleidentity_hash, exact deriveddisplay_name, verified publiclabel, andclassification_evidencewith run-containedpath,sha256, exact nonemptyexcerpt. Evidence must establish both bot classification and any handle-to-identity mapping through Git bot identity, GitHub account type, or documented project policy; a human privacy email never suffices. Setis_bot: trueon known botpr_authorsrows; an optionalis_botmust be boolean. Git/PR names containing[bot]and retained known PR bots require a matching bot row. Humancontributorsandbotsare disjoint and jointly cover every eligible identity. InDRAFT.md(orcontributors.mdwithout a draft), emit exactly one*Automated contributions: <labels>*line using sorted unique labels joined by comma-space; labels contain no newline, comma or asterisk. Never put bot identities in individual human credit lines. Proof-byte binding is mechanical; the semantic reviewer verifies classification/alias truth, including known bots without[bot]names. - Exclusions:
exclusion_basis, exactexclusionreason incontributors.md, andexclusion_evidencewith run-containedpath,sha256, and exact nonemptyexcerpt. Retain underlying permission/alias evidence privately. The reviewer checks its truth; a supported label alone is insufficient. - Changelog: absolute
backupand canonicaldestination,backup_sha256,final_sha256, exact level-twotarget_headingtext. The target selector may use ATX or Setext syntax for the same heading text; canonical## <version>selectors also identify a Setext version section. Required sections are unique and outside fenced examples. Extract from original character spans before encoding UTF-8, preserving CRLF and multibyte text; never apply character offsets directly to bytes. Historical sections/reference definitions remain byte-exact, including their heading syntax. Existing target/Unreleased detail stays, while additions and movement between those sections are allowed. Removing/rewriting existing detail remains blocked until scope is reconciled; never relabel history or weaken a digest to hide deletion. - Changelog excerpt: complete final target section, trim trailing blank lines and append LF; append canonical reference definitions not already inside it, separated by one blank line and ending with LF. Preserve internal section bytes. Without a canonical changelog, record null
backup/destinationand nonemptyabsent_reason; any selected excerpt still needs meaningfultarget_headingand content. Audit binds read-only observations without product writes.
PR discovery completeness, alias truth, prose accuracy, and absence of an undiscovered custom changelog remain explicit parent/reviewer duties. Typed records do not prove those semantic claims.
Record a demo execution¶
Invoke PLUGIN_ROOT/bin/python PLUGIN_ROOT/shared/release_evidence.py record-demo --script <absolute-script> --cwd <project-directory> --environment <environment-description> --python <project-interpreter> --out <new-evidence-directory>. Always pass --python with the project environment's interpreter: without it the recorder runs the demo under the launcher's own Python, not the project environment. Quote arguments for the host shell and inspect --help first. Review the script and obtain any required execution/network authority before invoking it. The recorder never installs dependencies, edits settings, or grants network access. --timeout-seconds defaults to 600 and cannot exceed 600. Existing output directories are rejected; no automatic retry.
Copy the produced demo.json object into metadata.release_evidence.demo. It binds script digest, argv-array command, interpreter path, operator-supplied environment description, cwd, exit code, output path/digest, and execution receipt path/digest. execution.json additionally records script_sha256_after; passing validation requires before/after digests equal the selected final script. Failed/timed-out runs retain evidence but cannot pass; the CLI exits nonzero. Empty output is valid for successful silent demos. Validation is read-only and never executes demo code. Records are parent-observed execution evidence, not cryptographic attestation or a dependency lockfile.