Skip to content

feat(util_paths): claudeHomePath() helper — centralize ~/.claude/ resolution (closes impl-gap #5 from #857) - #860

Merged
sonichi merged 2 commits into
mainfrom
feat/claude-home-path-helper
May 18, 2026
Merged

sonichi merged 2 commits into
mainfrom
feat/claude-home-path-helper

Conversation

@sonichi

@sonichi sonichi commented May 18, 2026

Copy link
Copy Markdown
Owner

Summary

Adds claude_home_path(*subpath) (Python) and claudeHomePath(...subpath) (TS) 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).

Closes implementation-gap #5 from issue #857 (workspace-design RFC tracking issue).

Motivation

Per the 2026-05-18 workspace-design RFC discussion in PR #858 + issue #857: Sutando's dependency on ~/.claude/ is real and accepted operationally. Channel tokens, agent memory, skill discovery, settings — all live there. 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. ~20 LoC now, single grep surface for any future audit.

Usage

from util_paths import claude_home_path
claude_home_path('channels', 'discord', 'access.json')   # /Users/wangchi/.claude/channels/discord/access.json
claude_home_path('projects', project_slug, 'memory', 'MEMORY.md')
claude_home_path('skills', skill_name)
import { claudeHomePath } from './util_paths.js';
claudeHomePath('channels', 'discord', 'access.json');
claudeHomePath('projects', projectSlug, 'memory', 'MEMORY.md');

$CLAUDE_HOME env var overrides the base for tests + alt-host installs.

Migration plan

Not flag-day. Existing callers (discord-bridge.py, inline-tools.ts skill-loader, health-check.py memory checks, etc.) can migrate opportunistically. New code that reads/writes under ~/.claude/ should go through this helper.

Test plan

  • Python smoke: claude_home_path('channels', 'discord', 'access.json') → expected path
  • TS smoke: same shape, same result
  • $CLAUDE_HOME override honored on both
  • npx tsc --noEmit passes
  • Full TS suite: 173/173 pass
  • CI run — should pass (helper-only, no callers migrated yet)

🤖 Generated with Claude Code

sonichi and others added 2 commits May 18, 2026 14:43
…olution

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>
@sonichi
sonichi merged commit 34d909b into main May 18, 2026
2 checks passed
@sonichi
sonichi deleted the feat/claude-home-path-helper branch May 18, 2026 21:47
qingyun-wu added a commit that referenced this pull request May 18, 2026
…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.
qingyun-wu added a commit that referenced this pull request May 19, 2026
…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.
qingyun-wu added a commit that referenced this pull request May 19, 2026
…#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 #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 (#1 + #4)

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.

* 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 #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).

* 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 #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.

* docs(workspace-design): mark all 6 decisions Ratified + link tracking 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).
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#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).
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.

1 participant