Validate --interval-seconds before writing the launch agent - #137
Conversation
Both sync install commands passed the option straight through Number(). A non-numeric value became NaN, and the library default uses `??`, which only replaces null and undefined. NaN reached the plist writer, which emits <integer>NaN</integer>. macOS rejects that plist, so the agent was written but never ran. Validate at the CLI boundary with parsePositiveIntegerOption, the helper added in steipete#133. The library keeps its current behaviour. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
🦞👀 Pull request received. I will update this pull request when review starts. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8500297f1a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const intervalSeconds = parsePositiveIntegerOption( | ||
| options.intervalSeconds, | ||
| "--interval-seconds", | ||
| ); |
There was a problem hiding this comment.
Keep accepting Number-compatible interval spellings
When an existing script supplies an interval such as 1e3, +60, or 0x3c, the previous Number(options.intervalSeconds) conversion produced a valid positive integer and a valid plist, but parsePositiveIntegerOption delegates to a decimal-digits-only regex and now rejects these inputs. This changes the accepted CLI grammar while attempting only to reject invalid intervals; validate the converted number as a safe positive integer (as parseLimitOption does for compatibility) for both launchd commands.
Useful? React with 👍 / 👎.
|
Codex review: needs maintainer review before merge. Reviewed August 31, 2026, 3:08 PM ET / 19:08 UTC. ClawSweeper reviewWhat this changesThis PR validates positive launchd intervals for account and bookmark sync installers before any plist is written, while retaining Number-compatible inputs such as Merge readinessKeep open: the focused CLI fix appears correct, preserves the previously accepted numeric grammar, and has sufficient real-command proof; it is ready for ordinary maintainer review. Priority: P2 Review scores
Verification
How this fits togetherBirdclaw’s jobs CLI turns scheduled-sync options into macOS LaunchAgent plists. The interval value flows from a user command through validation into the plist consumed by launchd. flowchart LR
A[User interval option] --> B[Jobs install command]
B --> C[Positive numeric validation]
C -->|invalid| D[Structured CLI error]
C -->|valid| E[LaunchAgent plist builder]
E --> F[macOS launchd scheduler]
Before merge
Agent review detailsSecurityNone. Review metrics
Technical reviewBest possible solution: Merge the CLI-boundary validation while retaining the established Number-compatible interval syntax. Do we have a high-confidence way to reproduce the issue? Yes—the base conversion and plist serialization establish the path, and the supplied rebuilt-CLI terminal evidence shows invalid input previously wrote Is this the best way to solve the issue? Yes—the validation is placed before the installer side effect and uses the existing compatible numeric grammar instead of changing defaults or library behavior. AGENTS.md: not found in the target repository. Codex review notes: model internal, reasoning high; reviewed against 73303adbc8c0. LabelsLabel justifications:
EvidenceWhat I checked:
Likely related people:
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (3 earlier review cycles)
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@clawsweeper re-review Two things landed since the last review. Bun primary was red on formatting, not tests. Real behavior proof added to the PR body. I built the CLI with Before, on main's handler, the command reports success and writes a plist macOS cannot parse: After, on this branch: A valid One note on method: |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
parsePositiveIntegerOption tests the raw string with /^\d+$/, so it rejects 1e3 and 0x10. Number() accepted both before this branch added validation, so those spellings regressed. Add parsePositiveLimitOption, which mirrors parsePositiveIntegerOption on top of parseLimitOption. That keeps the Number() grammar and validates the result, the same approach parseLimitOption already documents. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Fixed in
Real CLI, freshly built
Two parameterised tests cover the preserved spellings, one per command. Reverting to |
|
LAND recommended for I prepared the missing documentation and changelog entry in 51dfbdc, with credit to @devYRPauli. The maintainer candidate branch contains this PR plus that documentation-only commit. The original fork is unchanged. The landing owner can include that commit when landing this PR. Built-CLI proof on macOS, using temporary ./scripts/bun-canary.sh install --frozen-lockfile
./scripts/bun-canary.sh scripts/build-cli.mjs
proof=$(mktemp -d)
BIRDCLAW_HOME="$proof/home" node bin/birdclaw.mjs jobs install-account-launchd \
--interval-seconds abc --launch-agents-dir "$proof/agents" --no-loadRepeated for
Result: 52 real built-CLI invocations passed across both runtimes. No scheduled jobs were loaded. ./scripts/bun-canary.sh ./scripts/run-vitest.mjs run \
src/cli.test.ts src/lib/account-sync-job.test.ts \
src/lib/bookmark-sync-job.test.ts src/lib/launchd.test.ts
# Test Files 4 passed (4); Tests 128 passed (128)
./scripts/bun-canary.sh run --bun check
# Format, lint, and typecheck passed.
git diff --check
# Passed.Codex autoreview of the PR plus documentation was scoped-clean at the helper's default P0 threshold. Manual review also checked the previous numeric-grammar finding, installer ordering, defaults, and the regression tests; no remaining actionable defect found. CI run 33421817650 is green for all four CI lanes on the original PR head; GitGuardian also passed. The earlier formatting and numeric-grammar problems are resolved. The documentation-only candidate commit has local validation; the non-main branch push does not trigger this repository's CI workflow. No merge, release, or closure performed. |
Document the interval contract and record the user-visible fix from steipete#137. Co-authored-by: Yash Raj Pandey <yashpn62@gmail.com>
|
ClawSweeper status: review started. I am starting a fresh review of this pull request: Validate --interval-seconds before writing the launch agent This is item 1/1 in the current shard. Shard 0/1. This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking. Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted. |
Follow-up to #133, which added the numeric option helpers this uses.
Problem
jobs account-sync installandjobs bookmark-sync installboth take--interval-secondsand pass it through unchecked:The library default cannot catch a bad value.
src/lib/account-sync-job.ts:475andsrc/lib/bookmark-sync-job.ts:298use:??replaces onlynullandundefined.NaNis neither, so it passes through tosrc/lib/launchd.ts:141:birdclaw jobs account-sync install --interval-seconds abctherefore writes<integer>NaN</integer>. macOS rejects that plist:The command reports success and the agent is written, but it never runs on a schedule.
Change
Both actions now parse the option first with
parsePositiveIntegerOption, the helper added in #133, and return before any install work when it is invalid. An interval must be at least 1, which matches whatStartIntervalaccepts.src/lib/launchd.ts,account-sync-job.tsandbookmark-sync-job.tsare unchanged. The validation sits at the CLI boundary, the same place as #132 and #133. No flag name, default value or help text changes.Tests
Two tests in
src/cli.test.ts, one per command. Each asserts that an invalid interval exits 1 and that no plist is installed, and that a valid interval still reaches the installer with the right number.Reverting only the source change and keeping the tests fails exactly the two new ones:
Real behavior proof
Built the CLI with
npm run build:nodeand ran the real binary against a temporary LaunchAgents directory, so nothing touches the real one.Before, on
main's handler:The command reports success and writes a plist that macOS cannot parse.
After, on this branch:
No plist is written. A valid value is unaffected:
install-bookmarks-launchdtakes the same option through the same helper.