Repository navigation
fix(watch-tasks): honor SUTANDO_WORKSPACE for default TASKS_DIR - #736
Merged
Merged
Conversation
The bridges (discord-bridge.py, telegram-bridge.py, dm-result.py) all honor $SUTANDO_WORKSPACE for their tasks-dir resolution — see PRs #708/#720/#722/#723. But watch-tasks-stream.sh was still defaulting to the repo-relative $(dirname $0)/../tasks, so when Sutando.app sets SUTANDO_WORKSPACE (which it always does), the bridges write tasks to the workspace but the watcher polls the repo. Owner DMs land but the agent never sees them. Diagnosed live 2026-05-15: three Discord DMs were stranded in $SUTANDO_WORKSPACE/tasks/ for ~17 min before the workaround (restart the Monitor with an explicit positional arg). Resolution priority is now: 1. explicit positional arg (back-compat) 2. $SUTANDO_WORKSPACE/tasks (matches bridges) 3. repo-relative fallback (matches old default for non-workspace setups) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sonichi
approved these changes
May 16, 2026
sonichi
left a comment
Owner
There was a problem hiding this comment.
LGTM. Clean three-way priority (explicit arg → $SUTANDO_WORKSPACE/tasks → repo-relative fallback). Aligns with the bridge convention from #708/#720/#722/#723. Back-compat preserved for both edges (explicit positional + no-env-no-arg). Bash logic correct: ${1:-} and ${SUTANDO_WORKSPACE:-} both safe under set -u. Diagnosis details (3 DMs / 17min) clearly identify the failure mode. Safe to merge once CI clears.
4 of 6 tasks
qingyun-wu
added a commit
that referenced
this pull request
May 17, 2026
…readers (#793) * feat(health-check): honor SUTANDO_WORKSPACE for tasks/ + state/ paths Three call-sites in src/health-check.py hardcoded REPO_DIR / "tasks" or REPO_DIR / "state". When the owner pointed $SUTANDO_WORKSPACE at a non- repo path (PR #762), the watcher started reading from $SUTANDO_WORKSPACE/tasks/ while health-check kept writing alerts to <repo>/tasks/ — three task-health alerts on 2026-05-16 landed in the wrong dir before being archived manually. Same drift class as src/watch-tasks-stream.sh pre-#736 and skills/self-diagnose pre-#769. Switches: - check_task_queue (line 624) - emit_task_for_failures defaults (state + tasks) - notify_for_failures default state file Source-code paths (src/, skills/, logs/, .env, build_log.md) keep using REPO_DIR. Tests updated to mutate WORKSPACE_DIR instead of REPO_DIR. Stacks on #762's feat/workspace-default-app-support-subdir. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(phone-conversation): accept TWILIO_WEBHOOK_URL as alternative to NGROK_AUTHTOKEN Startup gate at conversation-server.ts:154 hard-required NGROK_AUTHTOKEN even when TWILIO_WEBHOOK_URL was set (e.g., a Cloudflare/Tailscale tunnel). The runtime tunnel-selection at line ~1654 already handled TWILIO_WEBHOOK_URL — only the startup gate was wrong, causing the server to exit immediately and return 502 at the tunnel for every inbound call. Result on env-pinned hosts: phone was silently broken from 2026-05-13 through 2026-05-17 (Twilio inbound → tunnel → :3100 dead). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sync-memory): derive MEMORY_DIR from SCRIPT_PARENT, not SUTANDO_WORKSPACE scripts/sync-memory.sh:83 computed MEMORY_DIR by sed-mapping REPO_DIR (which is SUTANDO_WORKSPACE or $HOME/Desktop/sutando) to a Claude project key. Claude actually keys its project memory dir on the launch CWD (where the user typed `claude` from), NOT on SUTANDO_WORKSPACE. On any host where the two differ — i.e., any host that follows PR #762's "workspace separate from repo" convention — the sed-mapped key resolves to a path that doesn't exist, then the fallback `find … | head -1` picks whichever sibling memory dir comes first alphabetically. Result on this MacBookPro host: sync-memory.sh has been syncing the WRONG dir since at least 2026-04-11 (date on the oldest memory file that never propagated). Every "Sync complete: N files" output reported success while silently skipping real memory writes. Discovered 2026-05-17 13:46 PT; manual `rsync && git push` repaired the gap by hand (commit e58204e in qingyun-wu/dotsutando). Fix: derive MEMORY_DIR from SCRIPT_PARENT (the script's own parent.parent → the repo root → the Claude-launch CWD on any sane install). NOTES_DIR is unchanged: it correctly follows SUTANDO_WORKSPACE because notes ARE workspace state, not source-tree state. The two-variable distinction matches the REPO_DIR vs WORKSPACE_DIR pattern PR #775 just landed for agent-api.py + github-webhook.py + task-bridge.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(util-paths): honor SUTANDO_WORKSPACE in personal_path + shared_personal_path Both helpers defaulted the `workspace` arg to REPO_DIR (the source tree). On hosts that pin SUTANDO_WORKSPACE to a path separate from the repo (the design PR #762 encourages), every caller that didn't explicitly pass a workspace argument got the source-tree path back instead of the workspace path — so notes/build_log/pending-questions/contextual-chips/ core-status resolution silently used the wrong directory. Concrete impact today: agent writes to `<repo>/notes/` instead of `<workspace>/notes/`, divergence between where bridges read and where agents write, the same workspace-vs-repo conflation bug class PRs #762, #775, and the upcoming health-check/sync-memory fixes are addressing. Fix: new `_workspace_root()` helper honors SUTANDO_WORKSPACE first, falls back to REPO_DIR for back-compat. Default arg in both functions switches from REPO_DIR to `_workspace_root()`. Explicit caller overrides (passing `workspace=...`) keep working unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(dashboard): honor SUTANDO_WORKSPACE for build_log + personal_path callers dashboard.py read build_log + pending-questions from REPO_DIR (the source tree) instead of WORKSPACE_DIR. On hosts that pin SUTANDO_WORKSPACE to a separate path — i.e., the design PR #762 recommends — the dashboard showed a stale-or-empty build_log relative to where the proactive loop actually wrote. Fix: 1. Add WORKSPACE_DIR = resolve_workspace() alongside the preserved REPO_DIR (two-variable split per PR #775). 2. Switch the two build_log path constructions from REPO_DIR to WORKSPACE_DIR. 3. Drop the explicit `REPO_DIR` argument from personal_path / shared_personal_path calls. The util_paths.py fix in the prior commit makes the default honor SUTANDO_WORKSPACE — so callers that pass no workspace argument now get the workspace path automatically, matching the contract. Preserved REPO_DIR usage: - exec'ing src/health-check.py (source path) - `cwd=REPO_DIR` for `git log` calls (git tree) - quota-state.json fallback chain (out of scope; existing fallback works) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(workspace-contract): codify REPO_DIR vs WORKSPACE_DIR split Formalizes the contract that PRs #762, #775, and this PR's individual fixes have been converging on. Audience: OSS contributors deciding where to put new files, plus future-us debugging the next iteration of this bug class. Covers: - The two-variable split (source tree vs runtime state) + when each applies. - Python / TypeScript / Shell resolution patterns. - Four common mistakes (the bug class this contract prevents), each cross-referenced to a fix in this PR. - A 5-step decision tree for new code ("walk top-to-bottom, stop at the first match"). - Cross-references to PR #762 and PR #775 so the lineage of the fix is discoverable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs+fix: tighten the rule — REPO_DIR is source-tree-only Owner-clarified 2026-05-17: REPO_DIR shall not be used for any user/runtime path; only source-code, exec'd scripts, and git-tree metadata get to use REPO_DIR. Earlier drafts of this PR framed the two-variable split as co-equal "use whichever is semantically correct"; the stronger framing is "default to WORKSPACE_DIR; REPO_DIR is the unusual case that needs justification." Three changes apply that stronger rule: 1. docs/workspace-contract.md — rewrite the lead to state the rule directly. Add a "code review test" section: for every REPO_DIR / "..." in a PR diff, ask "is this source code, an exec'd script, or a git cwd?" — if no, it's the bug pattern. 2. src/util_paths.py _workspace_root() — fallback was REPO_DIR for "back-compat"; that was a workaround that perpetuated the bug class. Now delegates to workspace_default.resolve_workspace() which handles SUTANDO_WORKSPACE + the canonical default (~/.sutando/workspace/) + the legacy migration in one call. The inline fallback for environments where workspace_default isn't importable uses the canonical default, never REPO_DIR. 3. src/dashboard.py get_quota_status() — quota-state.json IS runtime state, so the primary read path is now WORKSPACE_DIR. The legacy skill-dir path is preserved as a fallback only for the existing writer (skills/quota-tracker/) until that's migrated separately (out of scope for this PR). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(health-check): check new memory-sync path (~/.sutando/memory-sync/) PR #764 renamed the memory-sync clone dir from ~/.sutando-memory-sync/ to ~/.sutando/memory-sync/, but src/health-check.py:118 still pointed at the legacy path. Result: any host that's completed the auto-migration sees a persistent "memory-sync: repo configured but never synced" warn even though sync is healthy and running on cron — health-check just can't see it. Fix: check the new canonical path first; fall back to the legacy path for installs mid-migration; same warn message but updated to reference the script's actual location (scripts/sync-memory.sh, not the legacy ~/.sutando-memory-sync/scripts/ path). Belongs in this PR because it's the same workspace-path-drift bug class — code wasn't updated after a path canonicalization. Surfaced 2026-05-17 22:36 via a health-check task on the running node. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liususan091219
pushed a commit
to liususan091219/sutando-fork
that referenced
this pull request
May 28, 2026
…chi#736) The bridges (discord-bridge.py, telegram-bridge.py, dm-result.py) all honor $SUTANDO_WORKSPACE for their tasks-dir resolution — see PRs sonichi#708/sonichi#720/sonichi#722/sonichi#723. But watch-tasks-stream.sh was still defaulting to the repo-relative $(dirname $0)/../tasks, so when Sutando.app sets SUTANDO_WORKSPACE (which it always does), the bridges write tasks to the workspace but the watcher polls the repo. Owner DMs land but the agent never sees them. Diagnosed live 2026-05-15: three Discord DMs were stranded in $SUTANDO_WORKSPACE/tasks/ for ~17 min before the workaround (restart the Monitor with an explicit positional arg). Resolution priority is now: 1. explicit positional arg (back-compat) 2. $SUTANDO_WORKSPACE/tasks (matches bridges) 3. repo-relative fallback (matches old default for non-workspace setups) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
liususan091219
pushed a commit
to liususan091219/sutando-fork
that referenced
this pull request
May 28, 2026
…readers (sonichi#793) * feat(health-check): honor SUTANDO_WORKSPACE for tasks/ + state/ paths Three call-sites in src/health-check.py hardcoded REPO_DIR / "tasks" or REPO_DIR / "state". When the owner pointed $SUTANDO_WORKSPACE at a non- repo path (PR sonichi#762), the watcher started reading from $SUTANDO_WORKSPACE/tasks/ while health-check kept writing alerts to <repo>/tasks/ — three task-health alerts on 2026-05-16 landed in the wrong dir before being archived manually. Same drift class as src/watch-tasks-stream.sh pre-sonichi#736 and skills/self-diagnose pre-sonichi#769. Switches: - check_task_queue (line 624) - emit_task_for_failures defaults (state + tasks) - notify_for_failures default state file Source-code paths (src/, skills/, logs/, .env, build_log.md) keep using REPO_DIR. Tests updated to mutate WORKSPACE_DIR instead of REPO_DIR. Stacks on sonichi#762's feat/workspace-default-app-support-subdir. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(phone-conversation): accept TWILIO_WEBHOOK_URL as alternative to NGROK_AUTHTOKEN Startup gate at conversation-server.ts:154 hard-required NGROK_AUTHTOKEN even when TWILIO_WEBHOOK_URL was set (e.g., a Cloudflare/Tailscale tunnel). The runtime tunnel-selection at line ~1654 already handled TWILIO_WEBHOOK_URL — only the startup gate was wrong, causing the server to exit immediately and return 502 at the tunnel for every inbound call. Result on env-pinned hosts: phone was silently broken from 2026-05-13 through 2026-05-17 (Twilio inbound → tunnel → :3100 dead). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sync-memory): derive MEMORY_DIR from SCRIPT_PARENT, not SUTANDO_WORKSPACE scripts/sync-memory.sh:83 computed MEMORY_DIR by sed-mapping REPO_DIR (which is SUTANDO_WORKSPACE or $HOME/Desktop/sutando) to a Claude project key. Claude actually keys its project memory dir on the launch CWD (where the user typed `claude` from), NOT on SUTANDO_WORKSPACE. On any host where the two differ — i.e., any host that follows PR sonichi#762's "workspace separate from repo" convention — the sed-mapped key resolves to a path that doesn't exist, then the fallback `find … | head -1` picks whichever sibling memory dir comes first alphabetically. Result on this MacBookPro host: sync-memory.sh has been syncing the WRONG dir since at least 2026-04-11 (date on the oldest memory file that never propagated). Every "Sync complete: N files" output reported success while silently skipping real memory writes. Discovered 2026-05-17 13:46 PT; manual `rsync && git push` repaired the gap by hand (commit e58204e in qingyun-wu/dotsutando). Fix: derive MEMORY_DIR from SCRIPT_PARENT (the script's own parent.parent → the repo root → the Claude-launch CWD on any sane install). NOTES_DIR is unchanged: it correctly follows SUTANDO_WORKSPACE because notes ARE workspace state, not source-tree state. The two-variable distinction matches the REPO_DIR vs WORKSPACE_DIR pattern PR sonichi#775 just landed for agent-api.py + github-webhook.py + task-bridge.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(util-paths): honor SUTANDO_WORKSPACE in personal_path + shared_personal_path Both helpers defaulted the `workspace` arg to REPO_DIR (the source tree). On hosts that pin SUTANDO_WORKSPACE to a path separate from the repo (the design PR sonichi#762 encourages), every caller that didn't explicitly pass a workspace argument got the source-tree path back instead of the workspace path — so notes/build_log/pending-questions/contextual-chips/ core-status resolution silently used the wrong directory. Concrete impact today: agent writes to `<repo>/notes/` instead of `<workspace>/notes/`, divergence between where bridges read and where agents write, the same workspace-vs-repo conflation bug class PRs sonichi#762, sonichi#775, and the upcoming health-check/sync-memory fixes are addressing. Fix: new `_workspace_root()` helper honors SUTANDO_WORKSPACE first, falls back to REPO_DIR for back-compat. Default arg in both functions switches from REPO_DIR to `_workspace_root()`. Explicit caller overrides (passing `workspace=...`) keep working unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(dashboard): honor SUTANDO_WORKSPACE for build_log + personal_path callers dashboard.py read build_log + pending-questions from REPO_DIR (the source tree) instead of WORKSPACE_DIR. On hosts that pin SUTANDO_WORKSPACE to a separate path — i.e., the design PR sonichi#762 recommends — the dashboard showed a stale-or-empty build_log relative to where the proactive loop actually wrote. Fix: 1. Add WORKSPACE_DIR = resolve_workspace() alongside the preserved REPO_DIR (two-variable split per PR sonichi#775). 2. Switch the two build_log path constructions from REPO_DIR to WORKSPACE_DIR. 3. Drop the explicit `REPO_DIR` argument from personal_path / shared_personal_path calls. The util_paths.py fix in the prior commit makes the default honor SUTANDO_WORKSPACE — so callers that pass no workspace argument now get the workspace path automatically, matching the contract. Preserved REPO_DIR usage: - exec'ing src/health-check.py (source path) - `cwd=REPO_DIR` for `git log` calls (git tree) - quota-state.json fallback chain (out of scope; existing fallback works) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(workspace-contract): codify REPO_DIR vs WORKSPACE_DIR split Formalizes the contract that PRs sonichi#762, sonichi#775, and this PR's individual fixes have been converging on. Audience: OSS contributors deciding where to put new files, plus future-us debugging the next iteration of this bug class. Covers: - The two-variable split (source tree vs runtime state) + when each applies. - Python / TypeScript / Shell resolution patterns. - Four common mistakes (the bug class this contract prevents), each cross-referenced to a fix in this PR. - A 5-step decision tree for new code ("walk top-to-bottom, stop at the first match"). - Cross-references to PR sonichi#762 and PR sonichi#775 so the lineage of the fix is discoverable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs+fix: tighten the rule — REPO_DIR is source-tree-only Owner-clarified 2026-05-17: REPO_DIR shall not be used for any user/runtime path; only source-code, exec'd scripts, and git-tree metadata get to use REPO_DIR. Earlier drafts of this PR framed the two-variable split as co-equal "use whichever is semantically correct"; the stronger framing is "default to WORKSPACE_DIR; REPO_DIR is the unusual case that needs justification." Three changes apply that stronger rule: 1. docs/workspace-contract.md — rewrite the lead to state the rule directly. Add a "code review test" section: for every REPO_DIR / "..." in a PR diff, ask "is this source code, an exec'd script, or a git cwd?" — if no, it's the bug pattern. 2. src/util_paths.py _workspace_root() — fallback was REPO_DIR for "back-compat"; that was a workaround that perpetuated the bug class. Now delegates to workspace_default.resolve_workspace() which handles SUTANDO_WORKSPACE + the canonical default (~/.sutando/workspace/) + the legacy migration in one call. The inline fallback for environments where workspace_default isn't importable uses the canonical default, never REPO_DIR. 3. src/dashboard.py get_quota_status() — quota-state.json IS runtime state, so the primary read path is now WORKSPACE_DIR. The legacy skill-dir path is preserved as a fallback only for the existing writer (skills/quota-tracker/) until that's migrated separately (out of scope for this PR). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(health-check): check new memory-sync path (~/.sutando/memory-sync/) PR sonichi#764 renamed the memory-sync clone dir from ~/.sutando-memory-sync/ to ~/.sutando/memory-sync/, but src/health-check.py:118 still pointed at the legacy path. Result: any host that's completed the auto-migration sees a persistent "memory-sync: repo configured but never synced" warn even though sync is healthy and running on cron — health-check just can't see it. Fix: check the new canonical path first; fall back to the legacy path for installs mid-migration; same warn message but updated to reference the script's actual location (scripts/sync-memory.sh, not the legacy ~/.sutando-memory-sync/scripts/ path). Belongs in this PR because it's the same workspace-path-drift bug class — code wasn't updated after a path canonicalization. Surfaced 2026-05-17 22:36 via a health-check task on the running node. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Aligns
src/watch-tasks-stream.shwith the rest of the bridges (discord, telegram, dm-result), which all honor\$SUTANDO_WORKSPACEper PRs #708/#720/#722/#723. Without this, when Sutando.app setsSUTANDO_WORKSPACE(which it always does), the bridges write tasks to the workspace but the watcher polls the repo — owner DMs land but the agent never sees them.Diagnosed live
2026-05-15 — three Discord DMs were stranded in `$SUTANDO_WORKSPACE/tasks/` for ~17 min before the manual workaround (restart the Monitor with an explicit positional arg). Detailed in build_log.
Resolution priority
Test plan
🤖 Generated with Claude Code