* Compact CLAUDE.md and skills docs Cut ~2k lines of duplication without dropping actionable rules. CLAUDE.md (267 -> 103): `reload.sh --tag` was explained four separate times; now once. Dropped the Ghostty submodule and Release sections, which restated the cmux-ghostty and cmux-release skills, and removed the file:// deeplink block, which contradicted the rule that chat output uses http://127.0.0.1:17320/<tag> and never a file:// URL. Pitfalls compressed from paragraphs to one line each, pointing at the owning skill. skills/ (4849 -> 2819 across 20 skills): the dominant waste was SKILL.md files restating their own references/ verbatim. Kept one canonical statement with expansion in references/. Three rules the root file carried had no skill that covered them, so they moved into cmux-architecture rather than being lost: SPM package group folders with check-workspace-package-groups.py, the Package.resolved tracking policy, and "feature flag means a remote PostHog runtime flag" via CmuxFeatureFlags. The cmuxTests pbxproj wiring requirement was promoted from a reference file into cmux-testing, and the shortcut policy moved into cmux-keyboard-shortcuts with its duplicate removed from cmux-localization. Stale references fixed: - `cd cmuxd && zig build` referenced a directory with zero tracked files; the daemon is Go at daemon/remote/cmd/cmuxd-remote. - Changelog page is web/app/[locale]/(landing)/docs/changelog/page.tsx, and configuration is under the same (landing) segment. - Package CmuxSocketControl does not exist; the real one is CmuxControlSocket, cited twice as the exemplar to copy. - Two rg commands in the localization audit were double-escaped and passed `--` as if it were a glob flag, so they matched nothing and silently passed the audit. Left untouched: the auto-generated cmux-settings reference files, which would drift from their generator. * Dedupe release slash commands release.md, release-local.md, and release-nightly.md each restated the same version-bump and changelog procedure (450 -> 192 lines total). release.md is now the canonical command doc holding the shared prep, changelog guidelines, and contributor-credit format; the other two state only their delta (local build-sign-upload.sh path, and no-PR direct-to- main path with the homebrew-cmux submodule pointer commit). Stale and incorrect instructions fixed: - All three pointed at docs-site/content/docs/changelog.mdx. There is no docs-site/ in the repo; the changelog page renders from CHANGELOG.md. - release.md said to hand-edit 'typically 4 occurrences' of MARKETING_VERSION in project.pbxproj. That leaves CURRENT_PROJECT_VERSION stale, which Sparkle requires to be monotonic and which release-pretag-guard.sh rejects. Unified on scripts/bump-version.sh, which bumps both. - Documented build-sign-upload.sh --allow-overwrite, which matters because pushing a v* tag also fires release.yml, so a local upload can race CI for the same assets. * Address review findings on submodule remotes and build links - tagged-builds.md still told contributors to build chat links from the absolute .app path with a file:// URL, contradicting the rule in CLAUDE.md that chat links use http://127.0.0.1:17320/<tag>. - cmux-ghostty said 'origin is upstream and manaflow is the fork' and pushed to a 'manaflow' remote. .gitmodules points every submodule at manaflow-ai/*, and no checkout has a 'manaflow' remote, so those commands would fail. Both the skill and submodule-safety.md now tell you to check git remote -v, and document adding an explicit 'upstream' remote for syncing from ghostty-org. - submodule-safety.md verified ancestry against <remote>/main even when a feature branch was pushed. Now checks the branch actually pushed. - release.md credited @lawrencechen; the account is @lawrencecchen. Skipped, with reasons: the ~/.agents/skills vs ~/.codex/skills split in cmux-customization is the documented convention (normal install vs skills.sh install), matching cmux-diagnostics. Adding per-entry attribution to the un-credited changelog example entry would contradict the policy three lines above it, which exempts core-team work.
2.2 KiB
Effect Boundaries
Expands the backend TypeScript rules in ../SKILL.md for route handlers, services, and scripts.
Route handler shape
A handler is a shallow adapter: parse request input, select the Effect program, run it once at the boundary, translate domain errors to HTTP. Workflow sequencing, retries, provider calls, and database updates live outside the handler body. A handler that interleaves parsing, database writes, provider calls, and response construction makes retries and idempotency impossible to audit.
Reading one should immediately answer: what input the route accepts, which Effect program performs the workflow, which typed errors map to expected statuses, and which failures are unexpected defects.
Service shape
Use an Effect service when a workflow crosses an external boundary or has meaningful failure semantics: provider APIs, database reads/writes, auth and team lookup, payment or quota checks, retry and timeout policy, telemetry and usage recording, idempotency claims.
Model expected failures as typed domain errors named for the business failure. VmLimitExceeded, ProviderCapacityUnavailable, and IdempotencyConflict tell a caller more than a raw FetchError.
Dependency shape
Service dependencies are concrete capabilities declared as layer requirements: database client, provider client, auth/team service, clock or timeout policy, telemetry sink, idempotency repository. Not globals, broad ambient containers, or untyped option bags.
Plain TypeScript carve-out
Constants, schema declarations, config objects, frontend components, pure formatting helpers, and tiny route glue with no external effects stay plain TypeScript. Effect earns its place where explicit failure, dependency, retry, and cancellation semantics reduce real ambiguity.
Error mapping
When adding a route, check that invalid input maps to 400 or the existing validation status, auth and entitlement failures map to the existing auth/payment statuses, active-limit and quota failures are explicit, provider unavailability is distinguishable from a defect, and idempotency conflicts return a deterministic response. Never disguise an unexpected defect as an expected user error.