Repository navigation
Conversation
Auto-update waited 72 hours after a release and checked once a day, so a new release reached a laptop 3 to 4 days after its publication. - update.wait (default 24h, 0 or more): how long after its publication agentop update --auto waits before it installs a release. agentop reads it on each run. - update.check_every (default 6h, 1h to 24h): how often the proxy starts the updater. The proxy reads it at boot, so a hot reload refuses a change. - Validate refuses a bad value. agentop doctor shows it on the updates row. A bad wait does not cancel update.auto: false. With the defaults, a laptop that is on installs a release 24 to about 30 hours after its publication. Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Hai Huang <huang195@gmail.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 32 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
clawgenti
left a comment
There was a problem hiding this comment.
Summary
The timing knobs work as described: I probed Times() through config.Load with 0, "0", 0s, -0, -1h, 1d, 24 (no unit), 1.5h, true, null, "", a leading space, 59m, 25h and 300000h. Each one is accepted or refused as the docs say, and the boot/reload split for check_every is right. There is one must-fix. Because autoUpdateOff now shares a decode with the new wait/check_every fields, a wrong-type value in either field (a list or a map) silently turns an explicit update.auto: false back on. On the base commit the same config stays disabled. Details are inline.
Author: huang195 (MEMBER — maintainer)
Areas reviewed: Go (core/config, core/reloader, cmd/cortex, cmd/agentop), Docs
Agent/IDE config (.claude/.vscode): none
Commits: 1 commit, all signed-off: yes
CI status: passing
Local checks at c5f9e70: go test ./config/ ./reloader/ (core), the UpdateTrigger tests (cmd/cortex) and the Update tests (cmd/agentop) pass, and gofmt -l is clean. The PR also merges cleanly with main after #1840.
| @@ -108,9 +116,9 @@ func autoUpdateOff(path string) bool { | |||
| return "${" + key + "}" | |||
| }) | |||
| if yaml.Unmarshal([]byte(expanded), &c) != nil { | |||
There was a problem hiding this comment.
must-fix: a wrong-type wait or check_every cancels an explicit update.auto: false.
autoUpdateOff used to decode a struct that had only auto. Now it shares readUpdateConfig, which decodes the whole UpdateConfig, so a type error in either new string field makes yaml.Unmarshal return an error. This line then returns nil, and nil means "auto on". I probed runUpdate --auto with the autoUpdateRows harness, a release 100h old:
| config | base 608e8d2 | head c5f9e70 |
|---|---|---|
auto: false + wait: [24h] |
disabled | updated, installer ran |
auto: false + wait: {h: 24} |
disabled | updated, installer ran |
auto: false + check_every: [6h] |
disabled | updated, installer ran |
auto: false + wait: soon |
disabled | disabled |
This breaks the PR's own claim ("A bad wait gives the default and does not cancel update.auto: false") and the comment on readUpdateConfig ("an error in another field does not cancel the opt-out"). The new wait invalid, disabled row only covers a scalar. agentop doctor then shows "auto on" too, because Times() on a nil block reports no error. The proxy will not start with such a file, but a hot-reload edit leaves the old proxy running, and then the updater installs a release and restarts the proxy against the user's opt-out.
Two possible fixes:
- Keep a separate
auto-only decode forautoUpdateOff, as before, and usereadUpdateConfigonly for the timings. - Or treat a
*yaml.TypeErroras a partial decode. yaml.v3 keeps decoding the other fields after a type error, soc.Update.Autois still set. In that case return the block (or a marker) so thatTimes()/doctor can report the bad field.
Please add a list-valued wait row with auto: false to autoUpdateRows.
| // validateReloadable returns an error when a newly-loaded config | ||
| // differs from the active one on a field that cannot be reloaded | ||
| // under the running listeners. | ||
| func updateCheckEvery(c *config.Config) string { |
There was a problem hiding this comment.
nit: the new helper sits between the validateReloadable doc comment (lines 578-580) and the function it describes. So godoc now attaches that comment to updateCheckEvery, and validateReloadable has none. Move the helper above the comment, or below validateReloadable.
| } | ||
| // update.check_every too: the update trigger reads it once at boot. update.auto | ||
| // and update.wait reload, because agentop reads them on each run. | ||
| if updateCheckEvery(active) != updateCheckEvery(next) { |
There was a problem hiding this comment.
nit: this compares the raw strings, so equivalent values are refused as changes. Examples: unset to 6h (the default), and 6h to 360m. The safe direction is to refuse, so this is fine. If you want the message to match the behavior, compare the durations from Times() instead (both configs passed Validate already).
clawgenti
left a comment
There was a problem hiding this comment.
Summary
Re-review at 503f39e. This commit only merges main (#1840) into the branch. The PR's own changes are byte-identical to c5f9e70, and the merge left no stale "72 hours" or "daily" text in the update docs or code. The must-fix from my previous review is still open: a wrong-type update.wait or update.check_every (a list or a map) makes readUpdateConfig return nil, and that silently turns an explicit update.auto: false back on. I added two rows to autoUpdateRows at 503f39e (auto: false with wait: [24h], and auto: false with check_every: [6h], a release 100h old). Both give result "updated" ran true, want "disabled". On base, the same configs stay disabled. The fix options are in the earlier inline thread on cmd/agentop/cmd_update_auto.go, and one new inline comment below marks the line. The two nits in core/reloader/reloader.go (the misplaced doc comment, and the raw-string compare of check_every) are still open. They do not block.
Author: @huang195 (MEMBER — maintainer)
Areas reviewed: Go (core/config, core/reloader, cmd/cortex, cmd/agentop), Docs
Agent/IDE config (.claude/.vscode): none
Commits: 1 commit plus 1 merge commit. The feature commit is signed off, and DCO passes.
CI status: 34 pass, 1 pending, 2 skipping, none failing
Local checks at 503f39e: go test ./config/ ./reloader/ (core), go test -run UpdateTrigger (cmd/cortex) and go test -run 'Update|Doctor' (cmd/agentop) pass, and gofmt -l is clean.
| @@ -108,9 +116,9 @@ func autoUpdateOff(path string) bool { | |||
| return "${" + key + "}" | |||
| }) | |||
| if yaml.Unmarshal([]byte(expanded), &c) != nil { | |||
There was a problem hiding this comment.
must-fix (still open at 503f39e): when wait or check_every has the wrong type, yaml.Unmarshal returns a *yaml.TypeError. This line then returns nil, so autoUpdateOff reports auto on even with auto: false. I reproduced it at this head with auto: false + wait: [24h] and with auto: false + check_every: [6h]: the installer ran in both cases. Please keep an auto-only decode for the opt-out, or treat a *yaml.TypeError as a partial decode (yaml.v3 still sets Auto). Please also add a list-valued row with auto: false to autoUpdateRows.
Summary
Auto-update (#1838) waited 72 hours after a release and checked once a day. So a new release reached a laptop 3 to 4 days after its publication. This PR makes the defaults shorter and makes both values configurable in
~/.cortex/config.yaml:waitagentop update --auto, on each runcheck_everycmd/cortex/update_trigger.go), at bootWith the defaults, a laptop that is on installs a release 24 to about 30 hours after its publication.
What changed:
core/config:UpdateConfig.Wait,UpdateConfig.CheckEveryandTimes().Validaterefuses a bad value.core/reloader:validateReloadablerefuses anupdate.check_everychange.autoandwaitstill reload.cmd/cortex: the trigger usescheck_everyin place of the fixed 24 hours.cmd/agentop:--autoreadsupdate.wait. It reads only theupdate:block, as before. A badwaitgives the default and does not cancelupdate.auto: false.agentop doctorshows a bad value on theupdatesrow.docs/laptop-service.md(a table of the fields),CONTRIBUTING.md(stopping a bad release),cmd/agentop/README.md, theagentop updatehelp text, and the restart list inCLAUDE.md.Why the range of
check_every:Not changed: the 72-hour "no update check since" limit in the TUI and in doctor. With a check at most every 24 hours, it still means 3 missed checks or more.
How it was tested
core:go vet ./...,go test ./config/... ./reloader/.... New tests:TestUpdateTimes,TestValidateRefusesBadUpdateTimes,TestValidateReloadable_UpdateCheckEveryNeedsRestart.cmd/cortex:go vet ./...,go test ./.... The trigger tests use a 6-hour interval.TestUpdateTriggerUsesEverychecks a 24-hour interval.cmd/agentop:scripts/dev/go-test-shards.sh -race cmd/agentop(1051 tests in 8 shards, passed) andgo test -raceof the subpackages (passed). New rows inTestAutoUpdateDecisions:wait set,wait 0,wait invalid,wait invalid, disabled. New test:TestDoctorUpdatesBadTimes.gofmt -lis clean.pre-commit runon the changed files passed.Not tested: a live update on a Mac or on Linux.
Note for #1840
This PR changes the
--autohelp line incmd/agentop/cmd_update.go. If #1840 changes the same text, the second PR to merge must resolve a small conflict.Assisted-By: Claude (Anthropic AI) noreply@anthropic.com