Repository navigation
fix(watch-tasks-stream): default fallback to ~/.sutando/workspace/tasks (matches bridge contract) - #779
Conversation
qingyun-wu
left a comment
There was a problem hiding this comment.
LGTM — small, surgical, well-motivated.
Correctness ✅
Verified against src/workspace_default.py:196 resolve_workspace() — that helper returns ~/.sutando/workspace/ when env is unset. The shell fallback now matches exactly. Priority order preserved (positional arg > env > default); the env-set branch is unchanged so happy-path installs aren't touched. $HOME (not ~) is the right form inside double quotes under set -u at line 21 — if $HOME is somehow unset, the script aborts cleanly instead of writing to /.sutando/workspace/tasks.
Conventions ✅
Comment style matches the project's "incident-anchored why" pattern. Both dated incidents + bridge PR cross-refs are present.
Test coverage — partial, defensible
No automated test added. For a 5-line shell fallback with no existing shell-test infrastructure, the 3-branch smoke test in the PR body is a reasonable trade.
Risks
- Migration-ordering edge case (narrow). On a host that pulls this commit before any bridge has called
resolve_workspace()AND env is unset AND legacy<repo>/tasks/has unprocessed tasks: the watchermkdir -p's and polls the new default while legacy tasks still sit at the old path. The bridge's_migrate_from_legacyruns on bridge startup, so any host that also restarts bridges (the standard rollout) closes the gap. Worth a sentence in the rollout note: "aftergit pull, ensure bridges have been started at least once so legacy migration runs." - Stale watcher process — already addressed in the PR body with
pkill -f watch-tasks-stream.sh+ #bot2bot announce. 👍
Optional nits (none blocking)
- The new comment block names the bridges but stops short of pointing at
workspace_default.py::resolve_workspace()as the canonical contract. One line like(canonical contract: src/workspace_default.py::resolve_workspace())would let a future reader trace the source of truth in one hop. - Long-term: a tiny
src/workspace-default.shhelper that mirrorsresolve_workspace()would centralize the next path change. Not for this PR.
Safe to merge once CLA finishes.
6dd8bc4 to
77f86e2
Compare
…repo>/tasks When neither $1 nor $SUTANDO_WORKSPACE is set, the watcher fell back to `<repo>/tasks/` — but the bridges (discord/telegram/dm-result) fall back to `~/.sutando/workspace/tasks/` via `workspace_default.resolve_workspace()`. Result: any deployment that doesn't export SUTANDO_WORKSPACE into the watcher's env has bridges writing one place and the watcher polling another. Tasks land silently — owner DMs reach the bridge, get archived to results/, but the agent never sees the `TASK_FILE:` notification. This affected Chi's MacBook today (~45 min silent gap before manual restart with the env exported); same bug surfaces on any fresh install that doesn't put the env var in the watcher's shell. Switch the fallback to match `workspace_default.default_workspace_dir()` so the watcher and bridges converge by construction. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
77f86e2 to
0182fa2
Compare
sonichi
left a comment
There was a problem hiding this comment.
Verified the fix against main HEAD: src/workspace_default.py exists, bridge imports resolve_workspace() (discord-bridge.py:55), and the function returns ~/.sutando/workspace/ when env is unset. So this PR correctly aligns the watcher's no-env fallback with the bridge's canonical default — convergence-by-construction, not divergence-by-discipline.
One observation for the rollout (already raised in #qw earlier; logging here for the PR record): after this merges, any host with git pull succeeded but Monitor process not restarted is briefly divergent in the opposite direction — old in-memory watcher polls <repo>/tasks/ while bridges (already on new code or restarted) write to ~/.sutando/workspace/tasks/. Same DM-drop failure mode, smaller window. A one-line restart hint in the merge commit body would help.
On Mini's #qw follow-up hardening list (1: startup.sh exports env, 2: health-check probe for watcher_cwd ≠ resolve_workspace(), 3: feedback memory): the health-check probe is the most durable — catches recurrence even when (1) gets reverted or (3) gets forgotten. Worth a follow-up issue if not already filed.
LGTM, no changes requested. Concurring direction.
…on (#780) Author stays the GitHub-noreply email (CLA-Assistant gate). Committer becomes per-host so `git log --format='%h %an / %cn %s'` distinguishes which Sutando fleet host crafted each commit (Mini, MacBook, …). Opt-in by virtue of having stand-identity.json: • Mini → committer = "Echo Act IV (Mini) <mac-mini@noreply.sutando.local>" • MacBook → committer = "<MacBook stand> <macbook@noreply.sutando.local>" • OSS user (no stand-identity.json) → no change Silent fall-through on every "no identity" path: missing file, missing jq, empty/malformed name or machine. No new env var, no new file, no hard dep on jq. Smoke-tested all four paths. Follow-up to today's CLA-Assistant diagnosis (#779, #777): root cause of the silent CLA-pending was Mini's commit author being `Chi <wangchi@Chis-Mac-mini.local>` (OS-default hostname email) while MacBook's was `Chi Wang <4250911+sonichi@users.noreply.github.com>` (GitHub privacy noreply, resolvable). Author is now uniform via repo-local config; committer carries the per-host signal that author used to (badly) carry. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…repo>/tasks (sonichi#779) When neither $1 nor $SUTANDO_WORKSPACE is set, the watcher fell back to `<repo>/tasks/` — but the bridges (discord/telegram/dm-result) fall back to `~/.sutando/workspace/tasks/` via `workspace_default.resolve_workspace()`. Result: any deployment that doesn't export SUTANDO_WORKSPACE into the watcher's env has bridges writing one place and the watcher polling another. Tasks land silently — owner DMs reach the bridge, get archived to results/, but the agent never sees the `TASK_FILE:` notification. This affected Chi's MacBook today (~45 min silent gap before manual restart with the env exported); same bug surfaces on any fresh install that doesn't put the env var in the watcher's shell. Switch the fallback to match `workspace_default.default_workspace_dir()` so the watcher and bridges converge by construction. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on (sonichi#780) Author stays the GitHub-noreply email (CLA-Assistant gate). Committer becomes per-host so `git log --format='%h %an / %cn %s'` distinguishes which Sutando fleet host crafted each commit (Mini, MacBook, …). Opt-in by virtue of having stand-identity.json: • Mini → committer = "Echo Act IV (Mini) <mac-mini@noreply.sutando.local>" • MacBook → committer = "<MacBook stand> <macbook@noreply.sutando.local>" • OSS user (no stand-identity.json) → no change Silent fall-through on every "no identity" path: missing file, missing jq, empty/malformed name or machine. No new env var, no new file, no hard dep on jq. Smoke-tested all four paths. Follow-up to today's CLA-Assistant diagnosis (sonichi#779, sonichi#777): root cause of the silent CLA-pending was Mini's commit author being `Chi <wangchi@Chis-Mac-mini.local>` (OS-default hostname email) while MacBook's was `Chi Wang <4250911+sonichi@users.noreply.github.com>` (GitHub privacy noreply, resolvable). Author is now uniform via repo-local config; committer carries the per-host signal that author used to (badly) carry. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
When
$SUTANDO_WORKSPACEis unset AND no positional arg is given,watch-tasks-stream.shwas falling back to<repo>/tasks/while every bridge (discord-bridge.py,telegram-bridge.py,dm-result.py) falls back to~/.sutando/workspace/tasks/viaworkspace_default.resolve_workspace(). Bridges and watcher diverge, owner DMs land but never reach the agent.Switch the fallback to match the bridge contract.
Why now
SUTANDO_WORKSPACEin its env, watcher polled the legacy<repo>/tasks/, bridge wrote to~/.sutando/workspace/tasks/. Chi: "this will be an issue for other sutandos too." Confirmed — same bug surfaces on any fresh install whose watcher launcher doesn't pin the env.Related: #774 (docs warning for existing-repo installs to pin the env). This is the codepath fix that complements that doc change — fresh installs and any caller that forgot to export now still land on the bridge-canonical default.
Diff
Plus comment update explaining the divergence and the two incidents.
Post-merge restart caveat
Per MacBook bot's review observation: any host that pulls this commit but doesn't restart its Monitor /
watch-tasks-stream.shprocess is briefly divergent in the opposite direction — new code on disk (env-unset →~/.sutando/workspace/tasks/) but the still-running watcher loaded from the old code (env-unset →<repo>/tasks/). Smaller window than the bug being fixed, but same failure mode.Merge / rollout note: after
git pull, restart the watcher:TaskStopthe old watcher task, then re-arm with the same command (now loads new fallback).pkill -f watch-tasks-stream.sh— the per-session caller (proactive-loop / startup.sh) will respawn it.This should also be announced on
#bot2botat merge time so cross-fleet hosts re-sync without dropping DMs in the gap.Test plan
~/.sutando/workspace/tasks(was<repo>/tasks)SUTANDO_WORKSPACE=/x/ws→/x/ws/tasksSUTANDO_WORKSPACE(env branch unchanged)#bot2botfor cross-fleet alignment🤖 Generated with Claude Code