Skip to content

fix(daily-insight): honor SUTANDO_WORKSPACE — write to workspace, not repo - #832

Merged
sonichi merged 2 commits into
mainfrom
fix/daily-insight-honor-workspace
May 18, 2026
Merged

sonichi merged 2 commits into
mainfrom
fix/daily-insight-honor-workspace

Conversation

@qingyun-wu

Copy link
Copy Markdown
Collaborator

Symptom

The 06:50 daily-insight cron has been crashing silently for weeks:

```
FileNotFoundError: [Errno 2] No such file or directory:
'/Users/qingyunwu/Documents/github/sutando/results/insight-2026-05-18.txt'
```

Root cause

`src/daily-insight.py:18` hardcodes:

```python
WORKSPACE = Path(file).parent.parent
```

That resolves to the repo root (`/Users/.../github/sutando/`) — but on this machine the canonical `$SUTANDO_WORKSPACE` is `/Users/.../sutando-workspace-001/`. The script tried to write to `/results/insight-{date}.txt`, which doesn't exist.

Same workspace-path-violation pattern that the bridges had pre-PR #762.

Fix

One-line change: use the canonical `resolve_workspace()` helper from `workspace_default` (the same helper telegram-bridge.py, discord-bridge.py, agent-api.py, dashboard.py, dm-result.py, health-check.py already use).

```diff
sys.path.insert(0, str(Path(file).parent))
from util_paths import shared_personal_path # noqa: E402
+from workspace_default import resolve_workspace # noqa: E402

-WORKSPACE = Path(file).parent.parent
+WORKSPACE = resolve_workspace()
```

Verification

```
$ python3 src/daily-insight.py
Daily insight → /Users/qingyunwu/Documents/github/sutando-workspace-001/results/insight-2026-05-18.txt
You've made 2 calls total. Peak hour: 1:00 (1 calls). Hours with zero calls: 8:00, 9:00, 10:00. Consider scheduling deep work during those quiet windows.
```

Now writes to workspace and produces real insight content. The morning-briefing cron at 06:57 reads from `results/insight-{date}.txt`, so once this is merged the daily insights will actually surface in tomorrow's briefing.

🤖 Generated with Claude Code

sonichi added a commit that referenced this pull request May 18, 2026
#815) (#836)

* fix(workspace): honor SUTANDO_WORKSPACE in 5 scripts — same bug class as PR #815/#832/#833

Audit per owner's "REPO_DIR might be more places than #815" — confirmed 10
bugs across 5 scripts all hitting the same workspace-vs-repo distinction
the bridges fixed pre-PR #762. Each was resolving per-user-state paths
(logs/, results/, state/, build_log.md, core-status.json,
pending-questions.md) to the repo root instead of $SUTANDO_WORKSPACE.

## src/health-check.py — 6 sites

- L314 voice-agent.log freshness check → WORKSPACE_DIR/logs
- L598 core-status.json proactive-loop liveness → WORKSPACE_DIR
- L681 build_log.md critical-file check → WORKSPACE_DIR
- L795 bridge log-staleness check → WORKSPACE_DIR/logs
- L807 bridge heartbeat check → WORKSPACE_DIR/state
- L1171 restart-on-fix subprocess stdout redirect → WORKSPACE_DIR/logs

