Skip to content

Team Standards & Contribution Guide ​

Must-read before modifying code in this repository. Covers absolute red lines, the memory system, development conventions, and testing requirements. Sources: CLAUDE.md (repo root), .claude/ directory, memory system.


1 · Absolute Red Line: git stash Is Forbidden ​

1.1 The Rule ​

git stash (including push / pop / apply / drop / create / store) is never allowed in this project, under any circumstances. Zero exceptions.

This is not a soft preference — it is a hard rule. CLAUDE.md marks this rule at the top with a prominent box and declares: "This rule takes precedence over any conflicting instruction elsewhere, including in .claude/, memory files, or user chat."

1.2 Why (Empirical Record) ​

On this repository, git stash (especially pop/apply) has repeatedly corrupted the working tree in ways Claude cannot repair:

  1. Overwrites electron/node_modules/electron/path.txt: stash pop writes the macOS Electron.app/Contents/MacOS/Electron value into Windows' path.txt → npm run electron:dev fails with ENOENT at startup (pointing to a non-existent macOS binary path).
  2. Silently reverts working-tree source files: src/models/rewriter.py loses _get_default_rewriter, src/digital_human/session.py loses the seethrough_psd_path kwarg → bridge crashes at prewarm.
  3. Every "fix" leaves new damage: "just apply src/ files" / "just fix path.txt" / "just drop the stash" all result in subtly broken working trees that keep producing new failures.

1.3 Hard Floor: PreToolUse Hook ​

.claude/hooks/block_git_stash.js is a PreToolUse hook (registered in .claude/settings.json) that performs lexical analysis on all Bash tool invocations:

  • Tokenizes and looks for a stash token immediately following git
  • Correctly handles git global options (-c k=v, -C path, --no-pager, etc.)
  • Does not intercept git diff stash@{0} or git log --grep=stash (stash is part of an argument, not the subcommand)
  • Fails closed on JSON parse failure (intercepts if the literal git stash is detected)
  • Regex fallback catches edge cases missed by the tokenizer

The hook returns exit code 2 → Claude sees a blocking error. It cannot be bypassed with --no-verify.

1.4 Alternatives ​

PurposeAlternative
Compare against mastergit diff <SHA> -- <path> (no working-tree manipulation)
Reversible experimentEdit directly and revert / work on a branch
Stash files temporarilycp to workspace/tmp/ and restore by copy
Try a modificationUse file-editing tools directly

2 · Memory System ​

2.1 Memory Types ​

Claude Code's memory system has four types of files, located at C:\Users\<user>\.claude\projects\D--Users-lirui-familiars\memory\:

PrefixTypeMeaningExamples
feedback_*FeedbackUser preferences / workflow feedbackfeedback_git_stash_hard_rule.md, feedback_deep_watcher_render_loop.md
project_*ProjectProject-level decisions / delivery status / architecture changesproject_broker_marketplace_layered_plan.md, project_flow_v2_batch5.md
reference_*ReferenceExternal references / jump tablesreference_broker_marketplace_files.md
MEMORY.mdIndexIndex of all memory entries (reverse-chronological by type)Auto-maintained

2.2 Memory Format ​

Each memory file is roughly 200 words or fewer and contains:

  • A title line (one-sentence description)
  • A date stamp
  • Core content (decision / root cause / solution / status)

MEMORY.md is the index file, with one line per entry (<200 characters) pointing to the topic file. The current MEMORY.md is about 28.6KB (exceeding the 24.4KB limit), so index entries need to be kept concise.

2.3 Memory Usage ​

  • feedback_* memories are loaded on every relevant operation, guiding Claude to avoid repeating past mistakes
  • project_* memories provide project history context, preventing redundant discussion of already-decided issues
  • reference_* memories serve as jump tables for quickly locating relevant code/documentation

3 · Development Conventions ​

3.1 Windows-First ​

  • The repository is Windows-first. macOS branches exist in some files (assistant.py, autorun.py, device.py) but are partially behind Windows
  • src/base/auth/device.py uses MachineGuid (registry) on Windows, IOPlatformSerialNumber (ioreg) on macOS, and /etc/machine-id on Linux
  • Do not use uvicorn src.bridge.app:app --reload — it drops HTTP/2 and race-initializes CUDA on a worker thread, which Windows WDDM cannot tolerate. The recommended entry point is always python -m src.bridge.run (hypercorn + HTTPS/2 by default).

