Skip to content

cnb: dispatcher keep lead working, not nudge dev idle (#223) - #244

Merged
ApolloZhangOnGithub merged 6 commits into
masterfrom
lead/issue-223-dispatcher-lead-keepalive
May 17, 2026
Merged

cnb: dispatcher keep lead working, not nudge dev idle (#223)#244
ApolloZhangOnGithub merged 6 commits into
masterfrom
lead/issue-223-dispatcher-lead-keepalive

Conversation

@ApolloZhangOnGithub

Copy link
Copy Markdown
Owner

Summary

Inverts the previous nudge model:

  • Workers (dev) idle is normal — they wait for lead to assign work.
    Removed the "OKR continue" nudge that interrupted them with self-driven
    work prompts.
  • Lead idle is anomalous — the org has no engine. Added 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.

Test plan

  • pytest tests/test_nudge_coordinator.py (19 passed)
  • Restart dispatcher, verify lead gets nudged when idle
  • Verify dev sessions no longer get "继续工作 OKR" messages

Closes #223.

ApolloZhangOnGithub and others added 6 commits May 17, 2026 15:44
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>
Copilot AI review requested due to automatic review settings May 17, 2026 08:40
@ApolloZhangOnGithub
ApolloZhangOnGithub merged commit bfb041e into master May 17, 2026
10 of 13 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread bin/board
if identity:
validate_identity(db, identity)

_maybe_check_update(env, cmd.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread VERSION
@@ -1 +1 @@
0.5.76-dev
0.5.79-dev

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep version files in sync

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 👍 / 👎.

ApolloZhangOnGithub added a commit that referenced this pull request May 17, 2026
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>
ApolloZhangOnGithub added a commit that referenced this pull request May 17, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dispatcher 应当保活 lead session — 停了自动 nudge 继续

2 participants