Skip to content

feat(init): prune reports by API with a retention and size-budget sweep - #329

Merged
BenSheridanEdwards merged 1 commit into
mainfrom
feat/report-prune-sweep
Jul 22, 2026
Merged

feat(init): prune reports by API with a retention and size-budget sweep#329
BenSheridanEdwards merged 1 commit into
mainfrom
feat/report-prune-sweep

Conversation

@BenSheridanEdwards

@BenSheridanEdwards BenSheridanEdwards commented Jul 21, 2026

Copy link
Copy Markdown
Owner

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, because git push re-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?

  • New src/report-prune.ts and bin/styleproof-prune-reports.mjs (registered in package.json bin), 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.mjs scaffold: the close-prune job now calls the packaged command instead of cloning; a daily report-sweep job is added (defaults: 14 days, 1.5 GB, editable as two plain flags in the generated workflow); the report job is guarded to pull_request events so the schedule cannot trigger it.
  • Not in this PR: the map-store prune job in the same scaffold has the same clone-to-delete shape (sparse checkout narrows the checkout, not the push). Map bundles are far smaller, so it is left as a follow-up.

Behavioural Proof (with video and screenshots)

Not applicable: CI plumbing with no rendered surface. Behavioural proof is deterministic instead. test/report-prune.test.mjs covers 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 keyed pr-<n>). test/init.test.mjs validates the generated workflow against a real styleproof-init run for every package manager: schedule trigger present, event guards correct, the packaged command in both jobs, and no git rm against the report branch.

Verification Summary

  • npm test (build + full suite): 630 pass, 0 fail on this branch.
  • npm run format:check: clean.
  • The generated .github/workflows/styleproof.yml from a real init run parses as YAML with jobs styleproof, prune, report-sweep and on: [pull_request, schedule].
  • Action dogfood and Map store dogfood workflows: green on this PR.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

Fallow audit report

Found 2 findings.

Details
Severity Rule Location Description
minor fallow/code-duplication bin/styleproof-prune-reports.mjs:21 Code clone group 1 (10 lines, 2 instances)
minor fallow/code-duplication src/report-prune.ts:100 Code clone group 2 (12 lines, 2 instances)

Generated by fallow.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

🗺️ 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.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fallow audit report

Quality gate failed

3 inline findings selected for GitHub review.

selectReportFoldersToPrune,
} from '../dist/report-prune.js';

const argv = process.argv.slice(2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warn fallow/code-duplication: Code clone group 1 (10 lines, 2 instances)

Fix intent: Extract the repeated block or centralize shared logic.

Comment thread playwright.styleproof.config.ts Outdated
@@ -0,0 +1,38 @@
import { defineConfig, devices } from '@playwright/test';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 --files or delete this file.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 823e84f.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved in 4857906.

Comment thread src/report-prune.ts
const response = await fetchImplementation(`${repositoryUrl}${apiPath}`, {
method,
headers: {
authorization: `Bearer ${options.token}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warn fallow/code-duplication: Code clone group 2 (12 lines, 2 instances)

Fix intent: Extract the repeated block or centralize shared logic.

@BenSheridanEdwards
BenSheridanEdwards force-pushed the feat/report-prune-sweep branch 2 times, most recently from 350be6c to b190ba6 Compare July 21, 2026 14:19
@BenSheridanEdwards
BenSheridanEdwards force-pushed the feat/api-report-publish branch from 9548d79 to 7318616 Compare July 21, 2026 16:40
@BenSheridanEdwards
BenSheridanEdwards force-pushed the feat/report-prune-sweep branch from b190ba6 to 1a6e2c8 Compare July 21, 2026 16:41
@BenSheridanEdwards
BenSheridanEdwards force-pushed the feat/api-report-publish branch from 7318616 to 2e167fd Compare July 21, 2026 16:45
@BenSheridanEdwards
BenSheridanEdwards force-pushed the feat/report-prune-sweep branch from 1a6e2c8 to 783379c Compare July 21, 2026 16:45
@BenSheridanEdwards

Copy link
Copy Markdown
Owner Author

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 main and re-applying the prune-sweep on the new helper surface — you own the intended final shape of both features, so this reconciliation is safer in your hands than a mechanical resolve. Everything else in the StyleProof PR queue is merged (#330, #312, #327, #328); this and the broken dev-deps bump (#313, an ERESOLVE on typescript@7 vs typescript-eslint@8.64) are all that's left.

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.
@BenSheridanEdwards
BenSheridanEdwards force-pushed the feat/report-prune-sweep branch from 783379c to ba338c4 Compare July 22, 2026 06:52
@BenSheridanEdwards
BenSheridanEdwards changed the base branch from feat/api-report-publish to main July 22, 2026 06:57
@BenSheridanEdwards
BenSheridanEdwards merged commit 4f8ae3b into main Jul 22, 2026
1 check passed
BenSheridanEdwards added a commit that referenced this pull request Jul 22, 2026
`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>
BenSheridanEdwards added a commit that referenced this pull request Jul 22, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant