Skip to content

feat(release): batch accumulation controls for standing PR strategy#161

Merged
goosewobbler merged 6 commits intomainfrom
claude/implement-batch-accumulation-xJECy
Apr 24, 2026
Merged

feat(release): batch accumulation controls for standing PR strategy#161
goosewobbler merged 6 commits intomainfrom
claude/implement-batch-accumulation-xJECy

Conversation

@goosewobbler
Copy link
Copy Markdown
Owner

Summary

Implements Follow-up 4 — Batch release accumulation controls from AUTOMATION_PLAN.md.

  • ci.standingPr.minPackages — hard gate: if fewer than N packages have releasable changes, standing-pr update noops and closes any open standing PR with an explanatory comment
  • ci.standingPr.minAge — soft gate: posts a pending commit status (releasekit/standing-pr) on the release branch HEAD until the PR has been open for the configured duration (e.g. "6h", "30m", "1d"); transitions to success once elapsed
  • Manifest schema bumped v1 → v2 with a new firstUpdatedAt field preserved across updates; parseManifest accepts both versions for backward compat
  • Hourly schedule cron trigger added to the standing-pr.yml workflow template so the status check re-evaluates as time passes without needing a push
  • New duration.ts module with parseDuration / formatDuration helpers
  • New standing-pr-status.ts wrapping repos.createCommitStatus with context releasekit/standing-pr

Changed files

File Change
packages/config/src/schema.ts Add minAge, minPackages to StandingPrConfigSchema
releasekit.schema.json Mirror schema additions
packages/release/src/duration.ts New — parseDuration, formatDuration
packages/release/src/standing-pr-status.ts New — postStandingPRStatus
packages/release/src/standing-pr.ts Manifest v2, firstUpdatedAt, minPackages gate, status check posting
templates/workflows/standing-pr.yml Add schedule trigger; update job condition
packages/release/test/unit/duration.spec.ts New — full unit tests for duration parsing/formatting
packages/release/test/unit/standing-pr.spec.ts New tests: v1/v2 compat, minPackages gate, minAge status, firstUpdatedAt preservation, status failure resilience

Test plan

  • All 44 tests in duration.spec.ts + standing-pr.spec.ts pass
  • minPackages: 3 with 1 changed package → noop, existing PR closed with comment
  • minAge: "6h" with PR < 6h old → pending status posted
  • minAge: "1h" with PR > 1h old → success status posted
  • firstUpdatedAt preserved across multiple standing-pr update runs
  • v1 manifests (no firstUpdatedAt) parsed without error
  • Status check API failure caught and logged; update still succeeds

https://claude.ai/code/session_011GSASToKFMEpkMtWgytFXY


Generated by Claude Code

Implements Follow-up 4 from AUTOMATION_PLAN.md, including:

- `ci.standingPr.minPackages`: closes the standing PR and noops when
  fewer than N packages have releasable changes; enforced as a hard gate
- `ci.standingPr.minAge`: reports a `pending` commit status on the
  release branch until the PR has existed for the configured duration
- `duration.ts`: minimal duration parser accepting `\d+(s|m|h|d)`
- `standing-pr-status.ts`: `postStandingPRStatus` wrapping
  `repos.createCommitStatus` with context `releasekit/standing-pr`
- Manifest schema bumped to v2; `firstUpdatedAt` field preserved across
  updates so the age clock starts when the PR was first opened
- `parseManifest` accepts both v1 and v2 manifests for backward compat
- Hourly `schedule` cron trigger added to the standing-pr workflow
  template so the status check re-evaluates as time passes
- Full unit test coverage for new paths

https://claude.ai/code/session_011GSASToKFMEpkMtWgytFXY
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 23, 2026

Release Preview — no release

No bump label detected.
Note: Add bump:patch, bump:minor, or bump:major to trigger a release.


Updated automatically by ReleaseKit

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR implements batch accumulation controls for the standing PR strategy: a minPackages hard gate that closes the standing PR when too few packages have releasable changes, and a minAge soft gate that posts a pending/success commit status on the release branch HEAD until the configured duration elapses. The manifest schema is bumped to v2 with a firstUpdatedAt field (backward-compatible) and an hourly schedule trigger re-evaluates the age gate without requiring a push.

All three P1 issues raised in the previous review have been addressed: statuses: write permission is now present in the workflow, v1→v2 migration correctly falls back to createdAt instead of resetting the clock, and an invalid minAge value now emits a warn rather than silently defaulting to success.

Confidence Score: 5/5

Safe to merge — all three P1 issues from the prior review are addressed and no new blocking issues were found.

All previously flagged P1 concerns (missing statuses:write, v1→v2 clock reset, silent minAge fallback) are resolved in this revision. The remaining previously-noted cosmetic issue (formatDuration dropping sub-hour seconds) is P2 and does not affect correctness. Test coverage for the new gates is thorough.

No files require special attention.

Important Files Changed

Filename Overview
packages/release/src/duration.ts New module with parseDuration and formatDuration helpers; single-unit regex keeps parsing intentionally strict; seconds are dropped when hours are present (cosmetic, previously noted).
packages/release/src/standing-pr.ts Adds minPackages gate (close+noop when below threshold), firstUpdatedAt preservation with v1 createdAt fallback, and minAge status-check logic; all three issues flagged in the prior review are addressed.
templates/workflows/standing-pr.yml Adds hourly schedule trigger to re-evaluate minAge status; statuses: write permission correctly added; job condition updated to include schedule event.
packages/config/src/schema.ts Adds optional minAge (string) and minPackages (positive int) fields to StandingPrConfigSchema with appropriate Zod validators.
releasekit.schema.json JSON schema correctly mirrors the Zod schema additions with integer/minimum constraints on minPackages and string type for minAge.
packages/release/test/unit/duration.spec.ts Full unit-test coverage for parseDuration and formatDuration including edge cases (empty string, unknown units, composite strings, sub-second rounding).
packages/release/test/unit/standing-pr.spec.ts New tests cover v1/v2 compat, minPackages gate (noop + close), minAge pending/success transitions, firstUpdatedAt preservation, v1→v2 createdAt fallback, and status-failure resilience.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[standing-pr update triggered\npush / schedule] --> B{Any releasable\nchanges?}
    B -- No --> C[Close existing PR\nwith comment] --> Z[noop]
    B -- Yes --> D{minPackages\nconfigured?}
    D -- No --> F
    D -- Yes --> E{updates.length\n>= minPackages?}
    E -- No --> C2[Close existing PR\nwith comment] --> Z
    E -- Yes --> F[Reset release branch\nWrite bumps + notes\nCommit + force-push]
    F --> G[Capture releaseBranchSha]
    G --> H[Create / update PR]
    H --> I[Find existing manifest\nPreserve firstUpdatedAt\nor fall back to createdAt]
    I --> J[Write v2 manifest comment]
    J --> K{minAge\nconfigured?}
    K -- No --> L[Post success status]
    K -- Yes --> M{parseDuration\nreturns null?}
    M -- Yes --> N[warn: gate inactive] --> L
    M -- No --> O{PR age\n>= minAge?}
    O -- Yes --> L[Post success status\nReady to merge]
    O -- No --> P[Post pending status\nWaiting X for minAge]
Loading

Reviews (6): Last reviewed commit: "chore: merge main into feature branch" | Re-trigger Greptile

Comment thread packages/release/src/standing-pr.ts
Comment thread packages/release/src/duration.ts
- `duration.ts`: use switch statement for unit multipliers to avoid
  `noUncheckedIndexedAccess` error on Record index access
- `standing-pr.ts`: emit `warn()` when `ci.standingPr.minAge` is set to
  an unparseable duration string so the user knows the gate is inactive

https://claude.ai/code/session_011GSASToKFMEpkMtWgytFXY
Comment thread templates/workflows/standing-pr.yml
claude added 2 commits April 23, 2026 15:28
repos.createCommitStatus requires this permission. Without it the
releasekit/standing-pr status check is never posted, making the minAge
gate silently non-functional.

https://claude.ai/code/session_011GSASToKFMEpkMtWgytFXY
Resolves conflicts in standing-pr-status.ts, standing-pr.ts, and standing-pr.spec.ts.
Preserves batch accumulation controls (minAge, minPackages) while adopting main's
refactored postStandingPRStatusSafe wrapper and CommitStatusState type.

https://claude.ai/code/session_011GSASToKFMEpkMtWgytFXY
Comment thread packages/release/src/standing-pr.ts
claude added 2 commits April 23, 2026 17:14
…manifest

When a v1 manifest exists (no firstUpdatedAt), fall back to its createdAt
instead of current time so the minAge clock isn't reset on first v2 run.

https://claude.ai/code/session_011GSASToKFMEpkMtWgytFXY
Resolves conflicts in schema.ts and releasekit.schema.json — adds mergeMethod
from main alongside minAge and minPackages from this branch.

https://claude.ai/code/session_011GSASToKFMEpkMtWgytFXY
@goosewobbler goosewobbler merged commit ea63f20 into main Apr 24, 2026
21 checks passed
@goosewobbler goosewobbler deleted the claude/implement-batch-accumulation-xJECy branch April 24, 2026 23:22
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.

2 participants