Every project directory tracked by the NS hub **must be git initialized** (`git init`).
When Claude works in an exec session (claude-exec-{proj}):
New project dirs added to `proj_dirs` in `server.py` must also have `git init` run.
**Completion screenshots MUST be real UI screenshots** (browser showing the working feature), NOT code snippet captures or terminal output. For backend-only changes (edge functions, API fixes), show the UI where the feature is triggered successfully.
When user says "prove with img", "share the shot", "completion shot", or similar: take a Playwright screenshot of the relevant working UI, upload to `gdrive:claude-shared/Moat/outbox/`, and share the GDrive link.
**NEVER use relative filenames for Playwright screenshots** — always use absolute paths under the project root:
`filename="
Relative paths save to session CWD and clutter home dir.
**Rule**: Always upload to `gdrive:claude-shared/
**Upload command**:
rclone copy <local-file> "gdrive:claude-shared/<project>/outbox/" rclone link "gdrive:claude-shared/<project>/outbox/<filename>"
**If project folder missing**: `rclone mkdir "gdrive:claude-shared/
**[DEFAULT] Hub doc sharing — always docx table**: When sharing any document result from a hub stone (research, guide, comparison, analysis, screenshot summary, etc.), default format is **python-docx with styled tables** — even without explicit "docx" request. Styled table spec: header row dark bg (#1F2937) white text, alternating row shading (#F3F4F6 / white), `Table Grid` style. Upload via rclone to outbox, share link in stone comment.
**Inbox usage** (read from user): `gdrive:claude-shared/
Read with: `rclone ls "gdrive:claude-shared/
On "make into docx" / "표/테이블 docx로" / any tabular-output request: emit **HTML with styled tables** (borders, header bg, alternating row shading, priority highlights) — never markdown pipe-tables. Upload as `text/html` so Drive preview shows real tables.
Full inventory and quick-reference scripts: `~/.claude/env/README.md`
Usage: `source ~/.claude/env/shared.env &&
IPs and hostnames are dynamic — always query at runtime:
tailscale status --json | python3 -m json.tool | grep -E "HostName|TailscaleIPs|OS"
Full reference: `~/.claude/tool-hints/tailscale-notify.md`
Bootstrap: `irm http://
When `additionalContext` contains G1/G2/CM headers: output the `> **XX**` header lines verbatim as the first lines of the response (before any other content), then skip if none present.
**MANDATORY**: When **[MEMORY TRIGGER]** appears in context, immediately Edit the MEMORY.md file shown in the trigger and add the decision in the SAME response turn — do NOT defer to session end.
Also write immediately on: person/project status changes, kills/pauses, finalized decisions, or explicit "기억해"/"저장해".
MEMORY.md path: `~/.claude/projects/{cwd-with-slashes-as-dashes}/memory/MEMORY.md`
When an external site (Zoom, Notion, Figma, etc.) presents multiple SSO options, prefer **Google SSO with `be2jay67@gmail.com`** — this is the user's primary account and has the highest chance of being already authenticated.
Never do the following under any circumstances:
1. ❌ **`npm run build` while dev server is running** — use `npx tsc --noEmit` for type-check only; deploy via `vercel --prod` directly
2. ❌ **Killing Node.js processes without authorization** (`taskkill /IM node.exe`, `pkill node`, etc.)
- Never kill `node.exe` processes without user approval
- On port conflict: use different port or inform user instead of killing
3. ❌ **Running git commit without authorization** — only run when user explicitly says "commit"
4. ❌ **Running git checkout/restore without authorization** (reverting file changes)
- Must confirm before `git checkout
- `git checkout -b` (branch creation) is exempt
5. ❌ **Korean/multibyte filenames** — minimize Korean in filenames/branch names (prefer ASCII). Use `--name-only` for git output parsing.
6. ❌ **Tool output truncation** — for Korean content, set generous `head_limit`; process line by line if needed.
7. ❌ **Killing the hub server process directly** (`pkill uvicorn`, `kill -9
- The hub at port 9000 is managed by systemd and is the exec session's own communication channel.
- To restart hub after code changes: `POST http://
- Direct kill disconnects the exec session from the hub — losing task context.
(PostToolUse hook enforces this)
**Never mix up projects in a multi-project environment.** Always verify `.env.local` matches the target project before any connection.
**Past incident**: "table not found" → was connected to wrong Supabase project.
Full checklists (Supabase / Vercel / Railway): `~/.claude/tool-hints/external-services.md`
**Supabase Edge Functions errors**: Always check logs directly at [Supabase Dashboard → Edge Functions → Logs](https://supabase.com/dashboard/project/_/functions) before guessing the cause.
When browser work gets stuck or needs verification — use Playwright instead of guessing.
**Tool priority:** `mcp__playwright-session-*` → `playwright` CLI (`~/.local/bin/playwright`, always available)
**Session vs Remote:** Use `mcp__playwright-session-*` for non-interactive checks (snapshots, scraping, URL verification). Use `mcp__playwright-mcp-remote__*` only when user interaction is needed (login, file upload, clicking visible UI) — remote shows in noVNC so user can watch/intervene.
**Session rules:** Check `browser_tabs` first (session-1→2→3→4), use `about:blank` = idle session. **If session-N fails or is busy (non-blank tabs, tool error), immediately try session-(N+1) — exhaust all 4 before giving up.** ❌ Never create new sessions without authorization. ❌ Never auto-proceed on login required — stop and ask.
**Selector strategy (MANDATORY):** `getByRole` / `getByLabel` / `getByText` / `getByPlaceholder` — ❌ no CSS/ID selectors.
**Smoke test:** `node ~/.claude/snippets/pw-smoke.js` (template: `~/.claude/snippets/pw-smoke.js`)
On port conflict: kill process (`lsof -ti:
**Note**: If the process is Node.js-based (Next.js, etc.), confirm with user first (Prohibited Patterns #7). For non-Node servers (Python, Go, etc.): kill without confirmation.
Stone lifecycle: queued → pending_confirmation → done.
COMPLETION PROTOCOL: PATCH status=pending_confirmation + append_message.role=claude + star_relation + exec_start + exec_end + model_used (all in one PATCH).
COMMENT RULE: append_message ≤3 lines. Longer detail → docs/ns-replies/
PARALLEL DISPATCH: 2+ independent queued stones → emit multiple Agent calls in ONE message.
NO-OP PROTOCOL: if skipping a stone, post 1-line reason via append_message. Silent skip forbidden.
TOKEN DISCIPLINE: after completion post ONLY the 1-line summary. No follow-up questions.