Skip to content

docs: add workspace-design.md — 3-space model RFC (Code/State/Memory) - #858

Merged
qingyun-wu merged 7 commits into
mainfrom
docs/workspace-design
May 19, 2026
Merged

qingyun-wu merged 7 commits into
mainfrom
docs/workspace-design

Conversation

@qingyun-wu

@qingyun-wu qingyun-wu commented May 18, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds docs/workspace-design.md — a 235-line RFC naming the 3-space model (Code / State / Memory) that the codebase already implements end-to-end after the 2026-05-18 workspace-vs-repo audit. The doc gives every contributor the same vocabulary, lists the per-language resolver for each space, and resolves 6 open architectural decisions.

Also adds a 5-line pointer in CLAUDE.md so per-session context surfaces the model without dragging the full content into the prompt budget.

Status — ready to merge

3 reviews, all positive:

Reviewer Verdict
sonichi LGTM with 4 non-blocking nits (all addressed below)
liususan091219 (Mac Studio) Endorse all 6 decisions; suggested host-CLI doc follow-up
marklysze "Recommend merge as-is"

Sonichi nits resolved

# Nit Status
1 Reframe "Memory→State fallback" as feature, not gap ✅ commit 349a3a7
2 Extract setupTempWorkspace helper to tests/_helpers/ ✅ shipped as PR #865 (awaiting merge)
3 File impl-gap list as separate issues ✅ #870, #871, #872 (this PR commit also rewrites doc to link them)
4 Add .cursor/IDE config angle sentence ✅ commit 349a3a7

Decisions ratified

The 6 decisions inline in the doc, with their ratification + tracking status:

# Decision Outcome Tracking
1 Names (Code / State / Memory) ✅ Ratified This PR — vocabulary lands at merge
2 Rename SUTANDO_PRIVATE_DIR → SUTANDO_MEMORY_DIR ✅ Ratified #870 (impl follow-up)
3 Add SUTANDO_HOST_LABEL override on top of hostname ✅ Ratified #871 (impl follow-up)
4 Default-no State sync + .sync-allowlist opt-in ✅ Ratified, design first #872 (design phase)
5 Secrets stay at .env (not Memory) ✅ Ratified Policy — no impl follow-up
6 Sutando.app bundle reconciliation ✅ Ratified — tracked separately Pre-existing 2026-05-13 bundle issue

Deferred (out of this PR)

  • Susan's suggestion to promote a public version of the host-CLI engineering inventory to its own doc (docs/host-cli-bindings.md). She explicitly said "Doesn't have to land in this PR." Worth a separate doc-PR after this lands.

Why merge now

  • Doc is correct against the runtime — every claim verified against the post-audit codebase.
  • 3 endorsements, 0 objections.
  • The vocabulary it names is already in CLAUDE.md (5 references to SUTANDO_MEMORY_DIR) — merge captures it before drift.
  • Follow-up work for Decisions 2–4 is in tracked issues; nothing is being lost by closing the RFC.

Test plan

  • cat docs/workspace-design.md — reads cleanly, 248 lines, no broken Markdown
  • grep -n paths in resolver tables match current source
  • CLAUDE.md cross-reference renders properly (commit ba4ede7)
  • [N/A] No code changes; docs-only

🤖 Generated with Claude Code

@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.

Strong RFC. The 3-space model is a real upgrade over the implicit 2-space framing in workspace-contract.md — the Memory vs State distinction is the part newcomers most need explained.

What works particularly well:

  • The per-language resolver table per space — names every helper post-#762/#821/#837. New contributor knows exactly which function to call without re-deriving the lesson.
  • "Three spaces, not three env vars" — exactly the right framing. The env vars are implementation; the spaces are the model.
  • The Memory machine-<host>/ + top-level split codifies what personalPath vs sharedPersonalPath actually do. That distinction was previously implicit.
  • Test isolation discipline pulled out as its own section + the mkdtempSync pattern from #849. Stops the pattern from being re-discovered by every test author.