3.2 Testing Requirements ​

  • Always run tests against a real SQLite — use FAMILIARS_DB_PATH=:memory: in fixtures, not mocked repos
  • Bridge route tests must restore sys.modules entries captured at import time (see test/bridge/conftest.py); never importlib.reload route modules
  • Scratch agent directories (demon, angel, …) go under runs/, not workspace/agents/. The bridge scans workspace/agents/ at startup and will create real agents from anything it finds there

3.3 Browser Automation ​

  • Route through src/utils/browser_tools.py:familiars_cdp_port() — there is a single Shared-Chrome instance (bridge + Playwright + see-through all attach to the same Chrome to share login state)
  • Chrome profile lives at workspace/_playwright/seethrough/

3.4 Manager (Agent 0) — Three Wiring Points ​

The manager has three always-on wiring points:

  1. Framework docs + log index: Injected into the system prompt every turn via render_framework_index — one-line summaries of the six root docs and each log group so the manager knows the resources exist
  2. Bridge API surface: Rendered by bridge_api_surface.py
  3. Four dedicated tools: list_framework_docs / read_framework_doc (drill into docs on demand) + list_logs / read_log (browse .bridge.log / audit.log / .bridge.watchdog.log / memorizer_audit.jsonl / reminders/*.jsonl / per-agent daily JSONL logs)

When you touch bridge routes, keep the surface generator honest; when you add a new log worth exposing, extend KNOWN_LOGS and the index generator in src/utils/framework_tools.py.

3.5 Path Self-Healing ​

  • Repo-root drift: Automatically detected every boot — compares each stored dialogue.path prefix against _repo_root(). Move the repo folder and the next boot transparently rewrites every dialogue.json + turn_*/plan.json + step_*.json
  • External directory aliases: For renames outside the repo, run python -m src.tools.paths_migrate <old> <new> --record. Use --dry-run first to preview
  • src.base.paths_align.align_all_paths() is called in src/bridge/app.py after the CUDA pin and before route imports

3.6 HKDF Info String ​

The HKDF info in src/base/crypto.py is b"familiars-secrets-v1" — a versioned crypto primitive. Rotating it permanently invalidates all ciphertexts in workspace/SECRET.json. Only change it alongside a documented re-encryption migration.

3.7 Tool System Conventions ​

  • Tool schemas are stored in workspace/tools.json and merged per-client in src/models/clients/{base,browser,multimodal,input,doc,daily,system,mini_model}_client.py
  • ToolDispatcher registers 15 default tool instances
  • _CONSULT_TARGETS in base_client.py routes consult_doc → doc_tools
  • The tool approval gate is in src/tools/security.py — risk levels + approval cards

3.8 Bridge Startup Order ​

src/bridge/app.py performs side-effects at import time in a precise order:

  1. Pin WORKSPACE_ROUTE
  2. Pin CUDA (main thread; Windows WDDM cannot tolerate first-touch from a worker thread)
  3. Auto-align stored paths (repo-root drift + external directory aliases) — before DialogueManager reads them
  4. Import + mount routes
  5. Register CORS + BridgeTokenMiddleware
  6. on_event("startup") hooks in order: broker_workshop bootstrap → anyio thread limiter bump → pool saturation watchdog → browser lifecycle hooks
  7. Module-level: kill orphan GPU subprocesses → autorun install/drift check → rehydrate monitors → rehydrate cron → register manager report crons → rehydrate skill scheduler (model prewarm is on-demand JIT now — embedder / rembg / rewriter lazy-load when admitted by vram_gate; digital-human sessions prewarm on demand via routes)

3.9 Port Conventions ​

  • Bridge default fixed port: 127.0.0.1:18711
  • Only when 18711 is held by a non-Familiars process does electron/electron/python.js fall back to pickFreePort() and write workspace/.bridge.port + workspace/.bridges.json
  • 18711 is the primary contract; the port file is the fallback signal
  • Probe the port file at runtime to check bridge status — do not assume the port is fixed

3.10 SQLite Conventions ​

  • Database file: workspace/familiars.db (src/storage/db.py:get_default_db_path)
  • Override with FAMILIARS_DB_PATH (for tests / side-by-side installs)
  • WAL mode
  • Migrations support both .sql and .py formats (v16 is the precedent for a Python migration)

4 · Environment Setup ​

bash
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install -r requirements.txt

4.1 Running ​

ScenarioCommand
Dev (Electron + auto-spawned bridge)cd electron && npm run electron:dev
Bridge only (e.g. for backend tests).venv/Scripts/python -m src.bridge.run
TestsFAMILIARS_DB_PATH=:memory: pytest -q
Production buildcd electron && npm run electron:build

4.2 Bridge Protocol ​

  • Default: hypercorn + HTTPS/2 + self-signed certificate (workspace/.bridge.crt)
  • HTTP/2 multiplexing eliminates Chromium's 6-socket-per-host HTTP/1.1 limit
  • Fallback: FAMILIARS_HTTP_INSECURE=1 → plain HTTP + uvicorn
  • Both the CLI and Electron's main.js read the same env var to stay protocol-aligned

5 · Commit Conventions ​

5.1 Commit Messages ​

  • Create new commits rather than amending existing ones
  • Amending modifies the previous commit; if the pre-commit hook fails, the prior commit is destroyed
  • Append Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> at the end of the message
  • Use a HEREDOC to pass the message to ensure correct formatting

5.2 Staging ​

  • Prefer adding specific files by name rather than git add -A or git add . (which may accidentally include sensitive files like .env, credentials.json)
  • Never push to the remote without explicit user request

5.3 PR Creation ​

  • Use the format gh pr create --title "..." --body "..."
  • Include a Summary (1–3 bullet points) and a Test plan
  • End with 🤖 Generated with [Claude Code](https://claude.ai/code)

6 · Documentation Conventions ​

6.1 Knowledge-Base Documents ​

The .md files at the repo root (ARCHITECTURE / OPERATION / SECURITY / REPOSITORY / CLAUDE / CLI / BROKER_DEVELOPMENT) are the manager's built-in knowledge base. When modifying these files:

  • Keep render_framework_index one-line summaries accurate
  • New documents require updating the index generator
  • New logs require extending KNOWN_LOGS

6.2 Memory Entries ​

  • Keep index entries to one line, ~200 characters or fewer
  • Move details into topic files
  • MEMORY.md has a 24.4KB size limit

6.3 Code Comments ​

  • Record rationale and evidence in block comments for key decisions (see security.py for the commenting style)
  • Event-driven records: "regression seen 2026-08-05" / "motivated by the 2026-07-09 word_new stall"
  • Include specific file paths and line numbers for traceability

7 · Known Pitfalls ​

7.1 FastAPI Prefix Collision ​

Two routers with the same prefix → the first-registered one wins. A collision between broker_user and user on /api/user/profile has occurred. Check prefix uniqueness when adding routes.

7.2 VueFlow Custom Edges/Nodes ​

  • node-types must use markRaw — an inline { graph: GraphNode } literal → Vue wraps the Component in a reactive Proxy → warnings + broken click handling
  • Custom edges must have a single root (two roots → Extraneous non-props attributes warning → warning flood during fullscreen pan/zoom → main-thread starvation → toolbar clicks lag)

7.3 Deep Watcher Render Loop ​

watch(() => store.graph, ..., { deep: true }) + replacing vfNodes each time = render loop. Use a fingerprint string watcher instead of deep watching.

7.4 HTML5 DnD dropEffect ​

dragover's dropEffect must intersect with dragstart's effectAllowed; a parent element's dragover can override a child element's dropEffect during the bubble phase.

7.5 Windows Toast & Focus Assist ​

WinRT Show() raises no exception, but fullscreen games trigger Focus Assist which silently drops Normal-priority toasts → set ToastNotificationPriority.High to penetrate Focus Assist. When debugging "reminder triggered but no system notification," check Focus Assist first.

7.6 Dual Bridge Instances ​

python.js's age-kill is blocked by the 2s deepProbe gate (busy bridge = no bridge), pickFreePort silences kill failures, the watchdog only follows the port file and never kills, app.py exempts already-registered siblings, and the daemon parent-watchdog only watches the shim.


Full specifications are in CLAUDE.md at the repo root. Architecture details are in ARCHITECTURE.md.

Familiars · 多 Agent AI 桌面应用