refactor: capture time.Now() once at shell start, add NowSafe() guard, fix fuzz test context leaks#117
Conversation
…ext.Now value Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@codex review this PR Important: Read the SPECS section of the PR description. If SPECS are present: make sure the implementation matches ALL the specs. |
AlexandreYang
left a comment
There was a problem hiding this comment.
Review Summary
This PR refactors CallContext.Now from a func() time.Time to a time.Time value captured once at Run() start. The design intent is sound and the implementation is clean in the common case — but there is one correctness bug: startTime is not propagated to child runners created by subshell(), so builtins executed inside (...) subshells or either side of a | pipeline receive the zero time.Time{} value as callCtx.Now. This causes incorrect behaviour in find -mmin/-mtime predicates and in ls -l timestamp formatting whenever those commands run in a pipeline or subshell context.
Note: GitHub does not allow REQUEST_CHANGES on one's own PR, so this is submitted as COMMENT. The findings below should be treated as blocking.
Overall assessment: needs fixes (1× P1, 1× P2).
P1 — subshell() does not propagate startTime
Location: interp/api.go, subshell() function (around line 487)
Run() correctly captures r.startTime = time.Now(), but subshell() constructs a new runnerState literal that omits startTime. Any builtin (find, ls) called inside a (...) subshell or either side of a | pipeline therefore receives callCtx.Now == time.Time{} — the zero time (year 1, January 1).
Concrete impact:
# find inside a subshell — child startTime is zero;
# Now.Sub(file.ModTime()) is a large negative value,
# so -mmin/-mtime predicates never match recently modified files:
(find . -mmin -60)
# Also broken: either side of a pipe uses subshell(true)
find . -mmin -60 | wc -l
# ls -l in a pipeline — timestamp column (year vs HH:MM)
# always shows year because now.Sub(modtime) > 6 months:
ls -l | headRemediation: Add startTime: r.startTime to the runnerState literal in subshell():
runnerState: runnerState{
Dir: r.Dir,
Params: r.Params,
stdin: r.stdin,
stdout: r.stdout,
stderr: r.stderr,
filename: r.filename,
exit: r.exit,
lastExit: r.lastExit,
startTime: r.startTime, // propagate so builtins in subshells/pipes use the correct time
},P2 — Missing test: startTime propagation into subshells
Location: interp/start_time_test.go
The new test file covers startTime lifecycle on the top-level runner (zero before Run, set by Run, advances on re-run, cleared by Reset), but there is no test verifying that child runners created by subshell() inherit the correct value. Without this, the P1 regression above could silently re-appear.
Suggested addition:
// TestStartTimePropagatedToSubshell verifies that a child runner created by
// subshell() inherits the parent's startTime so builtins in subshells and
// pipelines use the correct time reference.
func TestStartTimePropagatedToSubshell(t *testing.T) {
r, err := New(AllowAllCommands())
require.NoError(t, err)
t.Cleanup(func() { r.Close() })
err = r.Run(context.Background(), parseScript(t, "true"))
require.NoError(t, err)
require.False(t, r.startTime.IsZero())
sub := r.subshell(false)
assert.Equal(t, r.startTime, sub.startTime,
"subshell must inherit parent startTime")
}Positive Observations
- Changing
Nowfromfunc() time.Timetotime.Timeis a well-motivated simplification: it eliminates an indirect call per builtin invocation and makes the value trivially serialisable/inspectable. TestNowConsistentAcrossRootscorrectly validates the single-timestamp guarantee forfind's multi-root walks.- The new
start_time_test.goprovides good coverage of the happy-pathstartTimelifecycle. time.Timeandtime.Noware already on theinterpAllowedSymbolsallowlist, so no allowlist changes are needed.
|
Iteration 1 self-review result: COMMENT (blocking findings) 2 findings: 1×P1, 1×P2 Summary: The PR correctly captures |
subshell() was not copying startTime from the parent runnerState,
so builtins executed inside (...) subshells or either side of a
pipeline received time.Time{} (zero value) as callCtx.Now. This
caused find -mmin/-mtime predicates and ls -l timestamps to behave
incorrectly in those contexts.
Also adds TestStartTimePropagatedToSubshell to prevent regressions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aecc4cbf65
ℹ️ 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".
The startTime field in runnerState uses time.Time, which must be declared in the interp allowed symbols list to pass the TestVerificationInterpCleanPass check. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@codex review this PR Important: Read the SPECS section of the PR description. If SPECS are present: make sure the implementation matches ALL the specs. |
AlexandreYang
left a comment
There was a problem hiding this comment.
Review Summary
This PR refactors CallContext.Now from a func() time.Time to a time.Time value captured once at the start of each Runner.Run() call. The change is internally consistent and correctly propagated through subshells and pipelines.
Assessment: safe to merge — no security issues, no sandbox-integrity violations, no bash compatibility regressions, and all tests pass.
No SPECS section found — skipping spec coverage table.
Findings Summary
Findings
Behavioral note: Now is now a fixed point-in-time for the whole Run() call
Severity: P3 — Informational / low-priority
Location: interp/api.go:374, builtins/ls/ls.go:140, builtins/find/find.go:210
Description: Under the old design, each builtin invocation received time.Now() at the moment the command ran. Now all builtins in a single Run() call share the timestamp captured at the start of Run(). For almost all scripts this is indistinguishable, but a long-running script with many time-predicate commands (e.g. find . -mmin -1; sleep 70; find . -mmin -1) will see the second find use a timestamp that is 70 seconds old. This matches the stated design intent and the comment in CallContext.Now, so it is not a bug — just a documented trade-off worth keeping in mind.
Remediation: No action required. The doc comment on CallContext.Now already documents this. If desired, a code comment at the r.startTime = time.Now() call site could briefly note the implication for long-running scripts.
Removed call-count assertion loses explicit coverage of single-invocation-time property
Severity: P3 — Nice-to-have
Location: builtins/find/now_test.go
Description: The old TestNowCalledOnce test used an atomic counter to assert that the Now() function was called exactly once per find invocation (not once per root path). The new TestNowConsistentAcrossRoots test validates the same behavioral property (consistent timestamp across root paths), but the mechanism that enforced it — a single now := callCtx.Now assignment at the top of run() — is no longer explicitly tested at the unit level. The existing test still provides adequate confidence because it would catch a regression if find were changed to read callCtx.Now inside the per-path walk loop (the timestamp would no longer be consistent). This is a very minor concern.
Remediation: Optionally add a comment in the test explaining that the behavioral invariant (one timestamp per invocation) is now enforced structurally by the value-type Now field rather than by call-count assertion.
Coverage Table
| Code path | Scenario test | Go test | Status |
|---|---|---|---|
startTime set by Run() |
— | interp/start_time_test.go:TestStartTimeSetByRun |
Covered |
startTime propagated to subshell |
— | interp/start_time_test.go:TestStartTimePropagatedToSubshell |
Covered |
startTime cleared by Reset() |
— | interp/start_time_test.go:TestStartTimeResetToZeroByReset |
Covered |
startTime zero before Run() |
— | interp/start_time_test.go:TestStartTimeZeroBeforeRun |
Covered |
startTime updates on re-run |
— | interp/start_time_test.go:TestStartTimeUpdatesOnSubsequentRun |
Covered |
find consistent timestamp across roots |
tests/scenarios/cmd/find/predicates/mmin.yaml |
builtins/find/now_test.go:TestNowConsistentAcrossRoots |
Covered |
ls -l timestamp formatting uses callCtx.Now |
tests/scenarios/cmd/ls/long_format/ |
— | Covered (indirectly) |
Overall coverage: Adequate.
Positive Observations
- Structural enforcement: Changing
Nowfromfunc() time.Timetotime.Timeeliminates an entire class of bugs where a builtin could inadvertently callNow()multiple times and get skewed results across a multi-root traversal. The property is now enforced by the type system rather than by convention. - Correct subshell propagation:
startTimeis explicitly included in therunnerStatecopy insidesubshell(), ensuring pipelines and background subshells share the same timestamp as their parent runner. - Clean
Reset()semantics:runnerStateis replaced wholesale inReset(), which meansstartTimeis correctly zeroed without needing a dedicated clear statement. - Allowed symbols updated:
time.Timewas correctly added tointerpAllowedSymbolsso the import allowlist CI check passes. - No direct
os.*or unsafe filesystem access introduced in interpreter core code. - All tests pass (
go test ./...) andgofmt -l .produces no output.
|
Iteration 2 self-review result: COMMENT (no blocking findings) 2 findings: 0×P0, 0×P1, 0×P2, 2×P3 (informational only) Assessment: safe to merge
|
|
@codex review this PR Important: Read the SPECS section of the PR description. If SPECS are present: make sure the implementation matches ALL the specs. |
AlexandreYang
left a comment
There was a problem hiding this comment.
Review Summary
This PR refactors CallContext.Now from a func() time.Time to a time.Time value captured once at Run() entry. The change ensures all builtins and subshells share a consistent time reference for a single shell execution, matching GNU tool behaviour for commands like find -mmin and ls -lt.
Overall assessment: safe to merge.
No findings. The refactor is straightforward and well-executed.
Findings
No findings.
Coverage
| Code path | Scenario test | Go test | Status |
|---|---|---|---|
startTime zero before Run() |
— | interp/start_time_test.go:TestStartTimeZeroBeforeRun |
Covered |
startTime set on Run() entry |
— | interp/start_time_test.go:TestStartTimeSetByRun |
Covered |
startTime advances on subsequent Run() calls |
— | interp/start_time_test.go:TestStartTimeUpdatesOnSubsequentRun |
Covered |
startTime propagated to subshell |
— | interp/start_time_test.go:TestStartTimePropagatedToSubshell |
Covered |
startTime cleared by Reset() |
— | interp/start_time_test.go:TestStartTimeResetToZeroByReset |
Covered |
find uses single now across multiple root paths |
— | builtins/find/now_test.go:TestNowConsistentAcrossRoots |
Covered |
ls now field access |
existing ls scenario tests |
— | Covered |
Coverage: Adequate
Positive Observations
- Semantics improvement: Changing
Nowfrom afunc() time.Timeto atime.Timevalue accurately models what the field represents (a fixed timestamp, not a live clock). The type now makes the intent impossible to misuse. - Subshell propagation:
startTimeis explicitly included when constructing subshells and pipe runners insubshell(), ensuring full consistency across the shell's execution tree — both foreground subshells and background goroutines in pipelines. - Reset correctness: Since
Reset()replacesrunnerStatewith a fresh struct literal that omitsstartTime, the field is automatically zeroed on reset without any special-casing. This is correct and tested. - Allowlist hygiene: Adding
time.TimetointerpAllowedSymbols(alongside the already-presenttime.Now) is correct and necessary sinceinterp/api.gonow references the type directly for thestartTimefield declaration. - Test removal justified: Removing the
atomic.Int32call-count assertion fromTestNowCalledOnceis correct — the new design makes the test unneeded since the value is passed in as data and cannot be called multiple times. The new test nameTestNowConsistentAcrossRootsmore accurately describes what is actually being verified. - Full test suite passes: All 40+ packages pass with no regressions.
- gofmt clean: No formatting issues detected.
|
Iteration 3 self-review result: APPROVE 0 findings — safe to merge. The refactor is complete, well-tested, and correctly propagates |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac8dec61ae
ℹ️ 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".
|
@codex review this PR Important: Read the SPECS section of the PR description. If SPECS are present: make sure the implementation matches ALL the specs. |
AlexandreYang
left a comment
There was a problem hiding this comment.
Review Summary
This PR refactors CallContext.Now from a func() time.Time function field to a time.Time value field, captured once at the start of Runner.Run() and propagated to subshells. The change enforces the "single consistent timestamp per run" invariant at the type level rather than by convention.
Assessment: safe to merge. No findings — this is an APPROVE.
Note: GitHub prevents approving one's own PR; submitting as COMMENT with APPROVE verdict.
Findings
| # | Priority | File | Finding |
|---|
No findings. Zero P0, zero P1, zero P2, zero P3.
Coverage
| Code path | Scenario test | Go test | Status |
|---|---|---|---|
startTime zero before Run |
— | start_time_test.go:TestStartTimeZeroBeforeRun |
Covered |
startTime set by Run |
— | start_time_test.go:TestStartTimeSetByRun |
Covered |
startTime updates on subsequent Run |
— | start_time_test.go:TestStartTimeUpdatesOnSubsequentRun |
Covered |
startTime propagated to subshell |
— | start_time_test.go:TestStartTimePropagatedToSubshell |
Covered |
startTime cleared by Reset |
— | start_time_test.go:TestStartTimeResetToZeroByReset |
Covered |
find consistent time across roots |
— | find/now_test.go:TestNowConsistentAcrossRoots |
Covered |
Positive Observations
- Type-level enforcement: Converting
Nowfromfunc() time.Timetotime.Timemakes it impossible for a builtin to observe different times within a single run. The invariant is now enforced by the type system, not by calling convention. - Correct subshell propagation:
startTimeis explicitly copied insubshell(), ensuring builtins running in pipelines and subshells share the same reference time as the parent. - Clean Reset semantics:
Reset()rebuildsrunnerStatefrom scratch (zero-value assignment), which naturally clearsstartTime. The testTestStartTimeResetToZeroByResetcorrectly validates this. now_test.gorefactored appropriately: The removedatomic.Int32call-count assertion was only meaningful whenNowwas a function. The replacement test still validates functional correctness (both files found under-mmin -60) without the now-redundant call-count check.allowed_symbolsupdated:time.Timeis correctly added to the interp allowlist to permit the new value field type.- No raw
time.Now()calls in builtins: All builtin code accessescallCtx.Now(the captured value), with the single legitimatetime.Now()call isolated tointerp/api.go:374.
|
Iteration 4 self-review result: APPROVE 0 findings — safe to merge. |
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
|
@codex review this PR Important: Read the SPECS section of the PR description. If SPECS are present: make sure the implementation matches ALL the specs. |
AlexandreYang
left a comment
There was a problem hiding this comment.
Review Summary
This PR refactors CallContext.Now from a func() time.Time closure to a time.Time value captured once at the start of Runner.Run(). The change is a pure internal refactor with no behavioral impact on security boundaries.
Overall assessment: safe to merge.
What was reviewed
builtins/builtins.go—CallContext.Nowfield type change (func() time.Time→time.Time)interp/api.go—startTime time.Timeadded torunnerState; captured inRun(), propagated insubshell(); cleared byReset()(implicit, sinceResetzeroes all ofrunnerState)interp/runner_exec.go—Now: r.startTimeassignment in theCallContextbuilt per builtin callbuiltins/find/find.goandbuiltins/ls/ls.go— callers updated fromcallCtx.Now()tocallCtx.Nowbuiltins/find/now_test.go— test updated to match new API; theatomic.Int32call-count assertion is correctly dropped (no longer meaningful)interp/start_time_test.go— new Go test file with five focused tests covering: zero before Run, set by Run, updated on subsequent Run, propagated to subshell, cleared by Resetallowedsymbols/symbols_interp.go—time.Timeadded to the interp allowed-symbol list
No findings
No P0, P1, P2, or P3 findings.
Security
The change does not affect any sandbox boundary. time.Time is a pure value type with no I/O capability. Adding it to the interpAllowedSymbols list is correct — its comment accurately describes it as pure data with no side effects.
Correctness
startTimeis set after the!r.didResetguard inRun(), so it is always captured on a properly initialised runner.Reset()zeroes the entirerunnerStatestruct, sostartTimeis implicitly cleared to the zero value —TestStartTimeResetToZeroByResetcorrectly verifies this.- Subshells inherit
startTimefrom the parent, ensuring time predicates in pipelines and subshell commands use the same reference as the top-level script —TestStartTimePropagatedToSubshellcovers this. - Subsequent
Run()calls each capture a freshtime.Now(), so stale timestamps don't leak between runs —TestStartTimeUpdatesOnSubsequentRuncovers this. - All production
callCtx.Nowusages (find,ls) have been updated; no remainingcallCtx.Now()call sites exist.
Test coverage
| Code path | Test | Status |
|---|---|---|
startTime zero before first Run |
TestStartTimeZeroBeforeRun |
Covered |
startTime set within [before, after] window by Run |
TestStartTimeSetByRun |
Covered |
startTime advances on subsequent Run calls |
TestStartTimeUpdatesOnSubsequentRun |
Covered |
startTime propagated to subshell |
TestStartTimePropagatedToSubshell |
Covered |
startTime cleared by Reset |
TestStartTimeResetToZeroByReset |
Covered |
find uses consistent time across multiple roots |
TestNowConsistentAcrossRoots |
Covered |
All tests pass (go test ./... clean).
Positive observations
- Replacing a closure with a plain value eliminates a subtle testability concern (the previous API required callers to inject a
func() time.Time— easy to forget or misconfigure). The new design is simpler and harder to misuse. - The
TestStartTimeZeroBeforeRuntest guards against accidental use of a stale timestamp from a previous run — a good defensive assertion. - The
interpAllowedSymbolsupdate is correctly scoped and well-commented.
|
Iteration 5 self-review result: APPROVE 0 findings — safe to merge. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac8dec61ae
ℹ️ 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".
|
@codex review this PR Important: Read the SPECS section of the PR description. If SPECS are present: make sure the implementation matches ALL the specs. |
AlexandreYang
left a comment
There was a problem hiding this comment.
Review Summary
This PR refactors CallContext.Now from a func() time.Time to a time.Time value captured once at the start of Runner.Run(). The change is a clean, targeted refactor with no security, correctness, or bash-compatibility implications.
Assessment: safe to merge. APPROVE (submitted as COMMENT — cannot self-approve).
What was reviewed
builtins/builtins.go—CallContext.Nowfield type changeinterp/api.go—startTimefield added torunnerState, captured inRun(), propagated viasubshell()interp/runner_exec.go—Now: r.startTimepassed toCallContextbuiltins/find/find.go— call site updated fromcallCtx.Now()tocallCtx.Nowbuiltins/ls/ls.go— call site updated similarlybuiltins/find/now_test.go— test updated to reflect new semanticsinterp/start_time_test.go— new tests for zero-before-run, set-by-run, advances-between-runs, subshell propagation, and cleared-by-resetallowedsymbols/symbols_interp.go—time.Timeallowlisted
Findings
No findings. P0: 0, P1: 0, P2: 0, P3: 0.
Coverage
| Code path | Test | Status |
|---|---|---|
startTime zero before Run() |
TestStartTimeZeroBeforeRun |
Covered |
startTime set by Run() |
TestStartTimeSetByRun |
Covered |
startTime advances between Run() calls |
TestStartTimeUpdatesOnSubsequentRun |
Covered |
startTime propagated to subshell |
TestStartTimePropagatedToSubshell |
Covered |
startTime cleared by Reset() |
TestStartTimeResetToZeroByReset |
Covered |
find: consistent Now across roots |
TestNowConsistentAcrossRoots |
Covered |
Positive Observations
- Correct subshell propagation:
startTimeis copied into the childrunnerStateinsubshell(), so pipelines and$(...)substitutions see the same reference time as the parent. - Reset hygiene:
Reset()reinitialises the entirerunnerStatestruct literal without explicitly listingstartTime, which correctly zeroes it — no stale timestamp can leak between reuses. - No security surface change: the
Nowvalue is a read-only time snapshot; changing it from a function to a value cannot introduce any new code execution path or sandbox escape. - All tests pass (
go test ./...) andgofmt -l .reports no formatting violations.
|
Iteration 6 self-review result: APPROVE — 0 findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac8dec61ae
ℹ️ 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".
|
@codex review this PR Important: Read the SPECS section of the PR description. If SPECS are present: make sure the implementation matches ALL the specs. |
AlexandreYang
left a comment
There was a problem hiding this comment.
Review Summary
This PR refactors CallContext.Now from a func() time.Time to a plain time.Time value, captured once at the start of Runner.Run() and propagated to subshells. The change is a pure internal refactor with no user-visible behaviour change.
Overall assessment: safe to merge. No findings.
What was reviewed
builtins/builtins.go—CallContext.Nowfield type changeinterp/api.go—startTimefield addition torunnerState, capture inRun(), propagation insubshell()interp/runner_exec.go— passr.startTimeinstead oftime.NowtoCallContextbuiltins/find/find.go—callCtx.Now()→callCtx.Nowbuiltins/ls/ls.go—callCtx.Now()→callCtx.Nowallowedsymbols/symbols_interp.go— addition oftime.Timeto the allowed symbols listbuiltins/find/now_test.go— test renamed, atomic counter assertion removedinterp/start_time_test.go— five new unit tests forstartTimelifecycle
Security
No security concerns. The change removes a closure (func() time.Time) and replaces it with an immutable value; this strictly reduces the attack surface (no callable function pointer in CallContext). Sandbox invariants are untouched.
Correctness
Reset() replaces r.runnerState wholesale with a freshly zero-initialised struct (lines 321–327 of api.go), so startTime is automatically zeroed — confirmed by TestStartTimeResetToZeroByReset passing.
Subshell propagation is correct: subshell() copies startTime from parent, ensuring pipelines and subshells share the same time reference as the parent Run() call.
startTime is set after the !r.didReset guard, ensuring it is always fresh even when Reset() is called implicitly on first Run().
Test coverage
| Code path | Test | Status |
|---|---|---|
startTime zero before Run |
TestStartTimeZeroBeforeRun |
Covered |
startTime set by Run within expected window |
TestStartTimeSetByRun |
Covered |
startTime advances on subsequent Run calls |
TestStartTimeUpdatesOnSubsequentRun |
Covered |
startTime propagated to subshell |
TestStartTimePropagatedToSubshell |
Covered |
startTime cleared by Reset |
TestStartTimeResetToZeroByReset |
Covered |
find uses consistent timestamp across roots |
TestNowConsistentAcrossRoots |
Covered |
All tests pass locally.
Allowed symbols
time.Time is correctly added to the allowed-symbols list. It is a pure value type with no I/O or side effects, and is now referenced directly by CallContext.Now.
Positive observations
- Removing the function pointer from
CallContextis a good simplification: it eliminates a mutable callable that future code could theoretically misuse and makes the intent ("snapshot at start of run") explicit in the type. - The old
TestNowCalledOncetest was testing an implementation detail (call count via atomic); the newTestNowConsistentAcrossRootstests the observable behaviour (consistent results across root paths), which is a better test design. time.SleepinTestStartTimeUpdatesOnSubsequentRunis the minimal 1 ms needed to guarantee clock advancement — acceptable here.
|
Iteration 7 self-review result: APPROVE — 0 findings. |
|
iteration 29 self-review result: COMMENT (no P0/P1/P2/P3 findings — APPROVE-equivalent; submitted as COMMENT since self-approval is blocked). SPEC coverage: satisfied. All code paths tested. Safe to merge. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 266061ee3b
ℹ️ 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".
| // Capture invocation time once so -mtime/-mmin predicates use a | ||
| // consistent reference across all root paths (matches GNU find). | ||
| now := callCtx.Now() | ||
| now := callCtx.Now |
There was a problem hiding this comment.
Use NowSafe when consuming CallContext.Now
This read bypasses the new zero-value guard and silently treats an unset CallContext.Now as year 0001, which can invert -mmin/-mtime results for any direct builtin caller that constructs CallContext manually. Before this refactor, missing time initialization failed fast (Now function nil-call panic); now it produces plausible-but-wrong output. Since this commit introduced NowSafe() explicitly for this invariant, find (and the analogous ls path) should use it so missing initialization is caught immediately instead of returning incorrect matches.
Useful? React with 👍 / 👎.
|
/merge |
|
View all feedbacks in Devflow UI.
The expected merge time in
DDCI response: DDCI is currently blocked for this repository. Please reach out in #ci-infra-support for additional information. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 696f9e4ac5
ℹ️ 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".
| } | ||
|
|
||
| now := callCtx.Now() | ||
| now := callCtx.Now |
There was a problem hiding this comment.
Use NowSafe when reading CallContext.Now in ls
This line bypasses the new zero-value guard and reintroduces silent year-0001 behavior when CallContext.Now is unset. Before this refactor, an unset Now (nil function) failed fast; after the type change to time.Time, direct ls handler calls (or any future runner regression that forgets to set Now) will produce incorrect long-format timestamp cutoffs instead of surfacing the invariant violation. Using callCtx.NowSafe() here preserves the intended fail-fast contract introduced in this commit.
Useful? React with 👍 / 👎.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
/merge |
|
View all feedbacks in Devflow UI.
The expected merge time in
DDCI response: DDCI is currently blocked for this repository. Please reach out in #ci-infra-support for additional information. |
SPECS
Summary
time.Now()once at shell start and stores it inCallContext.Now(atime.Timevalue), replacing repeatedtime.Now()calls scattered across builtins. This ensures all time predicates (e.g.find -newer,lstimestamps) use a consistent reference point.startTimeinto subshell child runners so the invariant holds across process boundaries.CallContext.NowSafe()which panics with a clear message whenNowis the zero value, turning a silent footgun (year-0001 predicate results) into an immediate programmer error.findandlsto callNowSafe()instead of accessingNowdirectly.time.Timeto the interpreter's allowed symbols list.context.Background()as the parent for per-iteration timeouts (instead oft.Context(), which the fuzz engine may cancel early) and to callcancel()immediately after each command completes rather than deferring.NowSafe()panic and happy-path behaviour, and a test assertingtime.Now()is captured once and consistently propagated across subshell roots.Test plan
go test ./...passesRSHELL_BASH_TEST=1 go test ./tests/ -run TestShellScenariosAgainstBash -timeout 120spasses🤖 Generated with Claude Code