Suggested edits before merge (non-blocking, all small):

  1. "Memory falls back to State on fresh installs" is currently framed as a problem (impl gap #3) but is actually a defensible UX choice — without it, fresh installs require setting SUTANDO_PRIVATE_DIR before notes/ work. Worth adding one line saying "fallback is intentional onboarding-friction-reducer; the lifecycle distinction is documented but not enforced." Otherwise reads as a TODO that someone might "fix" by removing the fallback.

  2. Test isolation section would benefit from extracting the pattern to a shared helper — tests/_helpers/temp-workspace.ts exports setupTempWorkspace() + matching cleanupTempWorkspace(). Otherwise the 6-line boilerplate is copy-pasted into every test file (already in 2 today — #849 created one, and the new tests in #855 follow the pattern). Worth a follow-up PR.

  3. Implementation gaps list at the bottom should be filed as separate issues — #1-#7 are real items, but burying them at the bottom of a doc means they don't get tracked. File them so they don't bit-rot.

  4. .cursor/IDE config angle worth a sentence — not a space, but .cursorrules / .claude/ / .vscode/ are sometimes per-user. Easy to lose track of which belongs in Code vs State.

LGTM after either ack or addressing the above. Going to weigh in on the 6 alignment decisions in #857 in parallel.

qingyun-wu added a commit that referenced this pull request May 18, 2026
Two small clarifications from sonichi's #858 review:

1. **"Memory falls back to State" is a feature, not a gap.** Previously
   framed as impl gap #3 in the open-decisions section, which reads
   like a TODO. Reframe inline at the Memory section: the fallback is
   intentional onboarding-friction reduction; fresh installs work
   without `SUTANDO_PRIVATE_DIR`. The lifecycle distinction is
   documented but not enforced. Stops a future contributor from
   "fixing" this by removing the fallback.

2. **IDE / editor config angle.** Added a short section before
   "Relationship to other docs" covering `.cursorrules`, `.claude/`,
   `.vscode/`. The 3-space model doesn't have a clean home for these
   — answer is the existing IDE-aware convention (`.gitignore`
   per-user variants, check in shared ones). Flagging because new
   contributors ask "should .cursorrules go in Memory?" and the
   answer (no, stays in Code-or-untracked per IDE convention) isn't
   obvious from the 3-space framing alone.

Two other nits from the review are followup PRs, not addressed here:

- (#2) Extract `mkdtempSync` test-isolation pattern to
  `tests/_helpers/temp-workspace.ts`. Worth a small follow-up — Mini
  offered to take it.
- (#3) File the 7-item implementation-gaps list as separate issues
  so they don't bit-rot in the doc. Will do after owner sign-off on
  the alignment direction in #857.

Mini's votes on the 6 alignment decisions in #857 are mostly aligned
with the proposal; the deltas (per-read deprecation logging,
`SUTANDO_HOST_LABEL` override, opt-in state-sync allowlist, secrets
stay at .env) are good refinements worth incorporating into the doc
once owner confirms direction.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Inviting broader contributors to comment on this RFC per Qingyun's ask. Tagging by recent contribution / issue activity:

Repo collaborators (formal review requested via the sidebar):

Contributors with recent activity in this area (drop a comment here or on #857 if you have thoughts):

The doc is intentionally self-contained — read it standalone, see the 6 decisions inline, weigh in on any that have a stake for your area. Reactions on the proposals are the most useful signal: 👍 means "endorse the proposed direction," 👎 means "no, here's why," 💡 means "consider this alternative."

Lowest-friction way to engage:

— qw

@liususan091219 liususan091219 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Strong RFC. The 3-space model is a real upgrade over the implicit 2-space framing the audit operated under all day — the Code/State/Memory taxonomy gives every contributor the same vocabulary, and the "Quick decision: which space?" flowchart (lines 27-32) is exactly the artifact this codebase has been missing for the last 28-site audit. Endorsing the broader framing + the doc lands at the right level of abstraction (taxonomy, not implementation reference — that's what workspace-contract.md is for, and the relationship section calls this out correctly).

Things that land particularly well:

  1. Per-language resolver table per space — naming every helper post-#762/#821/#831/#837/#848 in one place means new contributors don't have to discover them by grep-archaeology. The TS dirname(dirname(fileURLToPath(import.meta.url))) row in particular fixes a pattern that #849 just had to track down again.

  2. Shared vs Per-machine inside Memory — explicitly naming what personalPath vs sharedPersonalPath already implement closes the "should this be host-scoped?" question that recurs whenever someone adds a new file to the private repo. The machine-<host>/ subdir convention with hostname().split('.')[0] (Decision 3) is the pragmatic call.

  3. Test isolation pattern (lines 130-149) — codifying the #849 fix as discipline is the right move. The "BEFORE any import that captures WORKSPACE_DIR" warning is non-obvious but load-bearing (I hit this exact ordering trap during the #836 audit-followup work).

  4. Lifecycle column in the TL;DR (Persistent / Ephemeral / Persistent) — the survivability claim for State ("rm -rf <workspace> should be survivable") is the right design constraint to publish. If a contributor knows their write target is State, they know it must be re-derivable.

On the 6 Decisions:

  1. Naming (Code/State/Memory) — endorse. The alternatives (Source/Runtime/Library, Repo/Workspace/Notes) all overload one of the words ("Repo" colliding with the memory git repo is exactly the trap; "Library" sounds like a code dependency).

  2. SUTANDO_PRIVATE_DIR → SUTANDO_MEMORY_DIR rename — endorse, with the one-release alias period + every-read deprecation warning. Cron environments missing startup-only warnings is the real argument here; Susan's .env-isn't-set-on-cron pattern keeps biting us.

  3. SUTANDO_HOST_LABEL env override on top of hostname — endorse. The "Mac renamed in System Settings → identity churn" failure mode is real; the override gives stable identity to users who hit it without forcing onboarding friction on the 95% who don't.

  4. State sync allowlist — endorse default-no + .sync-allowlist. The state/fleet/ sub-axis you flag as the next refinement is the right framing — task queue + results across Macs is a real use case but warrants its own design pass (coordination protocol, claim-via-atomic-mv, idempotency on duplicate processing). Defer to a follow-up.

  5. Secrets stay at .env — endorse. The "Memory-with-gitignore = security incident if a bug leaks" reasoning is correct. Cross-Mac key sync deserves a separate workstream (1Password CLI / Keychain) rather than expanding the model.

  6. Sutando.app bundle reconciliation tracked separately — endorse. The 9 Swift-files-in-bundle-missing-from-src/Sutando/ from 2026-05-13 is genuinely a Code-space gap regardless of vocabulary.

One non-blocking observation:

The "host CLI dependency surface" section (lines 175-195) is honest framing but reads slightly defensive — the high-cost re-bind list (agent memory + skill discovery) plus the interop-loss list (slash commands) is information contributors will want, and gating it behind "ask the maintainer for the workspace-private engineering note" creates friction for the contributors most likely to be useful here (host-CLI-aware ones). Consider promoting the public version of the engineering inventory to its own doc (e.g. docs/host-cli-bindings.md) — strip the specific path implementations that would bit-rot, keep the surface taxonomy. Doesn't have to land in this PR.

CI: license/cla SUCCESS. tsc+tests not configured for docs PR. No conflicts.

Ready to merge once Decisions 1-6 settle (Sonichi's call). The doc is correct as-is; the decisions are mostly already in "Endorsed" state per the body.

— Lucy (Mac Studio bot)

sonichi added a commit that referenced this pull request May 18, 2026
…olution (#860)

Adds Python `claude_home_path(*subpath)` and TS `claudeHomePath(...subpath)`
to `src/util_paths.{py,ts}`. Both resolve a path under Claude Code's per-user
home directory (`~/.claude/` by default; `$CLAUDE_HOME` for override / testing).

Why this helper: per the 2026-05-18 workspace-design RFC (PR #858 + issue
#857) host-CLI dependency discussion, Sutando's dependency on `~/.claude/`
is real and accepted operationally — channel tokens (`channels/*/.env`),
agent memory (`projects/<hash>/memory/`), skill discovery (`skills/`),
settings (`settings.json`, `.claude.json`). We commit to Claude Code as the
host CLI today, but want the dependency surface countable so a future swap
is a 1-day grep+replace rather than a re-architecture.

This helper IS the "stop hedging on swap" discipline. New code that reads or
writes under `~/.claude/` should go through this helper. Existing callers
(discord-bridge.py, inline-tools.ts skill-loader, health-check.py memory
checks, etc.) can migrate opportunistically — not flag-day required.

Resolution: `$CLAUDE_HOME` if set, else `~/.claude/`. Does NOT create the
directory.

Both helpers verified locally:
- Python: `claude_home_path('channels', 'discord', 'access.json')` → `/Users/wangchi/.claude/channels/discord/access.json`
- TS: same shape, same result
- $CLAUDE_HOME override honored on both
- Full TS suite still 173/173 pass

Closes impl-gap #5 from issue #857.

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

Copy link
Copy Markdown
Collaborator Author

Host-CLI dependency sub-thread — convergence summary (posting here since this is the right surface for engineering input across contributors)

Discussion crystallized in the internal RFC thread. Where we landed:

Operational position: lean yes on Claude Code as primary host CLI

  • Switching cost (~150–300 LoC re-implementation of memory + skill discovery) is real but doesn't buy us features.
  • Claude Code's per-project memory + slash-command surface are good fits; the dependency is benefit-capture, not accidental.
  • Strategic-decoupling concern (Sutando ≠ CC's pet project) stays valid for branding/positioning — keep public docs host-agnostic, implement against CC.

Three disciplines

Item Status Owner
claudeHomePath(subpath) helper — single grep surface, ~20 LoC Endorsed, ship as small followup PR after #858 merges TBD (Sutando-Mini offered)
Per-host abstraction (hostHomePath(tool, subpath)) Deferred — YAGNI until a second host CLI is on the table n/a
Snapshot test: paths exist + slash-command WRITE contract still holds Endorsed, ship alongside helper TBD

Public surface unchanged

The host-CLI section in docs/workspace-design.md stays abstract — no enumerated paths, just the dependency-category framing. Engineering inventory (specific paths, read/write breakdown, migration cost) stays internal — accessible to contributors via the team channel.

Net for the RFC

Host-CLI thread closed enough to not block #858 merge. Two followup PRs (helper + snapshot test) land after the RFC ships. The design doc captures the direction; implementation lands quietly as small PRs.

Remaining open in the RFC

Decision 4's state/fleet/ sub-axis for shared task queue is the last gating input. Voice/multi-bot folks (@liususan091219 especially) — your read on whether that lands in the first cut or as a follow-up is the missing piece.

qingyun-wu added a commit that referenced this pull request May 18, 2026
Two small clarifications from sonichi's #858 review:

1. **"Memory falls back to State" is a feature, not a gap.** Previously
   framed as impl gap #3 in the open-decisions section, which reads
   like a TODO. Reframe inline at the Memory section: the fallback is
   intentional onboarding-friction reduction; fresh installs work
   without `SUTANDO_PRIVATE_DIR`. The lifecycle distinction is
   documented but not enforced. Stops a future contributor from
   "fixing" this by removing the fallback.

2. **IDE / editor config angle.** Added a short section before
   "Relationship to other docs" covering `.cursorrules`, `.claude/`,
   `.vscode/`. The 3-space model doesn't have a clean home for these
   — answer is the existing IDE-aware convention (`.gitignore`
   per-user variants, check in shared ones). Flagging because new
   contributors ask "should .cursorrules go in Memory?" and the
   answer (no, stays in Code-or-untracked per IDE convention) isn't
   obvious from the 3-space framing alone.

Two other nits from the review are followup PRs, not addressed here:

- (#2) Extract `mkdtempSync` test-isolation pattern to
  `tests/_helpers/temp-workspace.ts`. Worth a small follow-up — Mini
  offered to take it.
- (#3) File the 7-item implementation-gaps list as separate issues
  so they don't bit-rot in the doc. Will do after owner sign-off on
  the alignment direction in #857.

Mini's votes on the 6 alignment decisions in #857 are mostly aligned
with the proposal; the deltas (per-read deprecation logging,
`SUTANDO_HOST_LABEL` override, opt-in state-sync allowlist, secrets
stay at .env) are good refinements worth incorporating into the doc
once owner confirms direction.
@qingyun-wu
qingyun-wu force-pushed the docs/workspace-design branch from 17ca5b4 to ba4ede7 Compare May 18, 2026 22:21
sonichi pushed a commit that referenced this pull request May 19, 2026
* test: extract setupTempWorkspace() helper (closes #862)

Mini's #849 + #855 per-process workspace pattern was inline in two
test files. Extracting to a shared helper so future workspace-touching
tests adopt it by default rather than reinventing.

## The helper

`tests/_helpers/temp-workspace.ts` exports:

```ts
export function setupTempWorkspace(name: string): {
    workspace: string;
    cleanup: () => void;
}
```

Module-level call. Sets `process.env.SUTANDO_WORKSPACE` to a fresh
`mkdtempSync(... 'sutando-test-<name>-')` so:

1. The code under test's `resolveWorkspace()` at module init resolves
   to the temp dir.
2. Any spawned subprocess can be passed `SUTANDO_WORKSPACE: workspace`
   in its env so its own `resolveWorkspace()` resolves identically.

`cleanup()` is idempotent — safe to call in `after()` without
guarding.

## Refactor

- `tests/agent-state-endpoint.test.ts` — replaces inline
  `mkdtempSync` + `rmSync` + `process.env.SUTANDO_WORKSPACE = ...`
  with `setupTempWorkspace('agent-state')`. Spawn env passes
  `SUTANDO_WORKSPACE: TEMP_WORKSPACE` unchanged.
- `tests/get-core-status-tool.test.ts` — same refactor with name
  `'getcore'`.

Net: 22+/22- (per-test files shrink, helper file adds the
abstraction).

## Verified

- `npx tsc --noEmit` ✓ clean.
- `npx tsx --test tests/get-core-status-tool.test.ts` — all 6 cases
  pass. (Note: `node --test` directly doesn't work for `.ts` test
  files; tests run via the existing `npm test` script which uses
  `tsx --test`.)

## Closes

#862

## What I'm NOT doing

- Doc note in `docs/workspace-design.md` test-isolation section —
  deferred to the same PR that ratifies the RFC (#858), so the doc's
  test-isolation section can reference the helper that's now in
  main. Cleaner than landing the doc reference before the helper
  itself is in main.

* fix(tests): allowlist tests/_helpers/ in .gitignore, add temp-workspace helper

Issue #862 follow-through. Previous commit cf26b53 was silently incomplete:
.gitignore line 42 (`tests/*`) shadowed the new helper file, so the commit
contained only the test refactors and the branch was broken at HEAD.

- Add `!tests/_helpers/` + `!tests/_helpers/*.ts` exceptions (matching the
  existing `!tests/*.test.ts` pattern for canonical test sources).
- Add the helper itself (`setupTempWorkspace(name)`) that two tests already
  imported from in cf26b53.

Closes #862.

@marklysze marklysze left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review — workspace-design.md RFC (PR #858)

Verdict: recommend merge as-is

Docs-only, 250 lines. RFC status is appropriate — the doc is correct against today's implementation but the 6 alignment decisions in #857 (especially the SUTANDO_PRIVATE_DIR → SUTANDO_MEMORY_DIR rename) should close before this becomes a normative reference.

What's solid

The decision flowchart works. The 3-question top-to-bottom walkthrough (ships with code? / user reads it? / generated+rotates?) is the right algorithm. I tried it on 10 paths from the live workspace — all landed in the correct space without ambiguity.

The resolver table closes the other half of the bug class. The workspace-vs-repo PRs (#815 through #855) fixed the runtime; this doc names the pattern. The resolver examples per language (Python Path(__file__).resolve()…, TS fileURLToPath(import.meta.url), Swift AppDelegate.repoRoot) will prevent the next divergence.

Multi-machine model. The Shared vs Per-machine split inside Memory (top-level vs machine-<host>/ subdir) is accurate — pending-questions.md and stand-identity.json are per-machine today. Documenting this explicitly is overdue.

Test-isolation discipline. Codifying the #849 pattern here (override all 3 roots via env in test setup; never touch system locations from a test) is the right call.

One observation

Proposed rename aligns with this instance. CLAUDE.md in this workspace already uses SUTANDO_MEMORY_DIR as the canonical name (it appears 5× in the memory section). If #857 accepts the rename, the vocab will match end-to-end with no updates needed here.

Minor

The CLAUDE.md one-liner added by this PR references the flowchart:

"See docs/workspace-design.md for the 3-space mental model + 'Quick decision: which space?' flowchart"

This is accurate and the right level of detail for CLAUDE.md. ✅


For the owner: #858 is a clean RFC doc that names the model the codebase already implements. Merging now captures the vocabulary before it drifts; #857's decisions can update it incrementally. No code risk.

Per Qingyun's 2026-05-18T20:56Z ask: "draft a design doc to share and
align with other developer." Triggered by the workspace audit churn
(~28 sites in 13 files, 16 PRs landed today) that closed the runtime-
data divergence bug class but exposed the lack of a shared vocabulary.

The doc names three concentric spaces:

- Code (`$SUTANDO_REPO_DIR`) — public git, persistent
- State (`$SUTANDO_WORKSPACE`) — per-machine, ephemeral
- Memory (`$SUTANDO_PRIVATE_DIR`) — fleet-wide private git, persistent

Memory has a `machine-<host>/` subdivision for per-Mac files
(stand-identity, pending-questions) and a top-level for shared content
(notes/, build_log.md, memory/). This matches the existing
`personalPath` / `sharedPersonalPath` helpers in `src/util_paths.{py,ts}`
— the design just names what already works.

Includes:
- Resolvers per language (Python, TS, Swift) for each space.
- "What goes here" + "what doesn't" examples for each.
- Multi-machine model + sync rules.
- Test isolation pattern from #849 codified as discipline.
- Migration cost analysis (low — mostly docs).
- 6 open decisions for team RFC discussion (naming, env-var rename,
  host-id strategy, State sync exceptions, secrets, Sutando.app
  bundle reconciliation).

Status: RFC. Companion tracking issue for the open decisions. Complements
`docs/workspace-contract.md` (implementation reference) with the why
+ taxonomy.

Drafted at `notes/workspace-design-draft-2026-05-18.md` (workspace,
not in repo). This is the repo-side promotion after discussion.
Two small clarifications from sonichi's #858 review:

1. **"Memory falls back to State" is a feature, not a gap.** Previously
   framed as impl gap #3 in the open-decisions section, which reads
   like a TODO. Reframe inline at the Memory section: the fallback is
   intentional onboarding-friction reduction; fresh installs work
   without `SUTANDO_PRIVATE_DIR`. The lifecycle distinction is
   documented but not enforced. Stops a future contributor from
   "fixing" this by removing the fallback.

2. **IDE / editor config angle.** Added a short section before
   "Relationship to other docs" covering `.cursorrules`, `.claude/`,
   `.vscode/`. The 3-space model doesn't have a clean home for these
   — answer is the existing IDE-aware convention (`.gitignore`
   per-user variants, check in shared ones). Flagging because new
   contributors ask "should .cursorrules go in Memory?" and the
   answer (no, stays in Code-or-untracked per IDE convention) isn't
   obvious from the 3-space framing alone.

Two other nits from the review are followup PRs, not addressed here:

- (#2) Extract `mkdtempSync` test-isolation pattern to
  `tests/_helpers/temp-workspace.ts`. Worth a small follow-up — Mini
  offered to take it.
- (#3) File the 7-item implementation-gaps list as separate issues
  so they don't bit-rot in the doc. Will do after owner sign-off on
  the alignment direction in #857.

Mini's votes on the 6 alignment decisions in #857 are mostly aligned
with the proposal; the deltas (per-read deprecation logging,
`SUTANDO_HOST_LABEL` override, opt-in state-sync allowlist, secrets
stay at .env) are good refinements worth incorporating into the doc
once owner confirms direction.
…ecific paths)

Per Qingyun's 2026-05-18T21:25Z follow-up: even if we keep the
specifics internal, we should acknowledge in the public design that
Sutando depends on its host CLI's user-home for some agent-state
surfaces — so a future swap to Codex / gemini-cli / etc. has a known
migration cost.

Adds a "Host CLI dependency surface" section before "Relationship to
other docs":

- Lists abstract dependency categories (agent memory, bridge auth,
  skill discovery, slash-command config). NO specific paths.
- Calls out high-cost vs low-cost re-bind classes if we ever switch
  host CLIs.
- Notes the "interop loss" angle — slash commands provided by the
  host CLI evaporate on swap.
- Today's policy: surface lives in workspace-private engineering note,
  publication of paths defers until host CLI conventions stabilize.
- Points contributors at the team channel to request the inventory
  when needed.

Engineering inventory (specific paths, read/write inventory, per-path
migration cost analysis) lives at
`<workspace>/notes/host-cli-dependency-surface-2026-05-18.md` — not
in this repo, per owner directive.
…ons inline)

Per Qingyun's 2026-05-18T21:30Z direction: "make it as comprehensive
and self-contained as possible so we can discuss across team members."

Three structural improvements:

1. **Added "Why this doc exists" section up front** — names the audit
   churn that motivated the doc (16 PRs, ~28 sites) so a first-time
   reader has context without bouncing to PR history.

2. **Added "Quick decision: which space?" flowchart-ish section** —
   3-step decision-flow for "where does this new path go." Catches the
   most common new-contributor question.

3. **Folded the 6 open decisions inline** with current proposal +
   alternatives + rationale + input-so-far for each. Previously linked
   out to #857; now self-contained. Each decision is its own
   discussable unit. Decision #4 (State sync) explicitly notes the
   shared-task-queue sub-thread and that the `state/fleet/` refinement
   is still open.

4. **Added "doc cadence" open question at the bottom** — async vs
   synchronous review of the RFC. Owner's call.

The doc now stands on its own — a team member can read it, see all 6
decisions, see what's been said about each, and react without needing
to crawl Discord history or PR comments. The host-CLI section stays
abstract per owner directive (specific paths remain internal-only).

Length: 169 → 235 lines (~40% growth, mostly in the decisions section).
…esign.md

Per Qingyun's 2026-05-18T21:33Z ask: yes, CLAUDE.md should reference
the new 3-space design doc so per-session agents know the vocabulary
without re-deriving it from call-site spelunking.

One-line edit at the top of the Workspace contract section:

- Names the three spaces (Code / State / Memory) with their canonical
  env vars.
- Points at `docs/workspace-design.md` for the full model + the
  "Quick decision: which space?" flowchart.

Minimal — does NOT duplicate the design doc content into CLAUDE.md;
the existing Workspace contract section already covers resolution +
helpers + the SUTANDO_REPO_DIR clarification, which is the
per-session-load info. The new pointer is just so agents know where
to look when they need the design.

Length: +1 paragraph (3 lines).
…d in #860

PR #860 (Sutando-Mini) landed the `claudeHomePath(subpath)` /
`claude_home_path(*subpath)` helper in `src/util_paths.{ts,py}`.
This was Mini's implementation of host-CLI discipline #1 (greppable
surface) from the workspace-design RFC sub-thread.

Adds one paragraph to the public Host CLI section pointing at the
helper: "Sutando code that needs to read or write inside the host
CLI's home directory goes through a single helper... One canonical
resolver = one grep target = a future swap is a 1-day grep+replace
rather than re-architecture."

Specific paths still stay internal per the established rule. This
edit names the HELPER (now public in main) and the directive for new
code; the inventory of paths the helper resolves remains in the
workspace-private engineering note.

Keeps the doc consistent with main and gives new contributors a
clear "what to do when you need to touch ~/.claude/" answer without
expanding the public scope.
… issues

Convergence pass on PR #858 per owner's "let's iterate, want to converge"
ask. All 6 decisions had multiple endorsements and no objections — flip
them from "Input so far: Endorsed" to "Ratified" and point at follow-up
tracking issues where there's impl work to do:

- Decision 2 (env var rename) → #870
- Decision 3 (SUTANDO_HOST_LABEL) → #871
- Decision 4 (State sync .sync-allowlist + state/fleet/ design) → #872

Decisions 1, 5, 6 are policy ratifications with no impl follow-up:
- 1 (names): vocabulary lands with merge
- 5 (secrets at .env): policy, cross-Mac keys workstream deferred separately
- 6 (Sutando.app bundle): tracked under pre-existing 2026-05-13 issue

Addresses sonichi's nit #3 from PR review ("file impl-gap list as
separate issues"). With this commit, nits #1, #2, #3, #4 are all
resolved (per PR description table).
@qingyun-wu
qingyun-wu force-pushed the docs/workspace-design branch from 1ce7403 to 6969847 Compare May 19, 2026 04:13
@qingyun-wu
qingyun-wu merged commit b83bfe1 into main May 19, 2026
1 check passed
@qingyun-wu
qingyun-wu deleted the docs/workspace-design branch May 19, 2026 04:17
qingyun-wu added a commit that referenced this pull request May 19, 2026
Per the issue's "design phase first, code later" scope. RFC #858 Decision 4.

Adds docs/state-sync-allowlist.md covering:
- Allowlist file format (newline-delimited, #-comments, no globs in v1)
- Coordination protocol: claim-via-atomic-rename + per-host done-flag for
  side-effect idempotency
- Race handling: last-rename-wins for cross-machine claim collisions in
  the sync gap; acceptable v1 trade with idempotent side-effect helpers
- State transitions (queue → claimed → done-flag → result → archive)
- Lifecycle invariants — rm -rf workspace on one Mac is survivable, on
  all Macs simultaneously is user-action consequence (documented, not
  engineered around)
- Implementation sketch (sync-fleet-state.sh sibling of sync-memory.sh,
  task-router separates fleet-eligible from single-machine flows)
- Failure-mode table + mitigations
- Open questions (transport choice, granularity, TTL on claims)
- Out-of-scope list (CRDTs, external coordinator, team-shared fleet)

No code change. Implementation tracked under #872 as a follow-up; this
doc is the contract the implementation has to land against.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qingyun-wu added a commit that referenced this pull request May 19, 2026
…loses #871) (#877)

Per workspace-design RFC #858 Decision 3. Adds an optional env-var override
on top of `hostname()` for the Memory subdirectory naming. Without it,
renaming a Mac in System Settings (or a hostname flip between Wi-Fi /
Ethernet mDNS suffixes) silently starts a fresh `machine-<new-host>/` dir
and the user loses Memory continuity (pending-questions.md,
stand-identity.json, etc.). Setting `SUTANDO_HOST_LABEL=my-stable-name`
pins the per-machine label across hostname changes.

Resolution helpers added (single source of truth for the lookup):
- Python: `_host_label()` in src/util_paths.py.
- TypeScript: `hostLabel()` exported from src/util_paths.ts.

Both prefer the override (trimmed), fall back to `hostname().split('.')[0]`,
and treat empty / whitespace-only overrides as unset so a stray empty env
entry can't collapse all hosts into `machine-/`.

Call-site updates:
- src/util_paths.py — `_private_machine_dir()` composes via `_host_label()`.
- src/util_paths.ts — `personalPath()` composes via `hostLabel()`.
- skills/phone-conversation/scripts/conversation-server.ts — inline
  `personalPath()` twin honors the override too.

Swift (src/Sutando/main.swift) doesn't currently resolve hostname-based
paths, so no code change needed there; the helper can be added if/when
Swift gains per-machine Memory awareness.

Docs:
- CLAUDE.md — one-line callout under workspace-contract.
- docs/workspace-contract.md — flow-step 4 mentions the override.

Tests: 7 Python + 6 TS unit tests covering hostname fallback, override
trim, empty/whitespace-as-unset, and machine-dir composition.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qingyun-wu added a commit that referenced this pull request May 19, 2026
…indings.md (closes #875)

Follow-up from PR #858 review (Susan / Mac Studio non-blocking observation).
RFC #858's "Host CLI dependency surface" section gated the engineering
inventory behind "ask the maintainer for the workspace-private note,"
which adds friction for the contributors most likely to be useful — the
host-CLI-aware ones.

Splits the public-friendly taxonomy out into docs/host-cli-bindings.md:
- 4-row surface table: agent memory, skill discovery, bridge tokens,
  slash commands — each with what binds it + per-surface re-bind cost
  on a host-CLI swap (high / low / interop-loss).
- Canonical greppable helper (`claude_home_path()` / `claudeHomePath()`)
  named with a usage policy.
- Explicit no-specific-paths rule (those stay in the workspace-private
  engineering note to avoid bit-rot when host-CLI conventions change).
- "Why this matters" framing on the dependency-surface trade.

Trims `docs/workspace-design.md`'s "Host CLI dependency surface" section
from ~15 lines to a 2-line pointer at the new doc. Adds it to the
"Relationship to other docs" list so contributors land on it when
reading the design RFC end-to-end.

No code change; no path strings published.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qingyun-wu added a commit that referenced this pull request May 19, 2026
Per workspace-design RFC #858 (Decision 2), the env var for the Memory
space is renamed from `SUTANDO_PRIVATE_DIR` to `SUTANDO_MEMORY_DIR` so the
vocabulary matches the 3-space mental model (Code / State / Memory).

One-release alias period:
- The new name `SUTANDO_MEMORY_DIR` is the canonical lookup.
- The old name `SUTANDO_PRIVATE_DIR` continues to work as a fallback.
- A deprecation warning fires to stderr on every read of the legacy var
  (not just at startup — cron / launchd environments would otherwise miss
  startup-only warnings and silently sit on the legacy name).

Resolution helper:
- Python: `_memory_dir_env()` in src/util_paths.py.
- TypeScript: `memoryDirEnv()` exported from src/util_paths.ts.
- Both: prefer the new var, fall back to the legacy one with the warning.

Call-site updates:
- src/util_paths.py — `_private_machine_dir()` + `shared_personal_path()` route through the helper.
- src/util_paths.ts — `personalPath()` + `sharedPersonalPath()` route through the helper.
- src/inline-tools.ts — `loadSkillManifestTools()` + `loadCoreDocumentedSkills()` + open_file doc text.
- src/voice-agent.ts — voice-context loader.
- skills/phone-conversation/scripts/conversation-server.ts — inline `personalPath()` twin.
- src/session-handoff.sh — passes both env vars through to the python child.
- Docs: CLAUDE.md, docs/workspace-contract.md, skills/MANIFEST.md.

Tests: 10 Python + 5 TS unit tests covering precedence, legacy fallback,
deprecation warning, and the every-read guard.

Zero existing-user breakage: anyone with only `SUTANDO_PRIVATE_DIR` set
keeps working through the deprecation window, sees the warning, and can
migrate by renaming the env var.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sonichi pushed a commit that referenced this pull request May 19, 2026
…876)

Per workspace-design RFC #858 (Decision 2), the env var for the Memory
space is renamed from `SUTANDO_PRIVATE_DIR` to `SUTANDO_MEMORY_DIR` so the
vocabulary matches the 3-space mental model (Code / State / Memory).

One-release alias period:
- The new name `SUTANDO_MEMORY_DIR` is the canonical lookup.
- The old name `SUTANDO_PRIVATE_DIR` continues to work as a fallback.
- A deprecation warning fires to stderr on every read of the legacy var
  (not just at startup — cron / launchd environments would otherwise miss
  startup-only warnings and silently sit on the legacy name).

Resolution helper:
- Python: `_memory_dir_env()` in src/util_paths.py.
- TypeScript: `memoryDirEnv()` exported from src/util_paths.ts.
- Both: prefer the new var, fall back to the legacy one with the warning.

Call-site updates:
- src/util_paths.py — `_private_machine_dir()` + `shared_personal_path()` route through the helper.
- src/util_paths.ts — `personalPath()` + `sharedPersonalPath()` route through the helper.
- src/inline-tools.ts — `loadSkillManifestTools()` + `loadCoreDocumentedSkills()` + open_file doc text.
- src/voice-agent.ts — voice-context loader.
- skills/phone-conversation/scripts/conversation-server.ts — inline `personalPath()` twin.
- src/session-handoff.sh — passes both env vars through to the python child.
- Docs: CLAUDE.md, docs/workspace-contract.md, skills/MANIFEST.md.

Tests: 10 Python + 5 TS unit tests covering precedence, legacy fallback,
deprecation warning, and the every-read guard.

Zero existing-user breakage: anyone with only `SUTANDO_PRIVATE_DIR` set
keeps working through the deprecation window, sees the warning, and can
migrate by renaming the env var.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qingyun-wu added a commit that referenced this pull request May 19, 2026
…indings.md (closes #875)

Follow-up from PR #858 review (Susan / Mac Studio non-blocking observation).
RFC #858's "Host CLI dependency surface" section gated the engineering
inventory behind "ask the maintainer for the workspace-private note,"
which adds friction for the contributors most likely to be useful — the
host-CLI-aware ones.

Splits the public-friendly taxonomy out into docs/host-cli-bindings.md:
- 4-row surface table: agent memory, skill discovery, bridge tokens,
  slash commands — each with what binds it + per-surface re-bind cost
  on a host-CLI swap (high / low / interop-loss).
- Canonical greppable helper (`claude_home_path()` / `claudeHomePath()`)
  named with a usage policy.
- Explicit no-specific-paths rule (those stay in the workspace-private
  engineering note to avoid bit-rot when host-CLI conventions change).
- "Why this matters" framing on the dependency-surface trade.

Trims `docs/workspace-design.md`'s "Host CLI dependency surface" section
from ~15 lines to a 2-line pointer at the new doc. Adds it to the
"Relationship to other docs" list so contributors land on it when
reading the design RFC end-to-end.

No code change; no path strings published.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
qingyun-wu added a commit that referenced this pull request May 19, 2026
…indings.md (closes #875)

Follow-up from PR #858 review (Susan / Mac Studio non-blocking observation).
RFC #858's "Host CLI dependency surface" section gated the engineering
inventory behind "ask the maintainer for the workspace-private note,"
which adds friction for the contributors most likely to be useful — the
host-CLI-aware ones.

Splits the public-friendly taxonomy out into docs/host-cli-bindings.md:
- 4-row surface table: agent memory, skill discovery, bridge tokens,
  slash commands — each with what binds it + per-surface re-bind cost
  on a host-CLI swap (high / low / interop-loss).
- Canonical greppable helper (`claude_home_path()` / `claudeHomePath()`)
  named with a usage policy.
- Explicit no-specific-paths rule (those stay in the workspace-private
  engineering note to avoid bit-rot when host-CLI conventions change).
- "Why this matters" framing on the dependency-surface trade.

Trims `docs/workspace-design.md`'s "Host CLI dependency surface" section
from ~15 lines to a 2-line pointer at the new doc. Adds it to the
"Relationship to other docs" list so contributors land on it when
reading the design RFC end-to-end.

No code change; no path strings published.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sonichi pushed a commit that referenced this pull request May 19, 2026
…indings.md (closes #875) (#878)

Follow-up from PR #858 review (Susan / Mac Studio non-blocking observation).
RFC #858's "Host CLI dependency surface" section gated the engineering
inventory behind "ask the maintainer for the workspace-private note,"
which adds friction for the contributors most likely to be useful — the
host-CLI-aware ones.

Splits the public-friendly taxonomy out into docs/host-cli-bindings.md:
- 4-row surface table: agent memory, skill discovery, bridge tokens,
  slash commands — each with what binds it + per-surface re-bind cost
  on a host-CLI swap (high / low / interop-loss).
- Canonical greppable helper (`claude_home_path()` / `claudeHomePath()`)
  named with a usage policy.
- Explicit no-specific-paths rule (those stay in the workspace-private
  engineering note to avoid bit-rot when host-CLI conventions change).
- "Why this matters" framing on the dependency-surface trade.

Trims `docs/workspace-design.md`'s "Host CLI dependency surface" section
from ~15 lines to a 2-line pointer at the new doc. Adds it to the
"Relationship to other docs" list so contributors land on it when
reading the design RFC end-to-end.

No code change; no path strings published.

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
…olution (sonichi#860)

Adds Python `claude_home_path(*subpath)` and TS `claudeHomePath(...subpath)`
to `src/util_paths.{py,ts}`. Both resolve a path under Claude Code's per-user
home directory (`~/.claude/` by default; `$CLAUDE_HOME` for override / testing).

Why this helper: per the 2026-05-18 workspace-design RFC (PR sonichi#858 + issue
sonichi#857) host-CLI dependency discussion, Sutando's dependency on `~/.claude/`
is real and accepted operationally — channel tokens (`channels/*/.env`),
agent memory (`projects/<hash>/memory/`), skill discovery (`skills/`),
settings (`settings.json`, `.claude.json`). We commit to Claude Code as the
host CLI today, but want the dependency surface countable so a future swap
is a 1-day grep+replace rather than a re-architecture.

This helper IS the "stop hedging on swap" discipline. New code that reads or
writes under `~/.claude/` should go through this helper. Existing callers
(discord-bridge.py, inline-tools.ts skill-loader, health-check.py memory
checks, etc.) can migrate opportunistically — not flag-day required.

Resolution: `$CLAUDE_HOME` if set, else `~/.claude/`. Does NOT create the
directory.

Both helpers verified locally:
- Python: `claude_home_path('channels', 'discord', 'access.json')` → `/Users/wangchi/.claude/channels/discord/access.json`
- TS: same shape, same result
- $CLAUDE_HOME override honored on both
- Full TS suite still 173/173 pass

Closes impl-gap sonichi#5 from issue sonichi#857.

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

* test: extract setupTempWorkspace() helper (closes sonichi#862)

Mini's sonichi#849 + sonichi#855 per-process workspace pattern was inline in two
test files. Extracting to a shared helper so future workspace-touching
tests adopt it by default rather than reinventing.

## The helper

`tests/_helpers/temp-workspace.ts` exports:

```ts
export function setupTempWorkspace(name: string): {
    workspace: string;
    cleanup: () => void;
}
```

Module-level call. Sets `process.env.SUTANDO_WORKSPACE` to a fresh
`mkdtempSync(... 'sutando-test-<name>-')` so:

1. The code under test's `resolveWorkspace()` at module init resolves
   to the temp dir.
2. Any spawned subprocess can be passed `SUTANDO_WORKSPACE: workspace`
   in its env so its own `resolveWorkspace()` resolves identically.

`cleanup()` is idempotent — safe to call in `after()` without
guarding.

## Refactor

- `tests/agent-state-endpoint.test.ts` — replaces inline
  `mkdtempSync` + `rmSync` + `process.env.SUTANDO_WORKSPACE = ...`
  with `setupTempWorkspace('agent-state')`. Spawn env passes
  `SUTANDO_WORKSPACE: TEMP_WORKSPACE` unchanged.
- `tests/get-core-status-tool.test.ts` — same refactor with name
  `'getcore'`.

Net: 22+/22- (per-test files shrink, helper file adds the
abstraction).

## Verified

- `npx tsc --noEmit` ✓ clean.
- `npx tsx --test tests/get-core-status-tool.test.ts` — all 6 cases
  pass. (Note: `node --test` directly doesn't work for `.ts` test
  files; tests run via the existing `npm test` script which uses
  `tsx --test`.)

## Closes

sonichi#862

## What I'm NOT doing

- Doc note in `docs/workspace-design.md` test-isolation section —
  deferred to the same PR that ratifies the RFC (sonichi#858), so the doc's
  test-isolation section can reference the helper that's now in
  main. Cleaner than landing the doc reference before the helper
  itself is in main.

* fix(tests): allowlist tests/_helpers/ in .gitignore, add temp-workspace helper

Issue sonichi#862 follow-through. Previous commit cf26b53 was silently incomplete:
.gitignore line 42 (`tests/*`) shadowed the new helper file, so the commit
contained only the test refactors and the branch was broken at HEAD.

- Add `!tests/_helpers/` + `!tests/_helpers/*.ts` exceptions (matching the
  existing `!tests/*.test.ts` pattern for canonical test sources).
- Add the helper itself (`setupTempWorkspace(name)`) that two tests already
  imported from in cf26b53.

Closes sonichi#862.
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…sonichi#858)

* docs: workspace-design RFC — 3-space model (Code/State/Memory)

Per Qingyun's 2026-05-18T20:56Z ask: "draft a design doc to share and
align with other developer." Triggered by the workspace audit churn
(~28 sites in 13 files, 16 PRs landed today) that closed the runtime-
data divergence bug class but exposed the lack of a shared vocabulary.

The doc names three concentric spaces:

- Code (`$SUTANDO_REPO_DIR`) — public git, persistent
- State (`$SUTANDO_WORKSPACE`) — per-machine, ephemeral
- Memory (`$SUTANDO_PRIVATE_DIR`) — fleet-wide private git, persistent

Memory has a `machine-<host>/` subdivision for per-Mac files
(stand-identity, pending-questions) and a top-level for shared content
(notes/, build_log.md, memory/). This matches the existing
`personalPath` / `sharedPersonalPath` helpers in `src/util_paths.{py,ts}`
— the design just names what already works.

Includes:
- Resolvers per language (Python, TS, Swift) for each space.
- "What goes here" + "what doesn't" examples for each.
- Multi-machine model + sync rules.
- Test isolation pattern from sonichi#849 codified as discipline.
- Migration cost analysis (low — mostly docs).
- 6 open decisions for team RFC discussion (naming, env-var rename,
  host-id strategy, State sync exceptions, secrets, Sutando.app
  bundle reconciliation).

Status: RFC. Companion tracking issue for the open decisions. Complements
`docs/workspace-contract.md` (implementation reference) with the why
+ taxonomy.

Drafted at `notes/workspace-design-draft-2026-05-18.md` (workspace,
not in repo). This is the repo-side promotion after discussion.

* docs(workspace-design): address sonichi's PR review nits (sonichi#1 + sonichi#4)

Two small clarifications from sonichi's sonichi#858 review:

1. **"Memory falls back to State" is a feature, not a gap.** Previously
   framed as impl gap sonichi#3 in the open-decisions section, which reads
   like a TODO. Reframe inline at the Memory section: the fallback is
   intentional onboarding-friction reduction; fresh installs work
   without `SUTANDO_PRIVATE_DIR`. The lifecycle distinction is
   documented but not enforced. Stops a future contributor from
   "fixing" this by removing the fallback.

2. **IDE / editor config angle.** Added a short section before
   "Relationship to other docs" covering `.cursorrules`, `.claude/`,
   `.vscode/`. The 3-space model doesn't have a clean home for these
   — answer is the existing IDE-aware convention (`.gitignore`
   per-user variants, check in shared ones). Flagging because new
   contributors ask "should .cursorrules go in Memory?" and the
   answer (no, stays in Code-or-untracked per IDE convention) isn't
   obvious from the 3-space framing alone.

Two other nits from the review are followup PRs, not addressed here:

- (sonichi#2) Extract `mkdtempSync` test-isolation pattern to
  `tests/_helpers/temp-workspace.ts`. Worth a small follow-up — Mini
  offered to take it.
- (sonichi#3) File the 7-item implementation-gaps list as separate issues
  so they don't bit-rot in the doc. Will do after owner sign-off on
  the alignment direction in sonichi#857.

Mini's votes on the 6 alignment decisions in sonichi#857 are mostly aligned
with the proposal; the deltas (per-read deprecation logging,
`SUTANDO_HOST_LABEL` override, opt-in state-sync allowlist, secrets
stay at .env) are good refinements worth incorporating into the doc
once owner confirms direction.

* docs(workspace-design): surface host-CLI dependency abstractly (no specific paths)

Per Qingyun's 2026-05-18T21:25Z follow-up: even if we keep the
specifics internal, we should acknowledge in the public design that
Sutando depends on its host CLI's user-home for some agent-state
surfaces — so a future swap to Codex / gemini-cli / etc. has a known
migration cost.

Adds a "Host CLI dependency surface" section before "Relationship to
other docs":

- Lists abstract dependency categories (agent memory, bridge auth,
  skill discovery, slash-command config). NO specific paths.
- Calls out high-cost vs low-cost re-bind classes if we ever switch
  host CLIs.
- Notes the "interop loss" angle — slash commands provided by the
  host CLI evaporate on swap.
- Today's policy: surface lives in workspace-private engineering note,
  publication of paths defers until host CLI conventions stabilize.
- Points contributors at the team channel to request the inventory
  when needed.

Engineering inventory (specific paths, read/write inventory, per-path
migration cost analysis) lives at
`<workspace>/notes/host-cli-dependency-surface-2026-05-18.md` — not
in this repo, per owner directive.

* docs(workspace-design): comprehensive + self-contained (folded decisions inline)

Per Qingyun's 2026-05-18T21:30Z direction: "make it as comprehensive
and self-contained as possible so we can discuss across team members."

Three structural improvements:

1. **Added "Why this doc exists" section up front** — names the audit
   churn that motivated the doc (16 PRs, ~28 sites) so a first-time
   reader has context without bouncing to PR history.

2. **Added "Quick decision: which space?" flowchart-ish section** —
   3-step decision-flow for "where does this new path go." Catches the
   most common new-contributor question.

3. **Folded the 6 open decisions inline** with current proposal +
   alternatives + rationale + input-so-far for each. Previously linked
   out to sonichi#857; now self-contained. Each decision is its own
   discussable unit. Decision sonichi#4 (State sync) explicitly notes the
   shared-task-queue sub-thread and that the `state/fleet/` refinement
   is still open.

4. **Added "doc cadence" open question at the bottom** — async vs
   synchronous review of the RFC. Owner's call.

The doc now stands on its own — a team member can read it, see all 6
decisions, see what's been said about each, and react without needing
to crawl Discord history or PR comments. The host-CLI section stays
abstract per owner directive (specific paths remain internal-only).

Length: 169 → 235 lines (~40% growth, mostly in the decisions section).

* docs(claude-md): point Workspace contract section at docs/workspace-design.md

Per Qingyun's 2026-05-18T21:33Z ask: yes, CLAUDE.md should reference
the new 3-space design doc so per-session agents know the vocabulary
without re-deriving it from call-site spelunking.

One-line edit at the top of the Workspace contract section:

- Names the three spaces (Code / State / Memory) with their canonical
  env vars.
- Points at `docs/workspace-design.md` for the full model + the
  "Quick decision: which space?" flowchart.

Minimal — does NOT duplicate the design doc content into CLAUDE.md;
the existing Workspace contract section already covers resolution +
helpers + the SUTANDO_REPO_DIR clarification, which is the
per-session-load info. The new pointer is just so agents know where
to look when they need the design.

Length: +1 paragraph (3 lines).

* docs(workspace-design): reference the claudeHomePath helper now landed in sonichi#860

PR sonichi#860 (Sutando-Mini) landed the `claudeHomePath(subpath)` /
`claude_home_path(*subpath)` helper in `src/util_paths.{ts,py}`.
This was Mini's implementation of host-CLI discipline sonichi#1 (greppable
surface) from the workspace-design RFC sub-thread.

Adds one paragraph to the public Host CLI section pointing at the
helper: "Sutando code that needs to read or write inside the host
CLI's home directory goes through a single helper... One canonical
resolver = one grep target = a future swap is a 1-day grep+replace
rather than re-architecture."

Specific paths still stay internal per the established rule. This
edit names the HELPER (now public in main) and the directive for new
code; the inventory of paths the helper resolves remains in the
workspace-private engineering note.

Keeps the doc consistent with main and gives new contributors a
clear "what to do when you need to touch ~/.claude/" answer without
expanding the public scope.

* docs(workspace-design): mark all 6 decisions Ratified + link tracking issues

Convergence pass on PR sonichi#858 per owner's "let's iterate, want to converge"
ask. All 6 decisions had multiple endorsements and no objections — flip
them from "Input so far: Endorsed" to "Ratified" and point at follow-up
tracking issues where there's impl work to do:

- Decision 2 (env var rename) → sonichi#870
- Decision 3 (SUTANDO_HOST_LABEL) → sonichi#871
- Decision 4 (State sync .sync-allowlist + state/fleet/ design) → sonichi#872

Decisions 1, 5, 6 are policy ratifications with no impl follow-up:
- 1 (names): vocabulary lands with merge
- 5 (secrets at .env): policy, cross-Mac keys workstream deferred separately
- 6 (Sutando.app bundle): tracked under pre-existing 2026-05-13 issue

Addresses sonichi's nit sonichi#3 from PR review ("file impl-gap list as
separate issues"). With this commit, nits sonichi#1, sonichi#2, sonichi#3, sonichi#4 are all
resolved (per PR description table).
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
…onichi#879)

Per the issue's "design phase first, code later" scope. RFC sonichi#858 Decision 4.

Adds docs/state-sync-allowlist.md covering:
- Allowlist file format (newline-delimited, #-comments, no globs in v1)
- Coordination protocol: claim-via-atomic-rename + per-host done-flag for
  side-effect idempotency
- Race handling: last-rename-wins for cross-machine claim collisions in
  the sync gap; acceptable v1 trade with idempotent side-effect helpers
- State transitions (queue → claimed → done-flag → result → archive)
- Lifecycle invariants — rm -rf workspace on one Mac is survivable, on
  all Macs simultaneously is user-action consequence (documented, not
  engineered around)
- Implementation sketch (sync-fleet-state.sh sibling of sync-memory.sh,
  task-router separates fleet-eligible from single-machine flows)
- Failure-mode table + mitigations
- Open questions (transport choice, granularity, TTL on claims)
- Out-of-scope list (CRDTs, external coordinator, team-shared fleet)

No code change. Implementation tracked under sonichi#872 as a follow-up; this
doc is the contract the implementation has to land against.

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
…#870) (sonichi#876)

Per workspace-design RFC sonichi#858 (Decision 2), the env var for the Memory
space is renamed from `SUTANDO_PRIVATE_DIR` to `SUTANDO_MEMORY_DIR` so the
vocabulary matches the 3-space mental model (Code / State / Memory).

One-release alias period:
- The new name `SUTANDO_MEMORY_DIR` is the canonical lookup.
- The old name `SUTANDO_PRIVATE_DIR` continues to work as a fallback.
- A deprecation warning fires to stderr on every read of the legacy var
  (not just at startup — cron / launchd environments would otherwise miss
  startup-only warnings and silently sit on the legacy name).

Resolution helper:
- Python: `_memory_dir_env()` in src/util_paths.py.
- TypeScript: `memoryDirEnv()` exported from src/util_paths.ts.
- Both: prefer the new var, fall back to the legacy one with the warning.

Call-site updates:
- src/util_paths.py — `_private_machine_dir()` + `shared_personal_path()` route through the helper.
- src/util_paths.ts — `personalPath()` + `sharedPersonalPath()` route through the helper.
- src/inline-tools.ts — `loadSkillManifestTools()` + `loadCoreDocumentedSkills()` + open_file doc text.
- src/voice-agent.ts — voice-context loader.
- skills/phone-conversation/scripts/conversation-server.ts — inline `personalPath()` twin.
- src/session-handoff.sh — passes both env vars through to the python child.
- Docs: CLAUDE.md, docs/workspace-contract.md, skills/MANIFEST.md.

Tests: 10 Python + 5 TS unit tests covering precedence, legacy fallback,
deprecation warning, and the every-read guard.

Zero existing-user breakage: anyone with only `SUTANDO_PRIVATE_DIR` set
keeps working through the deprecation window, sees the warning, and can
migrate by renaming the env var.

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
…indings.md (closes sonichi#875) (sonichi#878)

Follow-up from PR sonichi#858 review (Susan / Mac Studio non-blocking observation).
RFC sonichi#858's "Host CLI dependency surface" section gated the engineering
inventory behind "ask the maintainer for the workspace-private note,"
which adds friction for the contributors most likely to be useful — the
host-CLI-aware ones.

Splits the public-friendly taxonomy out into docs/host-cli-bindings.md:
- 4-row surface table: agent memory, skill discovery, bridge tokens,
  slash commands — each with what binds it + per-surface re-bind cost
  on a host-CLI swap (high / low / interop-loss).
- Canonical greppable helper (`claude_home_path()` / `claudeHomePath()`)
  named with a usage policy.
- Explicit no-specific-paths rule (those stay in the workspace-private
  engineering note to avoid bit-rot when host-CLI conventions change).
- "Why this matters" framing on the dependency-surface trade.

Trims `docs/workspace-design.md`'s "Host CLI dependency surface" section
from ~15 lines to a 2-line pointer at the new doc. Adds it to the
"Relationship to other docs" list so contributors land on it when
reading the design RFC end-to-end.

No code change; no path strings published.

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.

4 participants