Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
b0cbf10
docs(spec): consolidate the calculus at v0.2 — one normative document…
claude Oct 13, 2026
44020f9
ci(proofs): pin Agda 2.7.0.1 + stdlib 2.0 by checksum; gate every Lea…
claude Oct 13, 2026
f230926
docs(status): rewrite PROOF-STATUS to the ratified world
claude Oct 27, 2026
444e4cf
docs(status): ROADMAP — ship v0.2, point open problems at spec §10
claude Oct 27, 2026
e59ef9c
docs(affirmation): add the 2026-10-27 entry from a full re-run
claude Oct 27, 2026
2cee670
proofs(agda): Reduction footer — O4 is resolved; the fix case waits o…
claude Oct 27, 2026
a595845
chore(proofs): remove the three never-instantiated prover templates
claude Oct 27, 2026
8b46a59
docs(adr): ADR 0005 — v0.2 consolidation, O5 adopted, O6 opened, prov…
claude Oct 27, 2026
0169a8c
docs(changelog): record the v0.2 consolidation and the Phase 0 pass
claude Oct 27, 2026
cb549c7
docs: axiom audit says Classical.choice in three theorems; cite the C…
claude Oct 27, 2026
3d907be
docs: qualify the gfp tier remark; bring PROOF-NEEDS' DOM rows to the…
claude Oct 27, 2026
70a8109
proofs: comment-aware Lean honesty gate; say which carrier each cited…
claude Oct 27, 2026
dade336
docs: clarify that grade algebra laws apply after erasing predicate a…
coderabbitai[bot] Oct 27, 2026
35745d8
fix(ci): run conformance checks in E2E tests and clarify Lean axiom a…
coderabbitai[bot] Oct 27, 2026
b726772
fix(ci): Run conformance tests via E2E entry points and reword Lean p…
coderabbitai[bot] Oct 27, 2026
9453337
Merge remote CI fixes and preserve E2E recipe integration
coderabbitai[bot] Oct 27, 2026
ef2ce42
fix(ci): repair startup-failing reusable pins and the estate-audit af…
hyperpolymath Oct 28, 2026
c6a2d5a
proofs(ci): strictest Agda flags; replace never-worked nanoda with a …
hyperpolymath Oct 28, 2026
a146ac2
ci: raise the plain checks to their strictest standard form
hyperpolymath Oct 28, 2026
a0f1f16
chore(proofs): remove the remaining orphaned template 'proofs'; repoi…
hyperpolymath Oct 28, 2026
d9f173e
fix(ci): round-2 repairs from first live run - lockfile own-SHA form,…
hyperpolymath Oct 28, 2026
b30203d
fix(ci): round-3 - cubical-clean Q-inj, single-line Lean seq-focus, s…
hyperpolymath Oct 28, 2026
a472e8c
Update .github/workflows/proofs.yml
hyperpolymath Oct 28, 2026
2556584
Update .github/workflows/trope-check.yml
hyperpolymath Oct 28, 2026
ea09bc7
docs(build): document just installation, Lean auditing, and Agda Qcod…
coderabbitai[bot] Oct 28, 2026
98e2fab
fix(ci): repair affirmation signature checks and Lean axiom auditing,…
coderabbitai[bot] Oct 28, 2026
695b5a9
fix(ci): resolve Rust lints and Agda/Lean proof failures; convert wik…
coderabbitai[bot] Oct 28, 2026
63c6404
fix(ci): replace Python regression tests with Bash and make affirmati…
coderabbitai[bot] Oct 28, 2026
6b06d5d
docs(rust,agda): document fate semantics, decoding behavior, CLI exit…
coderabbitai[bot] Oct 28, 2026
d1466ef
ci: fix review findings batch 1 — Sonar shell smells, job timeouts, a…
hyperpolymath Oct 28, 2026
7126f20
ci: group GITHUB_OUTPUT/SUMMARY redirects (SC2129 at the source, reti…
hyperpolymath Oct 28, 2026
982017a
ci(lean,lint): treat compiler specialisation constants as non-axioms …
hyperpolymath Oct 28, 2026
05554cd
governance: adopt the fork-governance verdict grid (DEED + K9-DEED co…
hyperpolymath Oct 28, 2026
2f7e52d
docs(wiki): update tropical factorisation verdict for the ratified ca…
coderabbitai[bot] Oct 28, 2026
ff399c9
fix(repo): Allow .gitbot-fleet/ in the root allowlist
coderabbitai[bot] Oct 28, 2026
bfb8168
governance: repair the silent scanner-lane outage — canonical standar…
hyperpolymath Oct 28, 2026
60e1600
ci: lock the new Patch Bridge workflow, and regression-test the moved…
hyperpolymath Oct 28, 2026
5838017
docs(adr-0006): record canary results — all green except the owner al…
hyperpolymath Oct 28, 2026
b174c84
fix: close the two review-confirmed soundness holes; repair the calcu…
hyperpolymath Oct 28, 2026
73f82b9
ci: canary push to verify the Actions allow-list now admits canonical…
hyperpolymath Oct 28, 2026
09b3e09
governance: grandfather the pre-contract K9 corpus in the shrink-only…
hyperpolymath Oct 28, 2026
afb9865
docs(adr-0006): record allow-list landing and first governance execut…
hyperpolymath Oct 28, 2026
389850b
chore(repo): add license headers to K9 debt ledger and correct setup …
coderabbitai[bot] Oct 28, 2026
f2e185e
chore(repo): add license headers to K9 debt ledger and correct setup …
coderabbitai[bot] Oct 28, 2026
9236fff
Merge remote branch head and preserve ledger and setup fixes
coderabbitai[bot] Oct 28, 2026
f7f4139
fix(ci): Separate fork governance publishing from PR scans and correc…
coderabbitai[bot] Oct 28, 2026
93a5a99
fix: deterministic §6.2 witness in both cores; clear the Codex/Hypati…
hyperpolymath Oct 28, 2026
de28b4b
fix: toolchain/lint-stable str_list form, fmt-canonical readiness clo…
hyperpolymath Oct 28, 2026
c89cdba
fix: rustfmt-canonical producer collect; harden-runner lock dependenc…
hyperpolymath Oct 28, 2026
68865eb
fix: key publisher concurrency on head SHA, not fork-controlled branc…
hyperpolymath Oct 29, 2026
62176b9
feat: structural graph validation in both cores (calculus MUST)
hyperpolymath Oct 29, 2026
ee32596
fix: rustfmt-canonical line in validate_graph
hyperpolymath Oct 29, 2026
34e0d63
fix: authenticate fork-governance results before publishing (Codex P1)
hyperpolymath Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .gitbot-fleet/enrolment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
Comment thread
hyperpolymath marked this conversation as resolved.
Resolved
"schema": 1,
"reporters": {
"echidnabot": { "enrolled": false, "reason": "proofs here are gated by their own toolchains (proofs.yml: Idris2, Lean, Agda); no ECHIDNA engine is configured, so this adapter could only ever report not_run" },
"rhodibot": { "title": "rhodibot (RSR compliance)", "enrolled": true, "gating": false },
"panicbot": { "enrolled": false, "reason": "panic-attack already reports directly as its own row in the Hypatia verdict grid; enrolling panicbot would count the same findings twice" },
"sustainabot": { "enrolled": false, "reason": "reserved fleet slot; no adapter exists (gitbot-fleet README)" },
"glambot": { "title": "glambot (presentation quality)", "enrolled": true, "gating": false },
"seambot": { "enrolled": false, "reason": "no seam register is declared (spec/seams/seam-register.json)" },
"finishingbot": { "title": "finishingbot (release readiness)", "enrolled": true, "gating": false },
"cipherbot": { "enrolled": false, "reason": "no cryptography is implemented in this repository; the hashes present are checksum/integrity pins, which cipherbot would report as cryptographic findings" },
"accessibilitybot": { "enrolled": false, "reason": "no served HTML surface; this is a checker library/CLI with AsciiDoc documentation" }
}
}
24 changes: 24 additions & 0 deletions .gitbot-fleet/report-backs/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <[REDACTED_EMAIL]>
= gitbot-fleet report-backs

One JSON envelope per enrolled fleet member (`.gitbot-fleet/enrolment.json`).
The Hypatia verdict grid reads this directory when it renders, so a
report-back here is the only way a member's row stops being orange. The
envelope contract is `docs/VERDICT-GRID-SPEC.adoc` in hyperpolymath/standards;
the producer is documented in gitbot-fleet's `docs/REPORT-BACK.adoc`.

*How the files get here.* The `fleet` job of
`.github/workflows/fork-governance.yml` (gitbot-fleet's
`fleet-report-back-reusable.yml`) runs the enrolled adapters on the PR head
and uploads the envelopes as the `fleet-report-backs` artifact; the Hypatia
lane downloads that artifact into this directory just before rendering.
Nothing is ever committed here: an envelope is evidence about one commit, and
a committed one would read as current when it is stale. The directory exists
because this repository's deed declares it
(`trope-checker_chora.deed`, `:report-backs ".gitbot-fleet/report-backs/"`),
and it is empty in git on purpose. An enrolled member with no report-back
renders *orange* ("enrolled, but no report-back was received"), never green;
do not add a placeholder envelope to turn a row green. Only a real run may.

Only files matching `*.json` are read, so this README is inert.
1 change: 1 addition & 0 deletions .githooks/validate-a2ml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ validate_a2ml() {
# --- Check 4: Section heading syntax ---
# Validate that [section] headings are well-formed (no unclosed brackets)
line_num=0
# shellcheck disable=SC2094 # report_issue annotates $file on stdout; it never writes it
while IFS= read -r line; do
line_num=$((line_num + 1))
# Lines starting with [ should have a matching ]
Expand Down
35 changes: 35 additions & 0 deletions .github/affirmation-trusted/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MPL-2.0
= Affirmation-check trust material

`github-web-flow.gpg` in this directory is GitHub's *current* web-flow
commit-signing public key:

* fingerprint: `9684 79A1 AFF9 27E3 7D1A 566B B569 0EEE BB95 2194`
* created 2024-01-16, no expiry
* fetched 2026-10-28 from `gh api users/web-flow/gpg_keys`
(the entry with `key_id` `B5690EEEBB952194`)

== Why it exists

The Affirmation Document Gate (`hyperpolymath/cicd-suite`
`actions/affirmation-check`, run from `main-estate-audit.yml`) classifies the
signature on the commit that last changed `AFFIRMATION.adoc`. The copy of the
web-flow key vendored inside that action is GitHub's *previous* key
(`5DE3E0509C47EA3CF04A42D34AEE18F83AFDEB23`, expired 2024-01-16) — the action's
own fingerprint allowlist names the new key, but the new key is not in its
keyring. Every GitHub-signed commit (web merges, squash landings, and the
test-merge commit a shallow PR checkout consists of) therefore classified as
`E` ("cannot be checked"), which the gate treats as "bad, revoked, or failed
verification" and fails on. That was the `estate-audit` failure on PR #102.

The workflow now runs `.github/affirmation-trusted/check.sh`, vendored from
`cicd-suite@bf7dd95cf49733019343ff485c2f45c6b261839c` with a local repair:
`E` accompanied by GnuPG's `NO_PUBKEY` is a notice; bad, revoked, and other failed
verifications still fail. The local script imports both the current and
historical vendored GitHub keys and rejects them unless their primary
fingerprints match the two pinned above. This is a contained workaround, not
the durable trust boundary: gate code in a pull-request checkout is necessarily
reviewed code, whereas the root fix belongs in `cicd-suite`, whose pinned
external action can then be restored. If GitHub rotates the web-flow key
again, re-fetch it the same way and update the fingerprint above in the same
commit.
6 changes: 6 additions & 0 deletions .github/affirmation-trusted/allowed_signers
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: MPL-2.0
# SSH commit-signing keys of the estate owner, from https://api.github.com/users/hyperpolymath/ssh_signing_keys
# (fetched 2026-10-02). Only the GitHub noreply principal is listed.
6759885+hyperpolymath@users.noreply.github.com namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOpGzhVGVlqEQuGus7hiS66b1PBWjel6ahCjBBLVDqCg
6759885+hyperpolymath@users.noreply.github.com namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBFX2cMllRUW/TjfJ9rHg6oALk7UfxzZrh7tDShhbLQ0
6759885+hyperpolymath@users.noreply.github.com namespaces="git" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBp4I5S7wV5b8xEUfcVI+E/9FQPoA2XO/9aMPfTOGa1p
222 changes: 222 additions & 0 deletions .github/affirmation-trusted/check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: MPL-2.0
# Vendored from hyperpolymath/cicd-suite@bf7dd95cf49733019343ff485c2f45c6b261839c
# Local repair: distinguish unavailable PGP keys from failed verification.

set -euo pipefail

root=${GITHUB_WORKSPACE:-.}
required=${AFFIRMATION_REQUIRED:-false}
aff_file=

for candidate in AFFIRMATION.adoc AFFIRMATION.md AFFIRMATION; do
if [[ -f "$root/$candidate" ]]; then
aff_file=$candidate
break
fi
done

if [[ -z "$aff_file" ]]; then
if [[ "$required" == "true" ]]; then
echo "::error::AFFIRMATION.adoc is required by the declared governance-tier capability." >&2
exit 1
fi
echo "::notice::AFFIRMATION is not applicable: governance-tier was not required."
exit 0
fi

path=$root/$aff_file
echo "Found AFFIRMATION document: $aff_file"

substantive_lines=$(awk '!/^[[:space:]]*(#|\/\/|;|$)/ { count++ } END { print count + 0 }' "$path")
if (( substantive_lines < 5 )); then
echo "::error::$aff_file has only $substantive_lines substantive lines; it is a stub, not an affirmation." >&2
exit 1
fi

# An inline code span (`...`) quotes text rather than asserting it: an
# affirmation may name the placeholder syntax it swept for, e.g. "`{{PROJECT}}`
# tokens were substituted". Strip code spans before matching so that only
# placeholders left in the prose itself count.
# shellcheck disable=SC2016 # Backticks are literal AsciiDoc code-span delimiters.
placeholders=$(sed -E 's/`[^`]*`//g' "$path" |
grep -niE '\{\{|TODO: update|<PROJECT|YOUR_PROJECT|lorem ipsum|example\.com' || true)
if [[ -n "$placeholders" ]]; then
echo "::error::$aff_file contains template placeholders." >&2
printf '%s\n' "$placeholders" | head -5
exit 1
fi

# The signature is the signature on the commit containing this content. Text
# such as "Signed:" inside the document proves nothing. Shallow checkouts may
# not contain the file-changing commit, so report that limitation honestly.
#
# Git can only classify a signature whose key it holds; without the key it
# reports E; the raw status below distinguishes NO_PUBKEY from a broken check.
# Merges made on
# github.com (squash, merge, web edits) are signed by GitHub's web-flow key,
# so that key is vendored here and trusted inside a throwaway keyring. The
# estate owner's SSH signing keys are vendored as allowed_signers. Trust comes
# only from these files and from what the caller names explicitly — never from
# the checked-out tree by default, because on a pull request that tree is the
# contributor's. Callers may add PGP keys (AFFIRMATION_TRUSTED_GPG_KEYS, one
# armoured file) and SSH signers (AFFIRMATION_ALLOWED_SIGNERS, a git
# allowed_signers file); relative paths are resolved against the repository
# root, so name such a file only where the tree is protected.
here=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
github_fingerprints=" 968479A1AFF927E37D1A566BB5690EEEBB952194 5DE3E0509C47EA3CF04A42D34AEE18F83AFDEB23 "
vendored_gpg_keys=("$here/github-web-flow.gpg" "$here/github-web-flow-previous.gpg")
trusted_gpg_keys=()
if [[ -n "${AFFIRMATION_TRUSTED_GPG_KEYS:-}" ]]; then
extra_keys=$AFFIRMATION_TRUSTED_GPG_KEYS
[[ "$extra_keys" == /* ]] || extra_keys=$root/$extra_keys
trusted_gpg_keys+=("$extra_keys")
fi
signer_files=("$here/allowed_signers")
if [[ -n "${AFFIRMATION_ALLOWED_SIGNERS:-}" ]]; then
extra_signers=$AFFIRMATION_ALLOWED_SIGNERS
[[ "$extra_signers" == /* ]] || extra_signers=$root/$extra_signers
signer_files+=("$extra_signers")
fi

# Build a private GnuPG home holding only the trusted keys, each given
# ultimate owner trust, and print its path. If a key file cannot be imported
# the home is left empty, so nothing is trusted — the runner's own keyring is
# never consulted. Prints nothing only if gpg is absent.
primary_fingerprints_in_home() {
local keyring_home=$1
gpg --batch --homedir "$keyring_home" --with-colons --fingerprint 2>/dev/null \
| awk -F: '$1 == "pub" { primary = 1; next } primary && $1 == "fpr" { print $10; primary = 0 }'
}

make_trusted_gnupg_home() {
local keyring_home key_file imported_fpr
local -a imported_fprs=()
command -v gpg >/dev/null 2>&1 || return 0
keyring_home=$(mktemp -d)
chmod 700 "$keyring_home"
for key_file in "${vendored_gpg_keys[@]}"; do
if [[ ! -s "$key_file" ]] \
|| ! gpg --batch --quiet --homedir "$keyring_home" --import "$key_file" >/dev/null 2>&1; then
echo "::warning::Trusted PGP key file could not be imported: $key_file" >&2
rm -rf -- "$keyring_home"
keyring_home=$(mktemp -d)
chmod 700 "$keyring_home"
printf '%s\n' "$keyring_home"
return 0
fi
done
mapfile -t imported_fprs < <(primary_fingerprints_in_home "$keyring_home")
if ((${#imported_fprs[@]} != ${#vendored_gpg_keys[@]})); then
echo "::error::Vendored GitHub web-flow key material did not import exactly the expected keys." >&2
rm -rf -- "$keyring_home"
return 1
fi
for imported_fpr in "${imported_fprs[@]}"; do
if [[ "$github_fingerprints" != *" $imported_fpr "* ]]; then
echo "::error::Vendored GitHub web-flow key fingerprint is not pinned: $imported_fpr" >&2
rm -rf -- "$keyring_home"
return 1
fi
done
for key_file in "${trusted_gpg_keys[@]}"; do
if [[ ! -s "$key_file" ]] \
|| ! gpg --batch --quiet --homedir "$keyring_home" --import "$key_file" >/dev/null 2>&1; then
echo "::warning::Trusted PGP key file could not be imported: $key_file" >&2
rm -rf -- "$keyring_home"
keyring_home=$(mktemp -d)
chmod 700 "$keyring_home"
printf '%s\n' "$keyring_home"
return 0
fi
done
gpg --batch --homedir "$keyring_home" --with-colons --fingerprint 2>/dev/null \
| awk -F: '$1 == "fpr" { print $10 ":6:" }' \
| gpg --batch --quiet --homedir "$keyring_home" --import-ownertrust >/dev/null 2>&1 || true
printf '%s\n' "$keyring_home"
}

signature=N
signer=
primary_fpr=
last_update_ts=
if git -C "$root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
gnupg_home=$(make_trusted_gnupg_home)
[[ -n "$gnupg_home" ]] || echo "::notice::gpg is not installed; PGP-signed commits cannot be checked."
# Git takes one allowed_signers file, so the vendored and caller-named files
# are joined. Without any file Git reports an SSH signature as N (no
# signature) instead of U (valid, signer not trusted).
allowed_signers=$(mktemp)
for signers_file in "${signer_files[@]}"; do
if [[ -s "$signers_file" ]]; then
cat -- "$signers_file" >> "$allowed_signers"
else
echo "::warning::Allowed signers file could not be read: $signers_file"
fi
done
git_verify=(git -C "$root" -c "gpg.ssh.allowedSignersFile=$allowed_signers")
if [[ -n "$gnupg_home" ]]; then
git_verify=(env "GNUPGHOME=$gnupg_home" "${git_verify[@]}")
fi
sig_line=$("${git_verify[@]}" log -1 --format='%G?%x09%GP%x09%GS' -- "$aff_file" 2>/dev/null || true)
# Split on tabs by hand: `read` would merge the empty %GP of an SSH signature.
if [[ "$sig_line" == *$'\t'*$'\t'* ]]; then
signature=${sig_line%%$'\t'*}
sig_rest=${sig_line#*$'\t'}
primary_fpr=${sig_rest%%$'\t'*}
signer=${sig_rest#*$'\t'}
fi
if [[ "$signature" == E ]]; then
aff_commit=$(git -C "$root" log -1 --format='%H' -- "$aff_file")
verify_status=$("${git_verify[@]}" verify-commit --raw "$aff_commit" 2>&1 || true)
# ERRSIG reason 9 means a missing public key. GnuPG also emits its
# generic gpg-exit failure in this case; other errors must still fail.
if grep -qE '^\[GNUPG:\] NO_PUBKEY [0-9A-F]+$' <<<"$verify_status" \
&& grep -qE '^\[GNUPG:\] ERRSIG [0-9A-F]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ 9( [0-9A-F]+)?$' <<<"$verify_status" \
&& ! grep -vE '^\[GNUPG:\] FAILURE gpg-exit [0-9]+$' <<<"$verify_status" \
| grep -E '^\[GNUPG:\] (BADSIG|REVKEYSIG|KEYREVOKED|EXPSIG|EXPKEYSIG|FAILURE|ERROR|NODATA|BADARMOR)( |$)' >/dev/null; then
signature=K
fi
fi
last_update_ts=$(git -C "$root" log -1 --format='%at' -- "$aff_file" 2>/dev/null || true)
# In a shallow clone the boundary commit appears to add every file, so the
# commit found above may only be the boundary, not the affirmation commit.
if [[ "$(git -C "$root" rev-parse --is-shallow-repository 2>/dev/null || true)" == true ]]; then
aff_commit=$(git -C "$root" log -1 --format='%H' -- "$aff_file" 2>/dev/null || true)
shallow_file=$(git -C "$root" rev-parse --path-format=absolute --git-path shallow 2>/dev/null || true)
if [[ -n "$aff_commit" && -f "$shallow_file" ]] && grep -qxF "$aff_commit" "$shallow_file"; then
echo "::notice::The checkout is shallow: $aff_commit is the history boundary, not necessarily the commit that last changed $aff_file. Fetch more history to check that commit's signature."
fi
fi
if [[ -n "$gnupg_home" ]]; then
rm -rf -- "$gnupg_home"
fi
rm -f -- "$allowed_signers"
fi

case "$signature" in
G) echo "Affirmation commit signature verified with a trusted key: ${signer:-unknown signer}."
if [[ -n "$primary_fpr" && "$github_fingerprints" == *" $primary_fpr "* ]]; then
echo "::notice::GitHub signed this commit when it landed through github.com; an author signature, if any, is on the pull request's head commit."
fi
;;
U) echo "::notice::Affirmation commit has a valid signature from an untrusted or locally unknown key${signer:+: $signer}." ;;
K) echo "::notice::Affirmation commit signature cannot be checked: public key unavailable." ;;
B|R|E) echo "::error::Affirmation commit signature is bad, revoked, or failed verification." >&2; exit 1 ;;
*) echo "::notice::Affirmation commit signature could not be verified from the available Git history." ;;
esac

# A dated affirmation is a frozen receipt, not a claim that remains current
# forever. Report age without invalidating historical evidence or forcing an
# empty monthly rewrite.
if [[ -n "$last_update_ts" ]]; then
current_ts=$(date +%s)
age_days=$(( (current_ts - last_update_ts) / 86400 ))
if (( age_days > 28 )); then
echo "::warning::$aff_file is a $age_days-day-old snapshot; verify its anchor before relying on it as current."
else
echo "$aff_file snapshot age: $age_days days."
fi
fi

echo "AFFIRMATION document validation passed."
41 changes: 41 additions & 0 deletions .github/affirmation-trusted/github-web-flow-previous.gpg
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----

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
=uMz0
-----END PGP PUBLIC KEY BLOCK-----
Loading
Loading