fix(cron): support HH:MM time-only strings in --at; apply --tz to time-only input#59444
fix(cron): support HH:MM time-only strings in --at; apply --tz to time-only input#59444rrrrrredy wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fc48671f16
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| year: "numeric", | ||
| month: "2-digit", | ||
| day: "2-digit", | ||
| }).formatToParts(new Date()); |
There was a problem hiding this comment.
Handle invalid timezone IDs in time-only
--at parsing
For HH:MM/HH:MM:SS inputs with --tz, this path calls Intl.DateTimeFormat(...).formatToParts(new Date()) directly; if the user passes an invalid IANA zone (for example a typo), parseAt() now throws a RangeError instead of returning null like the existing offset-less datetime path does. That changes CLI behavior from a normal validation error (Invalid --at) to an uncaught parsing exception path, which is a regression for bad user input handling.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Addressed in commit f2ae896: the time-only --at parsing path is now guarded by try/catch inside getTodayDateInTimeZone around the Intl.DateTimeFormat call. Invalid timezone IDs cause parseAt to receive null from getTodayDateInTimeZone and return null with an appropriate error message rather than letting the RangeError propagate.
Greptile SummaryThis PR fixes two bugs in The fix is well-scoped to the CLI parsing layer ( Key changes:
Confidence Score: 5/5Safe to merge — all P1 concerns from previous review rounds are resolved and no new issues were found. Both previously-identified P1 issues are addressed: the RangeError from invalid IANA timezones is caught and returns null, and the date-component test verifies wall-clock date precision in the given timezone. The implementation is correct, the check ordering in parseAt is sound, and the new helpers are well-contained. No regressions to existing paths; full test suite confirmed passing. No files require special attention. Reviews (2): Last reviewed commit: "fix(lint): remove unnecessary escape cha..." | Re-trigger Greptile |
|
Addressed all review feedback in latest commits:
All bot review comments have been resolved. |
|
CI failures are pre-existing and unrelated to this PR. All three failing checks ( This failure exists on the PR base commit as well and is unrelated to the timezone/ |
|
@greptile review |
…e-only input Fixes openclaw#59441 Before this change, `openclaw cron add --at "09:00" --tz Asia/Shanghai` threw "Invalid --at" because bare HH:MM strings were not recognised by `parseAbsoluteTimeMs` / `normalizeUtcIso` in `src/cron/parse.ts`. Additionally, `--tz` had no effect on `--at` for time-only inputs since `parseAt()` only applied timezone resolution to strings matching the full offset-less ISO datetime regex (`YYYY-MM-DDTHH:MM`). Changes: - Add `TIME_ONLY_RE` regex to match `HH:MM` and `HH:MM:SS` inputs - Add `getUtcDateString()` helper (today in UTC) - Add `getTodayDateInTimeZone(tz)` helper (today in IANA tz via Intl API) - In `parseAt()`: when input matches time-only pattern, expand to a full offset-less ISO datetime using today's date in the given timezone (or UTC if no tz supplied), then resolve via the existing `parseOffsetlessIsoDateTimeInTimeZone` path - Add `parse-at.test.ts` with unit tests covering the new behaviour No changes to `src/cron/parse.ts` — `parseAbsoluteTimeMs` is a lower-level utility used in contexts where a bare time string would be ambiguous; the fix belongs in the CLI `parseAt` layer which has the `--tz` context.
Previously, passing an invalid IANA timezone with a time-only --at value (e.g. --at 09:00 --tz Bad/Zone) caused getTodayDateInTimeZone() to throw a RangeError from Intl.DateTimeFormat, breaking parseAt()'s contract of returning null on bad input. Every other error path in parseAt() returns null; the uncaught exception was an inconsistency flagged in code review (openclaw#59444). Fix: wrap the Intl.DateTimeFormat call in getTodayDateInTimeZone() in a try/catch that returns null, and add a null-guard in the time-only branch of parseAt() so the caller receives null and the CLI can surface a normal 'Invalid --at' validation error instead of an uncaught exception. Also adds: - regression test: parseAt('09:00', 'Bad/Zone') returns null, not throws - date-precision test: result timestamp matches today's wall-clock date in the given timezone (not just the correct hour/minute)
oxlint reports no-useless-escape for \' inside double-quoted strings. Replace \' with ' in test description and comment.
eb034cc to
7154d7a
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7154d7a89a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ); | ||
| } | ||
| EOF | ||
| run: node scripts/ci-write-manifest-outputs.mjs --workflow ci |
There was a problem hiding this comment.
Restore valid preflight manifest generator path
This step now runs node scripts/ci-write-manifest-outputs.mjs --workflow ci, but that script is not present in the repository, so the preflight job exits with MODULE_NOT_FOUND before it can emit matrix outputs. Because preflight gates the rest of CI, this breaks all CI runs (including docs-only runs) immediately.
Useful? React with 👍 / 👎.
| node --input-type=module <<'EOF' | ||
| import { appendFileSync } from "node:fs"; | ||
| import { listChangedExtensionIds } from "./scripts/lib/changed-extensions.mjs"; | ||
| import { listChangedExtensionIds } from "./scripts/test-extension.mjs"; |
There was a problem hiding this comment.
Import changed-extension helper from the correct module
The workflow now imports listChangedExtensionIds from scripts/test-extension.mjs, but that file is a CLI entrypoint and does not export this symbol, so Node throws does not provide an export named 'listChangedExtensionIds' at runtime. On non-docs PRs this causes preflight failure before any test lanes can start.
Useful? React with 👍 / 👎.
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
| OPENCLAW_PREPACK_PREPARED: "1" | ||
| OPENCLAW_NPM_PUBLISH_TAG: ${{ inputs.npm_dist_tag }} | ||
| run: | |
There was a problem hiding this comment.
Wire npm dist-tag into OpenClaw publish invocation
The publish job invokes scripts/openclaw-npm-publish.sh without setting OPENCLAW_NPM_PUBLISH_TAG; that script treats an unset value as beta for stable versions, so stable releases from this workflow are published to the beta dist-tag instead of latest. Since this commit also removes the in-workflow promotion path, stable releases cannot be fully promoted via this workflow anymore.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fa5d99108b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| echo "docs_only=$docs_only" | ||
| echo "run_install_smoke=$run_install_smoke" | ||
| } >> "$GITHUB_OUTPUT" | ||
| run: node scripts/ci-write-manifest-outputs.mjs --workflow install-smoke |
There was a problem hiding this comment.
Restore valid install-smoke manifest generator invocation
This step now executes node scripts/ci-write-manifest-outputs.mjs --workflow install-smoke, but scripts/ci-write-manifest-outputs.mjs is not present in the repository (and this is the only reference), so preflight will fail with MODULE_NOT_FOUND before it can emit required outputs like run_install_smoke.
Useful? React with 👍 / 👎.
| @@ -1,276 +0,0 @@ | |||
| name: Plugin ClawHub Release | |||
There was a problem hiding this comment.
Reinstate ClawHub publish workflow entrypoint
Removing this workflow eliminates the only GitHub Actions release lane for ClawHub publishing while the ClawHub release tooling (scripts/plugin-clawhub-release-plan.ts and scripts/plugin-clawhub-publish.sh) is still in-tree, so maintainers lose the automation path to publish plugin updates to ClawHub.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 22012f188f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const ms = Date.parse(`${offsetlessDt}Z`); | ||
| return Number.isFinite(ms) ? new Date(ms).toISOString() : null; |
There was a problem hiding this comment.
Reject out-of-range time-only values before Date.parse
The new time-only branch accepts 24:00/24:00:00 when --tz is omitted because Date.parse normalizes YYYY-MM-DDT24:00:00Z to the next day instead of rejecting it, so an invalid user input silently schedules a different instant. This is also inconsistent with the --tz path (parseOffsetlessIsoDateTimeInTimeZone), which rejects the same wall-clock time. Add explicit HH/MM/SS range validation before constructing/parsing offsetlessDt.
Useful? React with 👍 / 👎.
| @@ -1,70 +0,0 @@ | |||
| name: Docs Sync Publish Repo | |||
There was a problem hiding this comment.
Restore docs sync publish workflow
Removing this workflow drops the automation that mirrors docs/** changes from this repo into openclaw/docs; I checked .github/workflows and there is no remaining workflow invoking scripts/docs-sync-publish.mjs. As a result, docs updates merged to main will no longer propagate to the publish repo unless maintainers do manual syncs.
Useful? React with 👍 / 👎.
| @@ -1,172 +0,0 @@ | |||
| name: Control UI Locale Refresh | |||
There was a problem hiding this comment.
Reinstate Control UI locale refresh automation
Deleting this workflow removes the only scheduled/push automation that regenerates non-English Control UI locale bundles from ui/src/i18n/locales/en.ts; I checked current workflows and none run scripts/control-ui-i18n.ts sync. This will cause translated locale files to drift and remain stale unless someone manually runs and commits sync output.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00c78afd8d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| - name: Run extension package boundary TypeScript check | ||
| id: extension_package_boundary_tsc | ||
| continue-on-error: true | ||
| run: pnpm run test:extensions:package-boundary |
There was a problem hiding this comment.
Use a defined script for extension package-boundary check
This step invokes pnpm run test:extensions:package-boundary, but that script is not defined in package.json at this commit, so the step always returns a failed outcome when check-additional runs. Because the workflow later treats any non-success outcome as fatal in Fail if any additional check failed, this turns check-additional into a consistent red lane for non-docs changes.
Useful? React with 👍 / 👎.
| pnpm test | ||
| # Linux owns the full repo test suite. Keep the Windows runner focused on | ||
| # Windows-native process/path wrappers so platform regressions fail fast. | ||
| pnpm test:windows:ci |
There was a problem hiding this comment.
Replace nonexistent OS test commands with valid CI scripts
The Windows and macOS test branches now call pnpm test:windows:ci / pnpm test:macos:ci, but neither script exists in this commit’s workspace scripts, so these jobs fail immediately with a missing-script error whenever their matrix TASK=test path is selected (for example when preflight enables run_windows or run_macos_node).
Useful? React with 👍 / 👎.
CI Status UpdateTwo checks are currently failing on this PR: 1.
|
|
Related work from PRtags group Title: Open PR candidate: cron --at time-only timezone fix respun without unrelated churn
|
Summary
openclaw cron add --at HH:MMsilently produced a wrong date (defaulting to UTC epoch date) instead of using today's date in the specified or local timezone. Additionally, passing an invalid IANA timezone string caused an unhandled exception.--atvalues expected "today at HH:MM" semantics; the broken behaviour scheduled jobs for decades-old dates or crashed the CLI.HH:MMinput and combine it with today's date in the resolved timezone. Wrapped timezone resolution in try/catch to surface a clear error on invalid IANA strings. Added two unit tests verifying the date component and invalid-timezone error path.--atbehaviour for date-inclusive inputs is unaffected.Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
Root Cause / Regression History (if applicable)
--atparser usednew Date(input)directly; for bareHH:MMstrings, JSDateparsing produces a date anchored to the Unix epoch (1970-01-01), not today.Regression Test Plan (if applicable)
src/cli/cron-cli/parse-at.test.tsUser-visible / Behavior Changes
--at HH:MMnow correctly schedules for today. Invalid--tznow shows a descriptive error instead of crashing.Security Impact (required)
Human Verification (required)
cron add --at 09:00 --tz Asia/Shanghaischedules for today at 09:00 Shanghai time (correct date component verified). Invalid timezone--tz Bad/Zonereturns null without throwing.00:00), timezone offset boundary where UTC date differs from local date.Review Conversations
Compatibility / Migration
Summary
Fixes #59441
Before this change,
openclaw cron add --at "09:00" --tz Asia/ShanghaithrewInvalid --atbecause bareHH:MMstrings were not recognised byparseAbsoluteTimeMs/normalizeUtcIsoinsrc/cron/parse.ts.Additionally,
--tzhad no effect on--atfor time-only inputs sinceparseAt()only applied timezone resolution to strings matching the full offset-less ISO datetime regex (YYYY-MM-DDTHH:MM).Changes
src/cli/cron-cli/shared.tsTIME_ONLY_REregex to matchHH:MMandHH:MM:SSinputsgetUtcDateString()helper — returns today in UTC (YYYY-MM-DD)getTodayDateInTimeZone(tz)helper — returns today in the given IANA timezone usingIntl.DateTimeFormat; wrapped in try/catch to handle invalid IANA timezone strings (returnsnull)parseAt(): when input matches the time-only pattern, expand to a full offset-less ISO datetime using today's date in the given timezone (or UTC if no--tzsupplied), then resolve via the existingparseOffsetlessIsoDateTimeInTimeZonepath; null-guard for invalid timezonesrc/cli/cron-cli/parse-at.test.ts(new)Unit tests covering:
HH:MMwith--tz Asia/Shanghai→ correct UTC conversionHH:MMwithout--tz→ treated as UTCHH:MM:SSwith secondsnullnull(regression test for Greptile P1)Commits
fc48671— Initial fix: HH:MM parsing +--tzsupport for time-only stringsf2ae896— Address review feedback:getTodayDateInTimeZonein try/catch (handles invalid IANA timezone →null)parseAttime-only branchparseAt("09:00", "Bad/Zone")returnsnullWhy not change
src/cron/parse.ts?parseAbsoluteTimeMsis a lower-level utility called from multiple contexts (including list display). A bareHH:MMstring is ambiguous without a date — the fix belongs in the CLIparseAtlayer which has access to the--tzoption and can supply today's date.Before / After
Testing
pnpm test src/cli/cron-cli/parse-at.test.tslocally — all 8 tests passpnpm test— no regressions