feat(init): prune reports by API with a retention and size-budget sweep - #329
Conversation
Fallow audit reportFound 2 findings. Details
Generated by fallow. |
🗺️ StyleProof report📊 View the side-by-side visual report →A navigable affordance was removed without acknowledgement — repair or acknowledge it; visual approval cannot clear this failure. |
| selectReportFoldersToPrune, | ||
| } from '../dist/report-prune.js'; | ||
|
|
||
| const argv = process.argv.slice(2); |
There was a problem hiding this comment.
warn fallow/code-duplication: Code clone group 1 (10 lines, 2 instances)
Fix intent: Extract the repeated block or centralize shared logic.
| @@ -0,0 +1,38 @@ | |||
| import { defineConfig, devices } from '@playwright/test'; | |||
There was a problem hiding this comment.
error fallow/unused-file: File is not reachable from any entry point
Fix intent: Delete the file or add a real entry-point reference.
Run
fallow fix --filesor delete this file.
| const response = await fetchImplementation(`${repositoryUrl}${apiPath}`, { | ||
| method, | ||
| headers: { | ||
| authorization: `Bearer ${options.token}`, |
There was a problem hiding this comment.
warn fallow/code-duplication: Code clone group 2 (12 lines, 2 instances)
Fix intent: Extract the repeated block or centralize shared logic.
350be6c to
b190ba6
Compare
9548d79 to
7318616
Compare
b190ba6 to
1a6e2c8
Compare
7318616 to
2e167fd
Compare
1a6e2c8 to
783379c
Compare
|
Heads up: #328 (publish reports through the git-data API) is now merged to main, and this PR conflicts with it non-trivially — so I've left it for you rather than force-resolve it. The conflict is a real semantic overlap, not a mechanical one:
Recommend rebasing onto current |
The scaffolded prune job cloned the report branch to delete one folder. A blobless clone does not make that cheap: git push re-fetches the retained blobs to build its pack, so pruning cost the size of the whole branch — the exact failure mode the API publisher removed from publish. Pruning now goes through the packaged styleproof-prune-reports command, which deletes folders as server-side subtree removals and retries the fast-forward race by re-reading and re-selecting against the fresh tip. Close-triggered pruning alone also cannot bound the branch: a missed close event leaks a folder forever, and one PR can publish hundreds of megabytes of crops, so the folders that blow the budget are routinely younger than any reasonable retention window. The scaffold therefore adds a daily report-sweep job: delete reports whose PR closed more than 14 days ago, then, while the branch is still over 1.5 GB, keep deleting oldest-closed first. Reports for open PRs are never touched, and a truncated tree listing refuses to prune rather than guess.
783379c to
ba338c4
Compare
`npm audit --audit-level=high` (ci.yml) began failing on every commit once GHSA-v2hh-gcrm-f6hx was published against fast-uri 3.0.0–3.1.3 (host confusion via a literal backslash authority delimiter). It is a transitive dependency, so the advisory turned main red independently of any code change — the #329 build was simply the first to run after it dropped. `npm audit fix` bumps fast-uri 3.1.3 → 3.1.4 (lockfile only, no manifest change) and clears the advisory: `npm audit --audit-level=high` now reports 0 vulnerabilities. Build is clean and the full suite stays 630/630. Co-authored-by: StyleProof Test <styleproof@example.test> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Cut 4.7.0 from the six commits merged since 4.6.3. Two are feature work, so this is a minor bump; the version bump is the Release workflow's signal to tag, publish to npm, and cut the GitHub Release on merge. Added - Reports publish through the git-data API (#328). - styleproof-init prunes reports by API with a retention + size-budget sweep (#329). - CERTIFICATION_FAILED dogfooded end-to-end (#327). Security - Cleared two high-severity transitive advisories: brace-expansion 5.0.6→5.0.7 (#330) and fast-uri 3.1.3→3.1.4, GHSA-v2hh-gcrm-f6hx (#331). Verified locally: build, typecheck, lint, format:check, privacy:check all clean; full node --test suite 630/630 with a complete install. Co-authored-by: StyleProof Test <styleproof@example.test> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Stacked on #328 (the API publisher); this PR is the pruning half of the same problem.
Why does this feature exist?
So the report branch stays bounded without the consumer ever thinking about it. The scaffolded prune job cloned the branch bloblessly and used
git rm+git push; that looks cheap but is not, becausegit pushre-fetches the retained blobs to build its pack, so pruning cost the size of the whole branch — the same failure mode #328 removes from publish.Close-triggered pruning alone also cannot bound the branch. A missed close event (Actions outage, workflow not yet on the default branch, repos scaffolded before the prune job existed) leaks a folder forever. And retention cannot hold a size ceiling: one PR can publish hundreds of megabytes of crops, so the folders that blow the budget are routinely younger than any reasonable retention window.
What changed?
src/report-prune.tsandbin/styleproof-prune-reports.mjs(registered inpackage.jsonbin), with two modes:--pull-request <n>deletes one folder on close;--retention-days <d> --budget-bytes <b>sweeps by the retention window, then keeps deleting oldest-closed first while the branch is over budget. Reports for open PRs are never touched; if open PRs alone exceed the budget it warns instead of deleting them. Deletion is a server-side subtree removal in one commit; a lost fast-forward race re-reads the tip, re-selects, and retries; a truncated tree listing refuses to prune rather than select on partial size data.bin/styleproof-init.mjsscaffold: the close-prune job now calls the packaged command instead of cloning; a dailyreport-sweepjob is added (defaults: 14 days, 1.5 GB, editable as two plain flags in the generated workflow); the report job is guarded topull_requestevents so the schedule cannot trigger it.Behavioural Proof (with video and screenshots)
Not applicable: CI plumbing with no rendered surface. Behavioural proof is deterministic instead.
test/report-prune.test.mjscovers the selection policy (retention window, over-budget oldest-closed-first with the newest reports surviving, open PRs never selected even over budget, under-budget no-op) and the API deletion path (single-commit subtree removals on the current tip, clean no-ops for absent folders and a missing branch, the ref-race re-read/re-select/retry cycle, truncated-listing refusal, and closed-PR timestamp pagination keyedpr-<n>).test/init.test.mjsvalidates the generated workflow against a realstyleproof-initrun for every package manager: schedule trigger present, event guards correct, the packaged command in both jobs, and nogit rmagainst the report branch.Verification Summary
npm test(build + full suite): 630 pass, 0 fail on this branch.npm run format:check: clean..github/workflows/styleproof.ymlfrom a real init run parses as YAML with jobsstyleproof,prune,report-sweepandon: [pull_request, schedule].