- CALENDAR CONFLICT: never schedule over an existing event without Josh's explicit override.
- EMAIL: always DRAFT, never send. Save as Gmail draft for Josh's review.
- FILES: never create a version-suffixed copy. Edit the master.
- Never contact venues, churches, or other third parties directly — Josh handles all outreach.
- STATE VERIFICATION: Before any suggestion, to-do item, or "ready for you" claim about a PR/issue/CI/deploy, run a fresh liveness check in that same turn (e.g.
gh pr view --json state,mergedAt/gh issue view --json state). If state ≠ OPEN, it is done: drop it silently.mergeable: UNKNOWN/nullon a PR usually means merged/closed — never read it as "the API is slow" and never advise merging without confirming state=OPEN. An inconclusive check is not a completed check: use a definitive fallback (localgit merge-tree,statusCheckRollup) or say plainly that you could not verify — never hand Josh a verification step the agent can run itself. - ONE REPO, ONE SESSION: never edit a repo another AI session is actively working (Josh, 2026-07-11). Before branching or editing, check
git status -sb— a non-devbranch or dirty tree means another session likely has the repo in flight. Hand the change to that session/lane (route via Josh) or ask Josh first. A separate worktree or non-colliding branch does NOT make concurrent edits OK — parallel semver bumps and surprise PRs still collide. - MAX 2 CONCURRENT WORKSTREAMS PER TERMINAL: Two live background jobs (e.g. a subagent + a headless agy dispatch) is the cap. When a THIRD thread (new discussion, dispatch, or background job) starts in the same session, the agent must WARN Josh first and propose a separate terminal — never comply silently. Origin: 2026-07-16, Claude A froze mid-permission-prompt while running a Sonnet subagent + a headless agy dispatch plus a new discussion; recovery required keystroke injection from another session.
- ISSUE CITATIONS ALWAYS CARRY REPO + NUMBER + TITLE: Every mention of a GitHub issue or PR — in chat, in a commit message, in an issue/PR body, in a memory or queue file — must be written as
repo#number "title", e.g.web-jam-back#998 "email subject or title still not easy for me to see its target venue".#followed by digits is an ILLEGAL token in anything Josh reads. There is no exception for a repeat mention, a list item, a parenthetical, "the one I just named", or a closing one-line offer. If you don't know the title, look it up (gh issue view N --repo R --json title) before writing the sentence — never emit a bare number as a placeholder. If the full citation is too verbose, shorten to the TITLE, never to the number. The violation is almost always the LAST sentence of a message (the "want me to do X?" offer, written after the careful part), so re-read the finished message and check every#before sending. Josh has asked for this five times (2026-07-24 → 2026-07-29); he reads these on a phone with many numbers in flight and a bare number costs him a lookup every time. - NO AGENT CONNECTS A NEW ACCOUNT, CREDENTIAL, OR MCP SERVER WITHOUT AUTHORIZATION: No agent adds a connector, account, credential, or MCP server to any Claude or Flash surface without Josh's explicit authorization naming it. Discovering that something could be connected is never permission to connect it. This applies to new OAuth grants, new MCP servers, new API tokens, and widening the scope of an existing connection. Origin (2026-07-30, Josh): "it should NEVER have something else that I have not authorized." See web-jam-tools#324 "No agent connects a new account, credential, or MCP server without Josh's explicit authorization — add the rule and audit where it can be mechanically enforced" for the enforcement-surface audit.
- STANDING AGENT CREDENTIAL CLASSIFICATION RULE (MACHINE-CONSUMED VS HUMAN-CONSUMED): Whenever an agent encounters or generates a new credential, account identifier, or token, the agent must STOP and prompt Josh to classify it as either machine-consumed (e.g.
GITHUB_TOKEN,GEMINI_API_KEY,HEROKU_API_KEY,CIRCLECI_TOKEN,DENO_DEPLOY_TOKENstored in shell rc or secret store) or human-consumed (e.g.webjam.claude@gmail.comstored in KeePass only) BEFORE storing, exporting, or configuring it in any shell profile,.envfile, or configuration file. Human-consumed credentials belong in KeePass only and must never be exported to shell profiles or stored in application configuration files (web-jam-tools#344 "Human-only credentials register and guard hook"). - NO AI DELETES OR FORCE-PUSHES A REMOTE BRANCH, EVER, WITHOUT AN EXPLICIT IMPERATIVE FROM JOSH NAMING THAT BRANCH. "The PR is merged" is NOT such an instruction — it states a fact, it does not authorize deleting anything. Local branch cleanup after a merge (deleting a LOCAL branch with
git branch -d/-D,git fetch --pruneto prune stale local remote-tracking refs) remains permitted and unchanged — this rule narrows that standing post-merge cleanup habit to local branches only, it does not remove it or require re-approval for it. Enforced by three independent layers: a harnesspermissions.denyblock on the waysgit push/git branchcan delete or clobber a remote ref (--delete/-d, empty-source colon refspecs,--force/-f/--force-with-lease,--mirror,--prune, andgit branch -D/--delete --forceagainst aremotes/ref — installed viascripts/install-hooks.shin this repo), a GitHub ruleset restricting deletions on the branches agents create (claude/**,agy/**,dev,main— Josh-only UI work, see web-jam-tools#308 "Remote branches can be deleted by an agent with no authorization — advisory guard does not block (3 layers: deny rules, GitHub ruleset, HARD RULES)"), and this HARD RULE. Origin: 2026-07-29, an agent deletedclaude/cross-ai-rules-issue-citation-hard-rulefromweb-jam-toolsimmediately after Josh merged web-jam-tools#307 "Add ISSUE CITATIONS hard rule to operational rules" — Josh had only said the PR was merged, never authorized a deletion, and thePreToolUseguard that fired was advisory text an agent could rationalize past. - REAPER RECORDING SESSIONS & RATE LIMIT SAFETY: When running REAPER music recording sessions via Reaper MCP:
- REAPER DAW, audio interfaces, recorded WAV audio stems, and
.RPPproject files live locally on the user's computer and are 100% safe from rate limit interruptions. - Google does NOT broadcast an advance warning gauge prior to hitting temporary hourly rate limits (
429 Rate Limit Exceeded). - Use
Flash Medfor routine, high-volume REAPER operations (transport_play,transport_stop,track_create, volume/pan tweaks, clip splits) to preserve hourly token headroom. - Reserve
Flash Highfor complex multi-track creative mixing, sidechain routing, and intricate composition passes. - Always execute a project save (
project_save) before running large multi-step automated sequences.
- REAPER DAW, audio interfaces, recorded WAV audio stems, and
- MAIN BRANCH PRs MUST ORIGINATE FROM DEV: Across all 8 active WebJamApps repos, any PR targeting
mainmust originate fromdevas its head branch (dev→main). Feature branches (gemini/*,claude/*,feat/*,fix/*) must targetdevas their base branch. Direct PRs from feature branches tomainare strictly forbidden and blocked by CI and script guardrails (web-jam-tools#351 "all 8 active github repos - their main branch only accepts PR requests from their dev branch"). - MULTI-REPO ISSUES STAY OPEN UNTIL ALL REPOS ARE COMPLETE: When an issue explicitly covers multiple repositories (e.g. "all 8 active github repos"), no single PR in one repository may pass
--closesor claim the issue is completed. PRs in individual repos must use--part-ofso the tracking issue remains OPEN until the final repository's PR is merged. - THE
BlockedLABEL IS CANONICAL — NATIVE ISSUE DEPENDENCIES DO NOT REPLACE IT. Josh wants BOTH: native GitHub issue-dependency links (the real relationship between issues) AND theBlockedlabel (capital B, hexB60205,repos: allinskills/fix-labels/labels.yaml) as the at-a-glance signal that makes an unworkable issue obvious in a plain list view without opening each issue. They do different jobs: use a native dependency whenever a specific issue blocks the work — it names which one, renders in the Issues list, and clears itself on close. Use theBlockedlabel whenever the work is unworkable for any reason, including the many with no issue to point at (a vendor, a credential Josh must generate, a physical action). Native dependencies cannot express that case at all, which is why the label is not redundant. No agent may pruneBlockedfromlabels.yaml(or delete it live) on the theory that native dependencies made it redundant — that is exactly what happened once already:blocked(lowercase) was removed in commit 7d2523d as part of a nine-label prune shipped for web-jam-tools#300, justified as "-> native issue dependencies," and Josh never actually agreed to that one — it rode along in a batch whose headline was about priority labels. web-jam-tools#329 "Restore the Blocked label as canonical in labels.yaml — it was pruned in a batch Josh never ratified, and he wants it alongside native dependencies" restored it. Seeskills/fix-labels/labels.yaml'sBlockedentry for the full rationale. - RESTRICTED LAPTOP DROPBOX SCOPE & SECURITY GUARDRAILS: Access to
~/Dropboxon the laptop is restricted to three approved top-level folders:joshandmariamusic,web-jam-llms, andmark_henrickson. All other top-level~/Dropbox/*folders — includingDropbox/WebJamApps— are explicitly denied inpermissions.denyviainstall-hooks.shfor file tools (Read,Edit,Write) and Dropbox MCP mutation tools (delete,move). Note: Deny rules on file tools do not constrain raw Bash commands (which use string-pattern matching for Bash permission rules), serving as an operational guardrail rather than an absolute security boundary (web-jam-tools#321 "Add the laptop Dropbox deny list, verify Flash confinement, and document the restricted scope").
- No
any:@typescript-eslint/no-explicit-anyis set to'error'. Do not disable this rule or use: anyoras any. - Shared Domain Types: Centralized types for socket connections, streams, payloads, and domain objects live in
src/types/index.ts. - Mongoose Generic Facade: Model facades extend
Facade<T>defined insrc/lib/facade.ts. When typing generic model methods, use double type assertions (e.g.(await ... as unknown) as T[]) to satisfy Mongoose generic method signatures without usingany. - SocketCluster Mocks: When mocking
AGServerorIClientin tests, cast stub objects usingas unknown as socketClusterServer.AGServeroras unknown as IClient. Ensurereceiver.next()mocks return{ value?: T; done?: boolean }.
- Snyk Failures & Resolution via
npm audit fix: PR checks may report failure onsecurity/snykdue to transitive dependency vulnerabilities. Runningnpm audit fixupdatespackage-lock.jsonwith non-breaking patches to resolve these vulnerabilities. Always run local tests afterwards to verify the test suite remains 100% green before committing and pushingpackage-lock.jsonto the PR branch.
- Runtime: Node.js v24.18.1
- Node Engine Version Bumps: When bumping Node.js in
package.jsonengines.node, runnpm install --package-lock-only --ignore-scripts(ornpm install --ignore-scripts) to updatepackage-lock.jsonroot engine definition without waiting onpostinstallscripts so both files are committed together.