cnb: dispatcher keep lead working, not nudge dev idle (#223) - #244
Conversation
Port the bash version-check from `bin/cnb` into a reusable Python module so tongxue who run `board` directly (skipping the `cnb` wrapper) also detect a stale install and route an update task to the device-supervisor tongxue. Each tongxue does not self-update. - `lib/update_check.py`: pure-Python check_update + helpers, shares the existing on-disk cache (`~/.cnb/latest-version`) and notification suppression (`~/.cnb/update-notified`) with the bash version. - `bin/board`: silent startup hook in main(); never blocks dispatch. `CNB_SKIP_UPDATE_CHECK=1` disables for tests / quick runs. - `board update-check [--force]`: manual trigger for debugging. - Skipped in venv (user-managed install). - Cross-provider versions are normalized: PEP 440 .dev0, npm -dev, v-prefix, prerelease suffixes all compare correctly. - Steady-state overhead is sub-millisecond: cache hit + tuple compare; npm is only re-fetched in the background when the 60-min TTL expires. - 36 unit tests cover normalization, venv detection, owner resolution precedence, notification suppression, async refresh, and the CLI command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to PR #224. Removes the duplicate version-check logic from bin/cnb (~120 lines) so both entry points (`cnb <subcmd>` and the interactive banner) share one implementation in lib/update_check.py. - `bin/cnb` subcommand path (line 165): replaced `_check_update notify` with `bin/board update-check --quiet` (silent stdout; owner still gets notified when stale). - `bin/cnb` banner path: replaced `_check_update` with `bin/board update-check --terminal` (silent unless stale, then prints the historical yellow banner line so UX is unchanged). - Removed `_check_update`, `_notify_update_owner`, `_version_gt`, `_in_virtualenv`, `_CACHE`, `_NOTIFIED` from bash. Kept `_read_update_owner` because `cnb exec` still uses it for sender fallback. - Added `--quiet` and `--terminal` modes to `cmd_update_check`, with tests for each (silent up-to-date, silent stale-quiet, yellow banner on stale-terminal, no banner in venv). - `bin/board` startup hook now skips when the command itself is `update-check` to avoid double-firing the check. 41 update-check tests pass; full suite 1670/1670 (excluding the pre-existing test_board_msg drift bezos is fixing in PR #220). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both from the PR #224 review: 1. Hoist the `is_venv()` check to the top of `cmd_update_check`. The old order ran `--force`'s suppression-clear and 2s sleep before `check_update`'s internal venv guard kicked in, so debugging from a venv shell wasted an npm spawn + wait. 2. Replace `time.sleep(2)` after the async refresh with a new `refresh_latest_version_sync(timeout=10)` helper. The old sleep was a race: a slow npm finished after the sleep returned, defeating `--force`; a fast one made the user wait for nothing. The sync variant blocks until npm returns or times out. Net: 6 new tests (sync-refresh success / timeout / missing npm / non-zero exit / empty stdout; venv-short-circuits-before-refresh). 42/42 update_check tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The subcommand-path hook was synchronously waiting for `bin/board update-check --quiet` to complete before dispatching, which surfaced as an empty-stdout failure on Linux CI for `cnb projects scan --json` (local pass, CI fail across 3.11/3.12/3.13). Detach the hook with `( ... ) & disown` plus full fd redirect so it cannot block or pollute the downstream subcommand whose stdout the test captures. Local repro of the test still passes; this is a defensive bet for the Linux CI failure. The banner path (line ~486) keeps its synchronous `--terminal` call since the user is sitting at the terminal anyway. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reverts the bin/cnb subcommand-path consolidation from #228. Replacing the inline bash `_check_update notify` with `bin/board update-check --quiet` caused two failures on Linux CI that don't repro locally: 1. `test_bin_cnb_projects_scan_dispatches_json_contract` — empty stdout from `cnb projects scan --json` (sync hook somehow interferes with downstream subcommand output capture on Linux). 2. `test_version_subcommand_notifies_lead_when_outdated` — when the prior commit detached the hook with `& disown` to avoid #1, the notification stopped being delivered before the test's inbox check. Either path (sync / async) breaks a different test. The bash version was working before; restore it and let the bin/board startup hook remain as the deliverable for #43. Both paths are now in place — a small amount of duplication, but it's safer to ship than to keep fighting CI. The bin/board hook (the actual KR2 value — tongxue running `board` directly get the check) is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inverts the previous nudge model: - Workers (dev) idle is normal — they wait for lead to assign work. Remove the "OKR continue" nudge that interrupted them with self-driven work prompts. - Lead idle is anomalous — the org has no engine. Add explicit lead keep-alive: when lead is idle, nudge it to scan team status, review PR queue, and proactively dispatch the next issue to free workers. NudgeCoordinator.tick now processes lead separately with a different message and only inbox/lead_idle nudge types (no queued_flush for lead since it's the org root). Bump VERSION to 0.5.79-dev. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 573edcf8cc
ℹ️ 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".
| if identity: | ||
| validate_identity(db, identity) | ||
|
|
||
| _maybe_check_update(env, cmd.name) |
There was a problem hiding this comment.
Skip the update hook for notification sends
When a stale cache exists and no update-notified key has been written yet, this hook also runs for board --as dispatcher send ...; the update notifier itself sends its message by spawning bin/board --as dispatcher send, so the child process re-enters _maybe_check_update before dispatching the send and recursively spawns more board sends until timeouts rather than delivering the notification. This affects both the new Python notifier and the existing bin/cnb bash notifier whenever they try to send the first stale-version message; skip the hook for send or set CNB_SKIP_UPDATE_CHECK=1 on the internal send.
Useful? React with 👍 / 👎.
| @@ -1 +1 @@ | |||
| 0.5.76-dev | |||
| 0.5.79-dev | |||
There was a problem hiding this comment.
VERSION is the canonical input for bin/sync-version --check, which expects package.json to match it and pyproject.toml to contain the PEP 440 form. This change sets VERSION to 0.5.79-dev while the other two files are 0.5.78, so the documented PR/release check fails and the installed runtime version can diverge from the package metadata.
Useful? React with 👍 / 👎.
Three failures introduced by #244 (lead-keepalive refactor) that broke master CI for every downstream PR: 1. **typecheck**: lib/concerns/nudge_coordinator.py:184 had mixed lambda signatures in a list — one with `n="lead"` default, one bare. mypy couldn't infer a unified callable type. Normalized both to no-arg lambdas; the "lead" string is now closed over directly. 2. **lint**: lib/concerns/helpers.py had `# noqa: F401 — re-export for concerns` on `tmux_ok` that ruff RUF100 flagged as unused (downstream usage made F401 already silent). Removed redundant directive. 3. **check-consistency**: #244 bumped VERSION to 0.5.79-dev without running `bin/sync-version`, leaving pyproject.toml and package.json at 0.5.78-dev. Synced all three, then bumped to 0.5.80-dev for this hotfix. Verified locally: - ruff check on full CI invocation: clean - mypy lib/: 65 source files, no issues - bin/sync-version --check: OK - bin/check-{changelog,branding,readme-sync,site-docs}: all OK Same shape as PR #222 (round 1) and PR #230 (round 2) — operational hotfix per CLAUDE.md rule 5 ("operational issues get assigned and executed, not discussed"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three failures introduced by #244 (lead-keepalive refactor) that broke master CI for every downstream PR: 1. **typecheck**: lib/concerns/nudge_coordinator.py:184 had mixed lambda signatures in a list — one with `n="lead"` default, one bare. mypy couldn't infer a unified callable type. Normalized both to no-arg lambdas; the "lead" string is now closed over directly. 2. **lint**: lib/concerns/helpers.py had `# noqa: F401 — re-export for concerns` on `tmux_ok` that ruff RUF100 flagged as unused (downstream usage made F401 already silent). Removed redundant directive. 3. **check-consistency**: #244 bumped VERSION to 0.5.79-dev without running `bin/sync-version`, leaving pyproject.toml and package.json at 0.5.78-dev. Synced all three, then bumped to 0.5.80-dev for this hotfix. Verified locally: - ruff check on full CI invocation: clean - mypy lib/: 65 source files, no issues - bin/sync-version --check: OK - bin/check-{changelog,branding,readme-sync,site-docs}: all OK Same shape as PR #222 (round 1) and PR #230 (round 2) — operational hotfix per CLAUDE.md rule 5 ("operational issues get assigned and executed, not discussed"). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Inverts the previous nudge model:
Removed the "OKR continue" nudge that interrupted them with self-driven
work prompts.
keep-alive: when lead is idle, nudge it to scan team status, review
PR queue, and proactively dispatch the next issue to free workers.
Test plan
pytest tests/test_nudge_coordinator.py(19 passed)Closes #223.