Repository navigation
docs: add workspace-design.md — 3-space model RFC (Code/State/Memory) - #858
Conversation
sonichi
left a comment
There was a problem hiding this comment.
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 whatpersonalPathvssharedPersonalPathactually do. That distinction was previously implicit. - Test isolation discipline pulled out as its own section + the
mkdtempSyncpattern from #849. Stops the pattern from being re-discovered by every test author.
Suggested edits before merge (non-blocking, all small):
-
"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_DIRbefore 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. -
Test isolation section would benefit from extracting the pattern to a shared helper —
tests/_helpers/temp-workspace.tsexportssetupTempWorkspace()+ matchingcleanupTempWorkspace(). 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. -
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.
-
.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.
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.
|
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
left a comment
There was a problem hiding this comment.
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:
-
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. -
Shared vs Per-machine inside Memory — explicitly naming what
personalPathvssharedPersonalPathalready implement closes the "should this be host-scoped?" question that recurs whenever someone adds a new file to the private repo. Themachine-<host>/subdir convention withhostname().split('.')[0](Decision 3) is the pragmatic call. -
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).
-
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:
-
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).
-
SUTANDO_PRIVATE_DIR→SUTANDO_MEMORY_DIRrename — 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. -
SUTANDO_HOST_LABELenv 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. -
State sync allowlist — endorse default-no +
.sync-allowlist. Thestate/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. -
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. -
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)
…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>
|
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
Three disciplines
Public surface unchangedThe host-CLI section in Net for the RFCHost-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 RFCDecision 4's |
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.
17ca5b4 to
ba4ede7
Compare
* 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
left a comment
There was a problem hiding this comment.
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.mdfor 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).
1ce7403 to
6969847
Compare
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>
…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>
…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>
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>
…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>
…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>
…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>
…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>
…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>
… (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.
…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).
…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>
…#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>
…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>
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.mdso per-session context surfaces the model without dragging the full content into the prompt budget.Status — ready to merge
3 reviews, all positive:
Sonichi nits resolved
349a3a7setupTempWorkspacehelper totests/_helpers/.cursor/IDE config angle sentence349a3a7Decisions ratified
The 6 decisions inline in the doc, with their ratification + tracking status:
SUTANDO_PRIVATE_DIR→SUTANDO_MEMORY_DIRSUTANDO_HOST_LABELoverride on top of hostname.sync-allowlistopt-in.env(not Memory)Deferred (out of this PR)
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
SUTANDO_MEMORY_DIR) — merge captures it before drift.Test plan
cat docs/workspace-design.md— reads cleanly, 248 lines, no broken Markdowngrep -npaths in resolver tables match current sourceba4ede7)🤖 Generated with Claude Code