Repository navigation
ci(pages): restore safe continuous GitHub Pages deployment-record pruning #1028
Copy link
Copy link
Open
Description
Purpose / owner
Restore permanent, automatic and bounded GitHub Pages deployment-record retention for qnbs/WorldScript-Studio. The intended steady-state policy is retain the newest 3 github-pages deployment records, with a conservative safety surplus if an active/current/rollback/evidence-bound record cannot be proven stale. Frequent main merges must not accumulate unlimited old GitHub Pages deployment records.
This issue only owns GitHub Actions' GitHub deployment records. It does not own Vercel deployments, Vercel Preview retention, release/tag publishing or historic privacy deletion. Those boundaries are tracked separately by #627 / QNB-67.
Verified starting point — 2026-10-12
- Source:
.github/workflows/prune-deployments.ymlcurrently usesworkflow_runon every completed CI / CD, Sunday cron 03:00 UTC and manual dispatch. It paginates all deployment records, groups by all environments and deletes beyond three per environment, including non-Pages environments.createDeploymentStatus(inactive)failures are logged but deletion is still attempted. - The intended operational workflow remains manually disabled after a stale GitHub Actions run incident. The specific older run
37655530284now readsstatus=completed, conclusion=cancelled, run_attempt=1, updated_at=2026-10-12T18:17:49Z, with zero jobs; current queued/in-progress repo collections were both zero. This reconciles visible lifecycle state, not historical proof of no execution. .github/workflows/ci.ymldeploys Pages under thegithub-pagesenvironment from successfulmainCI. A recent PR CI (38144396221) is SUCCESS; its Pages deployment job was legitimately skipped on a PR.- Current workflow policy checker's job write allowlist already grants
deployments:writetoprune-deployments.yml/prune. Preserve/strengthen least privilege and trusted-main executable authority. - Separate GitHub Support communications and historical deployment-record evidence must remain protected; no case number or confidential support text in public CI logs.
Implementation — one narrow independently reviewable PR, local Codex CLI
- Scope deletions only to positively classified
github-pagesrecords; do not mutateProduction,Preview, Vercel provider objects, unknown environments, records with missing/ambiguous metadata, or unresolved evidentiary holds. - Preserve newest 3 records by deterministic ordering, and additionally protect the exact active/current Pages deployment, any currently required rollback/evidence records. Fail closed on inability to establish positive ownership, pagination completeness, active identity, or safe retention floor. No fixed KEEP threshold overrides current-production protection.
- Restrict
workflow_runto legitimate successfulCI / CDmainpush events with strict payload/repository/head/conclusion checks; reject PR/tag/fork/manual CI, failure/cancelled/unknown. Keep scheduled reconciliation for backlog. For manual dispatch default to read-only dry run; require separate explicitly reviewed operation admission for deletion. Do not misusepull_request_target. - Add suitable concurrency serialization so two pruning writers cannot act on stale inventories. Bound operations per invocation and API rate/error handling; revalidate each candidate before mutation and avoid touching records concurrently replaced or promoted.
- If marking a deployment inactive fails, do not attempt deletion for it; preserve errors and stop or skip safely with an accurately surfaced failure/partial result, never a false all-green cleanup claim. Explicit handling when partial writes have occurred.
- Pure/testable classification and deterministic negative tests: PR main/fork/tag/failure, zero/1/2/3/N records, pagination truncation, active and rollback, unknown environment/status, tied timestamps/ID ordering, status API failure, 404/409/429/5xx, race/revalidation, cap/backlog, zero-delete dry-run, partial failure, repeat/idempotent pass. Include dry-run manifest and sanitized summary with count/IDs only where appropriate (no emails/secrets).
- One CI-trigger optimization benchmark: event counts before/after, number of avoidable prune starts, estimated runner minutes, and evidence all required product CI/CodeQL/security/signature gates are unchanged. Do not broaden to unrelated CI changes.
- Read
AGENTS.md,docs/PR-CI-MERGE-WORKFLOW.mdand trusted checker/tests. Local signed commits/hook/prepush/pr-budget, 3-channel review, exact-head independent substantive reviewer and required GitHub CI+CodeQL. No direct-main work, no admin bypass, no self-authorized merge. - Do not automatically reactivate the disabled workflow from the PR, CLI or GitHub UI. Reactivation is a separately approved protected operator action, only when a reviewed merged implementation and exact-main CI/CodeQL are green, a safe read-only dry run is recorded and the historical evidence hold/Support constraints have been dispositioned. Verify fresh state after a manual switch, then watch the first permitted invocation and count/protection invariants.
Links / exclusions
- CI efficiency parent/measurement: #550 / QNB-41. Only record measured deduplication here; do not remove Node 22/24, Playwright, Browser Quality, security, CodeQL, signatures, production proof or required check gates.
- Privileged workflow governance is not included: #510 / QNB-33, #506, #875. Preserve their independent ownership and existing protection.
- Historical Pages main-deploy skip #522 is closed; do not reopen without a new main-push deployment regression.
- Active R-15 #445 / PR #1027 remains outside this slice. No Gate 5/6/7, release or production authority switch.
- Incident/runbook #723 / QNB-81: private technical Support communication is kept separate from public issue content. No PII or tickets reproduced.
Exit criteria
- Tested narrow patch + new Ready-for-Review PR + signed commits; all exact-head required checks/reviews complete.
- At least one non-destructive dry-run proof against the postmerge current GitHub state, including counts and protected active Pages ID, without actually deleting any records.
- Independently confirmed GitHub Pages retention behavior on the first separately authorized real run (or, if not authorized, status remains
IMPLEMENTED_NOT_ENABLEDinstead of mislabelled Done). - GitHub issue and linked Linear ticket kept synchronized after each meaningful checkpoint; no duplicate new owner.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
qnbs commentedon Oct 12, 2026
Coordination checkpoint — 2026-10-12
The dedicated Linear mirror could not be created because the free workspace has reached its issue limit. No QNB issue ID has been allocated for #1028. Temporary cross-reference: QNB-41, comment dated 2026-10-12. #1028 remains the sole specialist GitHub implementation owner; QNB-41/#550 owns only pipeline-wide efficiency measurement. Do not close, repurpose or delete existing Linear issues just to create a mirror.
Local Codex CLI should re-fetch #1028, #550, #510, #506, #627 and #723 before source changes; synchronize progress in #1028 and a concise linked QNB-41 note. Scope remains bounded; no new review/CI/release policy or Vercel changes and no automatic workflow enablement.
qnbs commentedon Oct 13, 2026
SUPERSEDING MAINTAINER DECISION — 2026-10-13 12:45 CEST — #1029 MERGED; NO META-PR
PR #1029 merged normally to protected main
35a7866529779d8a4e8a67a48fbc6a85b02ca386. The old instruction below to rescue/split an open saturated #1029 is now historical, not executable. The maintainer explicitly cancels the previously suggested separate anti-cascade governance/policy/tooling/tests PR. Do not open a new PR, edit AGENTS.md or budget/prepush scripts, add a workflow/check, or otherwise increase governance churn merely to implement recurrence prevention. Existing 15/3000/30 budget controls and SATURATED successor policy are adequate; apply them intelligently for future actual code PRs without new metaconditions.Finish exact resulting-main CI/CD+CodeQL and normal Claude Vercel safe retention for #1029; then directly continue #1027 / R-15 Gate4 and critical v1.30.0 path. If a future actual PR reaches its cap with substantive unresolved findings, never merge unsafe code or add commit 16; follow existing safe successor rules within delegated scope, otherwise pause only that disallowed action and continue admitted release work. GitHub Pages pruning activation remains separately gated. No other meta-PR is authorized by this comment.
The original historical #1029 successor discussion was superseded by the successful merge and this explicit release-first decision.
qnbs commentedon Oct 13, 2026
PR #1029 — final head
064e665add56936976f0a99641e7bd09ecb60a1a, base7f8ea006(main unchanged)SATURATED, exit 0): 15 files, 2,884 meaningful lines, 15 of 15 commits. No commit slot is left.read()is the single read path and now gates every request on the livex-ratelimit-remaining(rate-reservebelow the 100-request reserve,rate-unknownwhen a successful response has no usable header; a successful response replaces the allowance, it is never kept stale). Tests: 150 records with 120 left stop at exactly 20 requests with zero writes; 101 passes, 100 refused after one request; missing header stops after one request; a 429 reporting 50 left is not retried; a later header-less response fails the next read. Each fails without its fix. Pagination of status histories stays bounded bypagination-limit(existing test).disabled_manually(provider read), job tokendeployments: read,DESTRUCTIVE_WRITES_ADMITTED = false, and a test pins that the constant and the permission move together. Nothing here authorizes enabling it.Activation prerequisites (not patched in #1029, commit budget closed; both concern only the closed write path; must land with the change that opens the gate):
funds()must treat an unknown allowance as unfunded once a response has succeeded, so a header-less final read cannot precede a direct DELETE.funds(ctx, DELETE_COST)aftersettle()and before the DELETE.qnbs commentedon Oct 13, 2026
PR #1029 merged as
35a7866529779d8a4e8a67a48fbc6a85b02ca386(squash of head064e665a, 15 commits, 15 files).35a78665; Production READY on that SHA, canonical aliases bound, HTTP 200; Vercel Preview retention pass recorded in ci(vercel): prune stale Preview deployments after PR closure #627 (comment) (11 previews removed, post-proof true).disabled_manually, job tokendeployments: read,DESTRUCTIVE_WRITES_ADMITTED = falsepinned by test. Enabling needs the owner's writer-fence admission plus the two activation prerequisites listed in the previous comment.qnbs commentedon Oct 14, 2026
MAINTAINER DIRECTION — 2026-10-14 ~10:55 CEST — reactivate bounded GitHub Pages retention after safety proof; #1028 REOPENED
The maintainer now explicitly requests addressing the accumulated GitHub Pages deployment-record backlog, safely preserving the latest few plus all protected current/rollback/evidence records, and then resuming ongoing automatic pruning, ideally coordinated with Claude's imminent Vercel postmerge retention. This is owner authorization to pursue the existing #1028 remediation and conditional operational activation, NOT a waiver of technical safety gates nor an order to run unsafe DELETE immediately. The earlier "do not reactivate without separate owner permission" condition is now satisfied at the intent level, subject to the objective preconditions below. No #1028 deletion or workflow activation has been executed by this comment.
Verified source state on main
be3c875fac33bfdd628dce9c70c1fb0e42ac672d:.github/workflows/prune-deployments.ymlstill hasdeployments: readand aschedule+ read-onlyworkflow_dispatch;scripts/pages-pruning/policy.mjsstill setsDESTRUCTIVE_WRITES_ADMITTED=false,MAX_BATCH=10, and explicit newest-three/current/rollback/evidence protection. Six historic IDs in.github/pages-pruning-holds.jsonremain held. The workflow was last independently observeddisabled_manually(13 Oct); reconfirm live workflow state with authenticatedghbefore execution (GitHub connector cannot read the workflow-state endpoint here). Pages deployment count has not been independently retrieved: use complete authenticated GET inventory, no inferred "~100" as authoritative.Execution sequencing / separation: (A) finish #1038 resulting-main exact SHA
be3c875fCI/CD + CodeQL SUCCESS, Vercel production/alias/HTTP proof and separately authorized local SAFE Vercel preview dry-run/reconcile/post-proof under GH #627; (B) as a separate GitHub Pages records operation under this issue, have Claude local Ubuntu runnode scripts/prune-pages-deployments.mjs --dry-run(GET-only) and record complete count/categories, current live Pages ID/status/rollback, six holds, 3 newest, unknowns, candidates and API-limit provenance. No destructive action while UNKNOWN, unsettled/unproved publication, incomplete pagination, ambiguous current or historical Support evidence.Required narrow source PR before enabling any writer:
github-pagesrecords, protect newest 3 + current/rollback + the six explicit evidence holds, inactivation POST verified before DELETE, rate reserve, MAX_BATCH 10, stable batch/repeat behavior, zero-write dry-run. Keep other environments/Vercel/public pages artifact untouchable. Build negative/fault/concurrency/API tests, preserve existing required CI/security checks.deployments: writeandDESTRUCTIVE_WRITES_ADMITTED=truetogether only in the tested protected PR when all guards are in place. No workflow enablement or production deletion during PR review. One narrow signed PR, ≤15 commits/3000 lines/30 files, Ready for Review; exact-head CI, CodeQL, substantive reviewer disposition and normal protected merge. No anti-cascade governance meta-PR or R-15 scope expansion.Separate postmerge operational activation: independently verify the new main CI+CodeQL and Pages current status/HTTP. Re-run real live dry-run before enablement; ensure all unknowns cleared and evidence holds still protected. Then Claude may enable the previously disabled workflow as the owner-requested separate operator action only after all safety proof is terminal. Observe first scheduled/approved run and verify no protected ID touched, site still healthy, max ten safe deletes, reproducible next backlog census. For accelerated clearance, an explicitly reviewed and independently approved bounded manual operator mode may be added only if necessary, with explicit input/authorization and the same fences; never temporarily weaken the built-in local GET-only CLI or reuse Vercel pruning permissions. Otherwise allow daily bounded cycles and document ETA without promising immediate completion. Stop and escalate on 404/409/429, ambiguous status, incomplete inventory, header-less limits, operation guard drift or any protected-object change. Ensure final state keeps at least newest-three and additional evidentiary records; exactly three is not a mandatory count.
Responsibility: This reopened #1028 remains the sole specialist GitHub implementation/operations owner; QNB-41 mirrors only measured CI/retention cost, GH #627/QNB-67 owns Vercel and never manages GitHub Pages objects. Do not declare #1028 Done until successful real run, protected-ID and live Pages website readback, automatic next-run behavior verified and terminal evidence published. Do not interrupt ongoing R-15 W1 work except for the bounded agreed operations. No new Linear issue needed.
qnbs commentedon Oct 14, 2026
Pages operation, step B — complete read-only inventory on
mainbe3c875f(2026-10-14 ~09:15 UTC). No write attempted; the workflow is still disabled.Method. (1) The repo's own GET-only CLI from a clean worktree at
be3c875f:node scripts/prune-pages-deployments.mjs --dry-run→ exit 0,queried 15 · classified 15 · protected 9 · candidate 6 · attempted 0 · inactivated 0 · deleted 0 · failed 0 · requests 33 · writeAttempts 0,followUpRequired true. (2) An independent, fully paginatedgh api --paginate repos/qnbs/WorldScript-Studio/deployments?per_page=100over all environments (116 records, no stderr), counted by environment and creator.The backlog is not on GitHub Pages.
github-pagesqnbs(12),cursor[bot](3)Previewvercel[bot]Productionvercel[bot]The ~100 records are the Vercel GitHub integration's deployment objects. They are not Pages records, the Pages pruner never touches them (other environments), and the Vercel retention (GH #627) deletes Vercel deployments, not these GitHub objects. If the "grown backlog" meant those 101 records, that is a different operation that needs its own decision (see the question at the end).
Pages records (all
success): newest three7061091724(be3c875f, 08:57Z),7058438003(046f49bf, 06:24Z),7052191696(426fb977, 23:01Z yesterday);current = 7061091724,rollback = 7058438003; six explicit holds6995722425,6984593438,6983983689,6969003237,6965361606,6961183719— all six present and classifiedexplicit-hold; six candidates7050380297,7039454621,7036706623,7032029130,7029403976,7026694232(13 Oct, 00:01–21:21Z). Oldest Pages record is 9 Oct 12:55Z; nothing older exists, so there is no hidden older backlog.UNKNOWN = 0, pagination complete.Site:
https://qnbs.github.io/WorldScript-Studio/HTTP 200,<title>WorldScript Studio</title>; Pages APIbuild_type=workflow, sourcemain.Workflow state before (live, authenticated):
Prune old GitHub deployment recordsid279862309,state=disabled_manually. Not touched.Consequence for the plan. With six candidates (≤
MAX_BATCH=10) a single bounded run would clear the backlog; an accelerated manual batch mode is not needed. What stays necessary is the narrow source PR that makes unattended deletion safe — every merge tomainadds one Pages record (nine on 13 Oct alone), so a daily bounded cycle is what keeps the count flat. I am starting that PR now (the two Codex P2 controls, the writer fence / revalidation model,DESTRUCTIVE_WRITES_ADMITTEDanddeployments: writetogether, negative/fault/concurrency tests). No workflow enablement and no deletion during its review.Question for the maintainer (not blocking the PR): do you also want the 101
vercel[bot]Preview/ProductionGitHub deployment records pruned? That is outside #1028's "only positively identifiedgithub-pagesrecords" rule, would need its own owner decision and safeguards (they are the integration's objects; Vercel's own retention does not remove them), and I will not touch them without it.qnbs commentedon Oct 14, 2026
2026-10-14 ~11:39 CEST — #1041 now open, current gate/readback (supersedes "starting PR")
PR #1041 is OPEN/Ready, head
bbea655958cdd927cdad57e1eac2beac431d9bf3(1 GitHub-verified signed commit, 9 changed files, 224 meaningful lines; preferred ≤8 files exceeded, all hard ceilings respected). Implements both previously deferred Codex P2 writer-path fixes, explicit bounded/fail-closed writer-fence documentation and matching negative tests;DESTRUCTIVE_WRITES_ADMITTED=trueand job-scopeddeployments:writeare only on the PR branch, not protected main. Scheduledmainremains the only destructive mode;workflow_dispatchremains read-only. The workflow was independently observeddisabled_manuallyat the authenticated 09:15 UTC read-only inventory; no activation or deletion attempted.At latest-head GET: 34 checks = 26 success, two pending Node22/Node24, five skipped, one neutral, no failed checks; CI/CD #38543027097 in progress, CodeQL #38543027043 successful. CodeScene APPROVED, CodeAnt quality passed; CodeAnt/Codex substantive reviewer summaries are still RUNNING, zero open threads is not final reviewer convergence. Merge remains
blocked, do not merge on green subchecks or disabled reviewer signal.After complete exact-head CI and substantive review, normal protected merge (no admin/bypass) → new-main CI/CD+CodeQL and website/active Pages/evidence readback → clean live GET-only dry-run → separately approved
gh workflow enableand observe actual scheduled first safe run before declaring #1028 completed. Six Pages candidates and nine protected Pages records were proven on prior clean main; 101vercel[bot]Preview/ProductionGitHub deployment records are categorically outside Pages scope. Their deletion would require distinct owner decision/integration preservation plan; do not broaden #1041 or conflate with completed #1038 Vercel Preview retention (GH #627). QNB-41 mirrored.qnbs commentedon Oct 14, 2026
2026-10-14 — Maintainer clarifies the 101 other GitHub deployment records ARE also to be cleaned
Owner decision: Yes, the 85
Preview+ 16ProductionGitHub-Deployment-API records created byvercel[bot]should be reconciled, immediately after the current safe Pages-pruning activation, not postponed to v1.30 postrelease. New, intentionally separate specialist GH #1042 owns read-only mapping, provider-safe status/deletion proof and bounded cleanup. The 101 count is the authenticated 2026-10-14 census, not an approved list of 101 deletions; current Vercel Production, rollbacks, active previews and evidence are held. An external provider'sinactivestatus events may have side effects and require explicit proven safety. Do not expand active PR #1041 or itsgithub-pagesworkflow to other environments. #1028 remains Pages-only: 15 Pages, 6 existing eligible candidates, six evidence holds; finish reviewed #1041, resulting-main gates, first safe scheduled run, then Claude continues #1042 with separate authority, then R-15 W1. No destructive operation initiated by this comment.fix(pages): stop on a changed candidate, keep no stale allowance, res…
ci(pages): give workflow_dispatch a read-only plan job without the wr…
fix(pages): retried failures lower the allowance, reload entries with…
ci(pages): admit the Pages record pruner's bounded destructive writes (…
qnbs commentedon Oct 14, 2026
PR #1041 merged — resulting-
mainproof (2026-10-14)Merge.
a6ef4087270fb4ee014b73eae54bfefe33c3526b(protected squash pinned to headf9f2c11d, GitHub-verified, no admin/auto-merge, branch kept). 4 commits on the branch, 3 correction waves, all findings valid and fixed with mutation-checked tests: 2 CodeRabbit + 8 Codex threads (unknown allowance funds nothing; re-prove the budget after the inactivation settles; candidate change stops the run; error responses leave the allowance unknown, retried failures lower it by one; reservation includes the POST; hold procedure cancels active runs; first observation is a scheduled pass; entries withoutupdated_atare reloaded; stop criteria match the code) and 1 CodeAnt least-privilege finding (dispatch now runs a separate read-onlyplanjob; only the scheduledprunejob holdsdeployments: write). Final head: Codex completed with no finding, CodeAnt no suggestion and gates passed, CodeScene approved, 0 unresolved threads, CI/CD + CodeQL green. Sourcery (rate limit) and cubic (neutral) are NO_SIGNAL, not passes.Resulting
main. CI/CD success, CodeQL success. Vercel Productiondpl_DSF4793GMC9R7zhCBGCru6Bm8Xn8READY, HTTP 200, canonical aliases bound; Vercel preview retention for this epoch: 3 exact-ID deletions (all PR #1041 previews), protected 67/67 present, post-proof all true (receipt on #627).Pages, GET-only dry run on
a6ef4087: queried 16, protected 9 (newest three, current7063613439, rollback7061091724, six explicit holds6995722425 6984593438 6983983689 6969003237 6965361606 6961183719), candidates 7 (7052191696 7050380297 7039454621 7036706623 7032029130 7029403976 7026694232),UNKNOWN0, failed 0, write attempts 0, 36 requests. Sitehttps://qnbs.github.io/WorldScript-Studio/HTTP 200, Pagesbuild_type=workflow. Workflow state before enablement:disabled_manually(id 279862309).Next (separate operator step, now allowed by the owner sequence): enable the workflow, dispatch one read-only
planrun to prove the Actions token path, and read back the first scheduled run (03:00 UTC): at most ten deletions, newest three / current / rollback / six holds untouched. Not done yet: nothing in this comment claims a real deletion.github-pagesrecords only; thevercel[bot]records are #1042.qnbs commentedon Oct 14, 2026
Workflow enabled (2026-10-14 ~11:10 UTC) — Actions token path proven, no deletion yet
gh workflow enable prune-deployments.yml: statedisabled_manually→active(id 279862309), read back. Oneworkflow_dispatchrun onmain@a6ef4087(run 38554851722): jobPlan GitHub Pages records (read-only)success,Reconcile GitHub Pages records onlyskipped as designed; sanitized resultmode=dry-run, queried 16, protected 9, candidates 7, attempted 0 — the dispatched run had adeployments: readtoken and wrote nothing.First write opportunity: the next scheduled run, 2026-10-15 03:00 UTC (the only trigger that can delete). Expected: at most ten of the seven proven candidates (
7052191696 7050380297 7039454621 7036706623 7032029130 7029403976 7026694232) retired, newest three / current7063613439/ rollback7061091724/ the six holds untouched, site HTTP 200,vercel[bot]records untouched. Not yet verified — this issue stays open until that run is read back with before/after counts and the next automatic run is confirmed.