WORKSPACE_DIR was already wired in this file (line 42 from earlier work);
just substituting the right call sites. Code paths (src/*.py, src/*.ts,
node_modules, .env, Sutando.app binary, subprocess cwd) correctly stay
as REPO_DIR.

Effect: health-check no longer false-positives "log stale / heartbeat
missing / sutando-app not running" when services are actually fine.
Real issues (voice-agent code stale, discord-bridge code stale) now
surface cleanly instead of being lost in the false-positive cascade.

## src/event_log.py

- L53–54: REPO_DIR/logs → WORKSPACE_DIR/logs (LOGS_DIR module global)
- L71: personal_path(...) workspace arg → WORKSPACE_DIR

## src/check-pending-questions.py

- L20: WORKSPACE = Path(__file__).parent.parent → resolve_workspace()

This one was particularly silent — the cron's been firing for weeks
reading the wrong file. After fix: surfaces the 2 long-pending questions
(May 9 email-organize, May 13 web-UI bundle/fork) that the friction
detector also found.

## src/call-stats.py + src/scan-call-logs.py

- Both hardcoded `Path(__file__).parent.parent / "results" / "calls" / ...`
- Now use resolve_workspace() (same pattern as daily-insight.py PR #832 +
  friction-detector.py PR #833)

call-stats verified post-fix: now reads 2 real calls; previously read 0
on workspace-only installs.

## What's NOT in this PR

- health-check.py L693 (notes-dir) — leaving to James's PR #815 to avoid
  conflict; same bug, same fix.

## Verification

  $ python3 src/health-check.py     # core-proactive-loop ok, no false positives
  $ python3 src/check-pending-questions.py  # Notified: 2 pending questions
  $ python3 src/call-stats.py       # 2 real calls, Friday busiest
  $ python3 src/scan-call-logs.py   # No new calls to scan (total: 2)

* test(health-check): mock WORKSPACE_DIR not REPO_DIR for core-status check

PR #836 moved core-status.json read from REPO_DIR to WORKSPACE_DIR (per-user
runtime state, not code). The test still mocked REPO_DIR, so case e
('running with 5-day-old ts should be warn') silently failed against the
real workspace's core-status.json instead of the temp fixture.

Update write_status, with_repo_override, and case_b_status_malformed to
mock WORKSPACE_DIR. All 17 cases pass.

CI flagged by Chi in Discord, 2026-05-18.

---------

Co-authored-by: Chi Wang <4250911+sonichi@users.noreply.github.com>
@qingyun-wu
qingyun-wu force-pushed the fix/daily-insight-honor-workspace branch from 6c6e4c2 to 08c7fad Compare May 18, 2026 18:58
qingyun-wu added a commit that referenced this pull request May 18, 2026
… not repo

Same workspace-path bug as daily-insight.py (PR #832): WORKSPACE was
hardcoded to Path(__file__).parent.parent (repo root), causing
FileNotFoundError when results/friction-{date}.txt couldn't be written
to the wrong path.

Verified before: tried /Users/.../github/sutando/results/friction-2026-05-18.txt → FileNotFoundError
Verified after: writes /Users/.../sutando-workspace-001/results/friction-2026-05-18.txt → OK
Output: surfaces 3 real friction items (2 long-unanswered questions, 1 stale task).

Both this and PR #832 close out the friction-detector + daily-insight
half of the workspace-path-violation pattern previously fixed in the
bridges.
sonichi pushed a commit that referenced this pull request May 18, 2026
… not repo (#833)

Same workspace-path bug as daily-insight.py (PR #832): WORKSPACE was
hardcoded to Path(__file__).parent.parent (repo root), causing
FileNotFoundError when results/friction-{date}.txt couldn't be written
to the wrong path.

Verified before: tried /Users/.../github/sutando/results/friction-2026-05-18.txt → FileNotFoundError
Verified after: writes /Users/.../sutando-workspace-001/results/friction-2026-05-18.txt → OK
Output: surfaces 3 real friction items (2 long-unanswered questions, 1 stale task).

Both this and PR #832 close out the friction-detector + daily-insight
half of the workspace-path-violation pattern previously fixed in the
bridges.
… repo

daily-insight.py hardcoded WORKSPACE = Path(__file__).parent.parent, which
resolves to the repo root (/Users/.../github/sutando/) instead of the
canonical $SUTANDO_WORKSPACE. As a result the daily 06:50 cron crashed
with FileNotFoundError when results/insight-{date}.txt couldn't be
written to a path that doesn't exist under the repo.

Verified:
  Before: tried to write /Users/qingyunwu/Documents/github/sutando/results/insight-2026-05-18.txt → FileNotFoundError
  After:  writes /Users/qingyunwu/Documents/github/sutando-workspace-001/results/insight-2026-05-18.txt → OK
  Output: "You've made 2 calls total. Peak hour: 1:00 ..."

Same workspace-path-violation pattern that landed in earlier fixes for
the bridges; uses the canonical resolve_workspace() helper.

Closes the daily-insight half of feedback_workspace_path_resolution.md.
@qingyun-wu
qingyun-wu force-pushed the fix/daily-insight-honor-workspace branch from 08c7fad to db6c88e Compare May 18, 2026 19:13
sonichi added a commit that referenced this pull request May 18, 2026
closes #840) (#841)

The describe block shares one spawned web-client child for all subtests.
Tool-track 'working' set in one subtest can linger into the next when CI
scheduling delays the async 'clear-tool' POST that the producing test
issues at its tail.

Two unrelated PRs flaked on this same suite today (#836 and #832), both
on workspace-fix PRs that don't touch web-client.ts — confirms the flake
is the test, not the SUT.

Fix: add a beforeEach that POSTs idle to both tool and browser tracks +
a 20ms flush window. Per-subtest baseline removes the order-dependency
that's been latent for the test's lifetime.

Verified: 9/9 subtests pass; full TS suite 162/162 pass.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@sonichi sonichi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

LGTM. Mechanical workspace migration matching the #836/#833/#838 family — daily-insight.py reads from resolve_workspace() instead of repo-walkup. Earlier flake on agent-state-endpoint was the flake fixed in #841 (merged); this re-run is clean. Merging.

@sonichi
sonichi merged commit 07a4bc2 into main May 18, 2026
2 checks passed
@sonichi
sonichi deleted the fix/daily-insight-honor-workspace branch May 18, 2026 19:17
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
sonichi#815) (sonichi#836)

* fix(workspace): honor SUTANDO_WORKSPACE in 5 scripts — same bug class as PR sonichi#815/sonichi#832/sonichi#833

Audit per owner's "REPO_DIR might be more places than sonichi#815" — confirmed 10
bugs across 5 scripts all hitting the same workspace-vs-repo distinction
the bridges fixed pre-PR sonichi#762. Each was resolving per-user-state paths
(logs/, results/, state/, build_log.md, core-status.json,
pending-questions.md) to the repo root instead of $SUTANDO_WORKSPACE.

## src/health-check.py — 6 sites

- L314 voice-agent.log freshness check → WORKSPACE_DIR/logs
- L598 core-status.json proactive-loop liveness → WORKSPACE_DIR
- L681 build_log.md critical-file check → WORKSPACE_DIR
- L795 bridge log-staleness check → WORKSPACE_DIR/logs
- L807 bridge heartbeat check → WORKSPACE_DIR/state
- L1171 restart-on-fix subprocess stdout redirect → WORKSPACE_DIR/logs

WORKSPACE_DIR was already wired in this file (line 42 from earlier work);
just substituting the right call sites. Code paths (src/*.py, src/*.ts,
node_modules, .env, Sutando.app binary, subprocess cwd) correctly stay
as REPO_DIR.

Effect: health-check no longer false-positives "log stale / heartbeat
missing / sutando-app not running" when services are actually fine.
Real issues (voice-agent code stale, discord-bridge code stale) now
surface cleanly instead of being lost in the false-positive cascade.

## src/event_log.py

- L53–54: REPO_DIR/logs → WORKSPACE_DIR/logs (LOGS_DIR module global)
- L71: personal_path(...) workspace arg → WORKSPACE_DIR

## src/check-pending-questions.py

- L20: WORKSPACE = Path(__file__).parent.parent → resolve_workspace()

This one was particularly silent — the cron's been firing for weeks
reading the wrong file. After fix: surfaces the 2 long-pending questions
(May 9 email-organize, May 13 web-UI bundle/fork) that the friction
detector also found.

## src/call-stats.py + src/scan-call-logs.py

- Both hardcoded `Path(__file__).parent.parent / "results" / "calls" / ...`
- Now use resolve_workspace() (same pattern as daily-insight.py PR sonichi#832 +
  friction-detector.py PR sonichi#833)

call-stats verified post-fix: now reads 2 real calls; previously read 0
on workspace-only installs.

## What's NOT in this PR

- health-check.py L693 (notes-dir) — leaving to James's PR sonichi#815 to avoid
  conflict; same bug, same fix.

## Verification

  $ python3 src/health-check.py     # core-proactive-loop ok, no false positives
  $ python3 src/check-pending-questions.py  # Notified: 2 pending questions
  $ python3 src/call-stats.py       # 2 real calls, Friday busiest
  $ python3 src/scan-call-logs.py   # No new calls to scan (total: 2)

* test(health-check): mock WORKSPACE_DIR not REPO_DIR for core-status check

PR sonichi#836 moved core-status.json read from REPO_DIR to WORKSPACE_DIR (per-user
runtime state, not code). The test still mocked REPO_DIR, so case e
('running with 5-day-old ts should be warn') silently failed against the
real workspace's core-status.json instead of the temp fixture.

Update write_status, with_repo_override, and case_b_status_malformed to
mock WORKSPACE_DIR. All 17 cases pass.

CI flagged by Chi in Discord, 2026-05-18.

---------

Co-authored-by: Chi Wang <4250911+sonichi@users.noreply.github.com>
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
… not repo (sonichi#833)

Same workspace-path bug as daily-insight.py (PR sonichi#832): WORKSPACE was
hardcoded to Path(__file__).parent.parent (repo root), causing
FileNotFoundError when results/friction-{date}.txt couldn't be written
to the wrong path.

Verified before: tried /Users/.../github/sutando/results/friction-2026-05-18.txt → FileNotFoundError
Verified after: writes /Users/.../sutando-workspace-001/results/friction-2026-05-18.txt → OK
Output: surfaces 3 real friction items (2 long-unanswered questions, 1 stale task).

Both this and PR sonichi#832 close out the friction-detector + daily-insight
half of the workspace-path-violation pattern previously fixed in the
bridges.
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
closes sonichi#840) (sonichi#841)

The describe block shares one spawned web-client child for all subtests.
Tool-track 'working' set in one subtest can linger into the next when CI
scheduling delays the async 'clear-tool' POST that the producing test
issues at its tail.

Two unrelated PRs flaked on this same suite today (sonichi#836 and sonichi#832), both
on workspace-fix PRs that don't touch web-client.ts — confirms the flake
is the test, not the SUT.

Fix: add a beforeEach that POSTs idle to both tool and browser tracks +
a 20ms flush window. Per-subtest baseline removes the order-dependency
that's been latent for the test's lifetime.

Verified: 9/9 subtests pass; full TS suite 162/162 pass.

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
… repo (sonichi#832)

daily-insight.py hardcoded WORKSPACE = Path(__file__).parent.parent, which
resolves to the repo root (/Users/.../github/sutando/) instead of the
canonical $SUTANDO_WORKSPACE. As a result the daily 06:50 cron crashed
with FileNotFoundError when results/insight-{date}.txt couldn't be
written to a path that doesn't exist under the repo.

Verified:
  Before: tried to write /Users/qingyunwu/Documents/github/sutando/results/insight-2026-05-18.txt → FileNotFoundError
  After:  writes /Users/qingyunwu/Documents/github/sutando-workspace-001/results/insight-2026-05-18.txt → OK
  Output: "You've made 2 calls total. Peak hour: 1:00 ..."

Same workspace-path-violation pattern that landed in earlier fixes for
the bridges; uses the canonical resolve_workspace() helper.

Closes the daily-insight half of feedback_workspace_path_resolution.md.

Co-authored-by: Chi Wang <4250911+sonichi@users.noreply.github.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.

2 participants