# Multi-Agent Pipeline  -  Global Rules

> **TLDR**  -  Non-negotiable rules that apply to every phase, every mode, every action. Read once, enforce everywhere. If code / a commit / a PR body violates any rule below, stop and fix before proceeding.

## Language Application (read this FIRST in every run)

The pipeline has two language axes. Both MUST be applied from the very first turn, before any status text is rendered.

| Axis | Source | Used for |
|---|---|---|
| `prefs.global.outputLanguage` (default `"en"`) | Read at Phase 0 Step 0 | All assistant-authored conversational text: status updates, findings, phase headers in chat, summaries, error explanations, audit reports, picker `question` + `description` text, anything the user reads outside the structural UI chrome. |
| `prefs.global.promptLanguage` (locked to `"en"`) | Hard-coded `"en"` | `AskUserQuestion` `label` (button text) + `header` (chip), CLI host error UI, internal contract identifiers. |

### Per-field language matrix (canonical)

This is the single source of truth. When a contributor or model is unsure where a string lives, look here first.

| Field / surface                              | Language                          |
|---                                           |---                                |
| Assistant chat replies (any phase)           | `outputLanguage`                  |
| Phase headers in chat                        | `outputLanguage`                  |
| Phase tracker render labels                  | English (script chrome)           |
| `AskUserQuestion.question`                   | `outputLanguage`                  |
| `AskUserQuestion.options[].description`      | `outputLanguage`                  |
| `AskUserQuestion.options[].label`            | English (UI button contract)      |
| `AskUserQuestion.header`                     | English (≤12-char chip)           |
| Confirmation prompt body (e.g. push y/n)     | `outputLanguage`                  |
| GitHub issue comment body                    | `outputLanguage`                  |
| Wiki / Confluence page body                  | `outputLanguage`                  |
| Jira comment body (cross-link + summary)     | `outputLanguage`                  |
| PR description body                          | `outputLanguage`                  |
| Commit subject + body                        | English (git convention)          |
| Branch name                                  | English (`feature/...`)             |
| PR title prefix (`feat:`, `fix:`, ...)         | English (Conventional Commits)    |
| Code identifiers, file paths, log lines      | English (interop)                 |
| `agent-state.json` values                    | English (machine-readable)        |
| `agent-log.md` entries                       | English (audit trail consistency) |
| Reviewer / triage system prompts             | English (model contract)          |

**Required steps**

1. At the very start of every run (Phase 0 Step 0, before any status output), call `jq -r '.global.outputLanguage // "en"'` on `$HOME/.claude/multi-agent-preferences.json`. Cache as `OUTPUT_LANG` for the session.
2. Render every assistant-authored line in `OUTPUT_LANG`. If the user types Turkish but `outputLanguage="en"` is set, follow the pref but suggest `/multi-agent:language tr` once.
3. `AskUserQuestion` `label` and `header` stay English regardless of `OUTPUT_LANG` (UI contract). `question` and `description` follow `OUTPUT_LANG`  -  the user reads them as conversational copy, not as button affordances.
4. Always English regardless of either axis: commit messages, PR titles, branch names, code identifiers, agent-state.json values, agent-log.md, reviewer/triage system prompts.

**Failure mode this prevents.** Entering `/multi-agent`, `/multi-agent:autopilot`, `/multi-agent:local`, etc. and switching the assistant's conversational text or picker question copy to English while `outputLanguage="tr"` is set. The user sees a half-English half-Turkish dialogue, flagged as a pipeline bug, not a stylistic choice.

## Code & Commit Rules

- **NEVER** put "Copilot", "AI", "generated by", or similar attribution in code, comments, commit messages, PR descriptions, or issue comments. The pipeline is a tool  -  it writes on behalf of the configured Git identity, not as itself.
- **NEVER** commit without passing build (all gates in Phase 4 Step 1 must be green).
- **NEVER** commit without passing review (at least one AI reviewer must return `approved: true` with no blocking findings).
- **NEVER** skip tests. Every public method, every error path, every edge case.
- **NEVER** delete, rename, or weaken an existing test to get a green run. Existing tests are immutable during a task. A test may change only when the task itself changes the spec that test encodes, and the commit body must name the changed test and the spec change. Deterministic backstop: the `test_lines_removed` diff-risk signal (Phase 4 Step 1.75) flags test files that shrink.
- **Follow existing code style and conventions.** Read neighbor files before writing new ones  -  match naming, structure, import order.
- **Use design tokens, no magic numbers.** `16` → `.Spacing.spacing16`. `#E31837` → `Color.Primary.primary`. `.font(.system(size: 14))` → `.typographyStyle(.body1)`.
- **Design system primitives before custom views.** Before writing a new SwiftUI / Compose / React / View / Configuration triplet inside a domain or feature module, grep the shared component library (project-specific path, e.g. `Common/UIComponents/`, `core-ui/`, `packages/ui/`) for an existing primitive that solves the same problem. New domain-level wrappers, custom modals, custom buttons, or hand-rolled toasts are forbidden when the design system already has an equivalent. If the primitive exists but lacks a modifier (placeholder, size, error binding), **add the modifier to the primitive** in its `+Modifiers` extension  -  do not fork the primitive into the consumer domain. The Figma `CodeConnectSnippet` is the authoritative pointer to which primitive to use.

## Swift-Specific Rules

- **Xcode header on new Swift files:**
  ```swift
  //
  //  {FileName}.swift
  //  {ModuleName}
  //
  //  Created by {identity.name} on {DD.MM.YYYY}.
  //
  ```
- No force unwraps (`!`) except IBOutlets. No force casts (`as!`)  -  use `as?` with `guard`.
- No `print()`  -  use `os_log` or `Logger`.

## Commit Format

| Scenario                    | Format                                      |
| --------------------------- | ------------------------------------------- |
| Has Jira ID                 | `{type}({scope}): description [{jiraId}]`   |
| No Jira (GitHub issue only) | `{type}({scope}): description [#{shortId}]` |
| Free-text task (no tracker) | `{type}({scope}): description`              |

- **Types**: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `style`, `perf`
- **Max first-line length**: 72 chars
- **Body** for "why", not "what"
- **Git author**: identity selected in Phase 0, from preferences file (not Keychain, not git global config)

## Branch Naming

- `feature/{jiraId}-{short-description}` for features
- `bugfix/{jiraId}-{short-description}` for fixes
- `hotfix/{description}` for emergency patches (no tracker required)
- `release/{version}` for release branches

## External System Outputs (PR descriptions, Jira comments, GitHub issue bodies)

**formatting contract (required)**  -  violating these produces unreadable output and is the single most common regression:

1. **Real newlines, never literal `\n`.** Shell strings like `"line1\nline2"` get saved verbatim by Bitbucket / Jira REST APIs  -  they render as literal `\n`, not line breaks.

   ✅ **Correct pattern** (heredoc → rawfile → data-binary):

   ```bash
   cat > /tmp/body.md <<'EOF'
   ## Summary

   - bullet 1
   - bullet 2
   EOF

   jq -n --rawfile body /tmp/body.md '{description: $body}' > /tmp/payload.json

   curl -s -X POST -H "Content-Type: application/json" \
     --data-binary @/tmp/payload.json \
     "$API_URL"
   ```

   ❌ **Wrong**: `-d '{"description": "## Summary\n- bullet"}'` → renders literal `\n`.

2. **Never use HTML entities** in titles, commit messages, task subjects, or body text: `&amp;`, `&lt;`, `&quot;`, `&#39;` are NOT decoded by Bitbucket / Jira / GitHub. They appear literally. Use plain `&`, `<`, `"`, `'`.

3. **Never hand-concatenate JSON** for bodies with special characters. Use `jq` (which escapes correctly) or `jq --rawfile` (which preserves bytes exactly).

4. **Terminal output, log lines, and `echo`/`printf` calls** also use real newlines  -  never `\n` literals. For multi-line messages, prefer heredoc.

5. **Markup dialect is a second axis beyond language.** Markdown: PR body, GitHub issue, wiki files. Jira: wiki markup. Confluence: storage format. Wrong dialect renders literally. Table: `payload-contracts.md`.

## Issue Management

- **NEVER auto-close** GitHub issues or Jira tickets. Closure requires team review (configurable, typically 4 approvals).
- PR body: use `Ref: #N`, `Related: #N`, or `See: {jiraId}`  -  NEVER `Closes #N`, `Fixes #N`, `Resolves #N` (GitHub auto-closes on merge).
- Jira auto-close keywords forbidden for the same reason.

## Reviewers (Every PR)

- **Default reviewers are required** on every PR.
  - **Bitbucket**: fetch from default-reviewers REST endpoint, filter out PR author, include in payload (empty `reviewers: []` means "no reviewers"  -  a regression, not a default).
  - **GitHub**: rely on CODEOWNERS + branch protection required reviewers; fall back to `prefs.projects[{p}].githubDefaultReviewers` if none configured.
- **Every Bitbucket PUT must include** `reviewers`, `fromRef`, `toRef`, `draft`  -  missing fields wipe the existing values (Bitbucket treats omission as empty).

## Build Queue

- `xcodebuild` and `./gradlew` build/test calls acquire `/tmp/claude-xcodebuild.lock` first: parallel runs corrupt DerivedData/simulators and contend on the Gradle daemon.
- Each worktree uses its own `-derivedDataPath "{worktreePath}/.DerivedData"` to prevent cross-contamination.
- Lock auto-releases; stale locks (>15min) get force-cleaned.
- Non-Xcode builds (Gradle, npm, Python) don't need the lock  -  they handle their own concurrency.

## Retry Discipline

- **3-iteration hard kill** on any retry loop (build fix, review fix, mutation verify). On the 4th failure, pause and ask the user. No exceptions.
- **Reflection prompt before retry**: "What failed? What specific change fixes it? Am I repeating the same approach?"
- **Never blindly retry** the identical action  -  diagnose the root cause first.

## Secrets & Sensitive Files

- **NEVER commit** secrets, tokens, keys, certificates, `.env` files, `Pods/`, `.build/`, `DerivedData/`, `.worktrees/`, or `agent-log.md` / `agent-state.json`.
- Stage files by name (`git add path/to/file.swift`)  -  never `git add -A` or `git add .` (accidentally includes sensitive files and agent artifacts).
- Secret scan runs as Phase 4 Gate 4  -  if any hit, fix immediately before proceeding.

## Provider CLI Invocations

Provider tools that print failed argv on retry leak credentials into the conversation transcript. Every Vercel call from the pipeline (Phase 6 deploy hooks, Phase 7 site updates, manual `vercel deploy` shells) MUST go through the wrapper:

```bash
# CORRECT  -  token via env var, output redacted automatically. Token resolution
# goes through the cross-platform credential helper so the same snippet works
# on macOS / Linux / Windows installs:
VERCEL_TOKEN="$(~/.claude/lib/credential-store.sh get mmerterden_Vercel_Access_Token)" \
  bash $HOME/.claude/lib/vercel-deploy.sh deploy --prod

# Health check before deploy:
bash $HOME/.claude/lib/vercel-deploy.sh doctor
```

**Forbidden:**

```bash
# Vercel CLI prints argv on retry → token leaks to transcript.
vercel deploy --token=vcp_...
vercel deploy --token "$VERCEL_TOKEN"
```

The wrapper at `$HOME/.claude/lib/vercel-deploy.sh` (installed to `~/.claude/lib/`, `~/.copilot/lib/` and `~/.codex/lib/` with the rest of the shell libraries) refuses any `--token=` argv input, runs the CLI with `VERCEL_TOKEN` env, and pipes every stdout/stderr line through a redact filter that scrubs `vcp_...`, `Bearer ...`, and JSON-body token shapes. Regression gate: `smoke-vercel-deploy-redact.sh` (12 assertions).

Same rule applies to any future provider wrapper (e.g. `cloudflare-deploy.sh`, `npm-publish.sh`)  -  never pass tokens via argv when the underlying CLI may echo argv on failure.

## User Interaction Discipline

These rules govern when and how the pipeline asks the user, and when it must NOT ask.

- **No auto-commit.** Never run `git commit` without explicit user approval, even when build + review pass. The Phase 4 commit step pauses and asks ("commit message X, OK?"); only on `yes` does the commit run. Applies to checkpoint / WIP commits during Dev (Phase 3) and to the orchestrator's own state-save commits.
- **No micro-confirmations for reversible local work.** Local edits, lint fixes, test additions, scaffold files, in-worktree rewrites  -  just do them. Ask only for irreversible or shared-state actions: `git push`, `git push --force`, tag, third-party POST (Confluence, Jira, Slack), publish, deletes spanning many files, branch deletion. The threshold is "can the user undo this in 30 seconds via Cmd+Z or `git restore`?"  -  if yes, do not ask.
- **Empty `AskUserQuestion` answer is not consent.** When the user submits without picking any option (empty answers map), do NOT silently apply the "Recommended" default. Re-ask the question, or proceed treating each item as unselected. Future-behavior preference questions are not preemptive  -  only ask when the answer changes what happens NOW.
- **Question phrasing in Plan Mode.** Don't reference "the plan" in `AskUserQuestion` text while in plan mode  -  the user cannot see the plan until `ExitPlanMode`. Use `ExitPlanMode` for approval; `AskUserQuestion` only for choosing between concrete options.

## Figma Access Tier (pipeline-wide, BLOCKING)

When any task references a Figma frame (URL, node ID, or free-text "from the design"), the pipeline MUST establish a Figma ground-truth artefact via a 3-tier fallback chain before any UI line is written. The tier in use is persisted as `state.figmaAccess.tier` and read by every phase that consumes or verifies the reference (Phase 0 intake, Phase 1 analysis, Phase 2 planning, Phase 3 dev, Phase 4 review, Phase 5 manual test, Phase 7 channels).

| Tier | Source | When chosen | Code Connect available |
|---|---|---|---|
| 1 | Figma MCP server (`mcp__claude_ai_Figma__get_design_context`, `get_screenshot`, `get_metadata`); MCP token resolves through `prefs.global.keychainMapping.figma_mcp` (for MCP server config bootstrap) | The host serves the `mcp__claude_ai_Figma__*` tools AND MCP auth succeeds (after one re-auth retry; on continued auth failure the user is asked: recreate the MCP token or continue with the PAT  -  never a silent fallthrough) | yes (`CodeConnectSnippet` blocks) |
| 2 | Figma REST API (`GET /v1/files/{fileKey}/nodes`, `GET /v1/images/{fileKey}`) with Personal Access Token resolved via `~/.claude/lib/credential-store.sh get <logical-key>` where `<logical-key>` = `prefs.global.keychainMapping.figma` | Tier 1 unreachable AND PAT is mapped; on a 401/403 the user is asked the Expired-token decision (regenerate / different token / skip) before moving to Tier 3 | no, fall back to repo `*.figma.swift` / `*.figma.kt` mappings keyed by `fileKey` + `nodeId` |
| 3 | User-attached screenshot in chat or task attachment | Tiers 1 + 2 both unreachable AND user has provided a screenshot | no  -  record an Open Question, pick the closest existing primitive WITH user confirmation, set Phase 4 reviewer flag to `review_blocking` |

**Tier 1 availability is per host, and "unavailable" is not "auth failed".** Tools absent (the normal case on Copilot CLI and Codex CLI, where the installer registers only the toolkit MCP) means Tier 2 is the expected entry point: record `figmaAccess.tier1Unavailable = "host"`, skip the re-auth retry, and never raise the MCP-token question. Tools present but failing auth is `"auth"`, where the retry does apply. Probe mechanics: `phases/phase-0-init.md`.

**Halt condition.** If all three tiers fail, halt the run and ask the user how to proceed. Never substitute primitives, never guess from Confluence prose, never derive layout from a text description.

**Synced-file rule.** The literal Keychain service name for the Figma PAT is NEVER embedded in any synced file under `~/.claude/commands/` or `~/.copilot/skills/`. The mapping resolves at runtime through `prefs.global.keychainMapping.figma`. See the Synced Command Hygiene rule below.

Full chain definition, REST endpoints, URL parsing, Code Connect snippet rules, and the pre-UI checklist live in `rules/figma-pipeline.md` "MUST: Figma access - 3-tier fallback chain (BLOCKING, pipeline-wide)". Do not duplicate that text here; the canonical copy is the rule file.

### Figma Access by Phase (pipeline-wide BLOCKING, v9.0.0)

Per Locked decision 30 of `/multi-agent:analysis` and the parallel rule in `$HOME/.claude/rules/figma-pipeline.md`, Figma MCP / REST is allowed only in the analysis phase. Phase 2 through Phase 7 in every orchestrator mode (Full or Short, `--local`, autopilot) consume the analysis document + repo Code Connect mappings.

| Phase | MCP | REST | Sole design source |
|---|---|---|---|
| Analysis Phase 1 | allowed | allowed (Tier 2) | Figma ground truth |
| Phase 2 Planning | forbidden | forbidden | analysis/<feature>-<platform>.md Section 6, 14 |
| Phase 3 Dev | forbidden | forbidden | analysis doc Section 5, 6, 7, 13 + Code Connect *.figma.swift |
| Phase 4 Review | forbidden | forbidden | analysis doc Section 21 References citations |
| Phase 5 Test | forbidden | forbidden | analysis doc Section 13.6 + 15.2 variant subset |
| Phase 6 Commit | forbidden | forbidden | analysis doc URL in PR body |
| Phase 7 Report | forbidden | forbidden | analysis doc embedded in channel artefacts |

Violation: smoke gate `smoke-no-mcp-in-dev-phases.sh` fails the run.

Memory: [[mcp-only-in-analysis]]

## Supported Version Gate (pipeline-wide)

The npm dist-tag `required` names the oldest version a user may run. Most releases do not set it, and nothing changes for them. A release that changes a contract a run depends on sets it, and then an older install is not "behind"  -  it is wrong, and its output would have to be redone.

- **Where it runs**: Phase 0 Step 0.6 for every pipeline mode, and as the first step of any standalone command that reaches an external system or writes to a repo. The check is cached (`updateCheck.ttlHours`, default 24h) and shared, so a second command in the same window costs nothing.
- **Exempt commands**  -  they are the remedy or cannot depend on a contract: `update`, `setup`, `uninstall`, `help`, `status`, `log`, `search`, `routines`, `forget`, `language`.
- **How to check**: `bash $HOME/.claude/scripts/require-supported-version.sh`. Exit 0 = proceed. Exit 3 = halt: run the `/multi-agent:update` flow, then stop and ask the user to re-issue the command  -  never continue into the work on the freshly updated install, because this run's docs and scripts were already loaded from the old version.
- **Fail-open**: offline, blocked registry, unknown local version or no `required` tag all exit 0. A version gate that bricks the pipeline on a flaky network is worse than the drift it guards.
- **Not opt-out**: `updateCheck.enabled: false` silences the advisory "update available" nag, not this gate. The only override is `MULTI_AGENT_ALLOW_OUTDATED=1`, which exits 0 with a warning and must be logged in the run record when used.
- **Setting the floor** (maintainers): `npm dist-tag add @<scope>/multi-agent-pipeline@<version> required`. It is set out of band, so a release can be promoted to required after the fact, and demoted the same way.

## Naming & Hygiene

- **No task-sequence shortcuts in code.** "F1", "F2", "F3", "Feature1", "Step2" etc. are task-numbering UX labels in pickers / Confluence; they do not belong in identifiers, comments, log categories, commit messages, branch names, or file names. Use the full feature name (`FlightStatus`, `CheckoutFlow`, `PasswordReset`). Existing legacy references stay until a touch-up touches the same line; do not mass-rename without scope.
- **No section sign `section` character.** Use the plain word `section` / `bölüm` / `böl.` or omit. This rule is part of the humanizer punctuation policy (no em-dash, en-dash, ellipsis, curly quotes, section sign) and applies to chat, docs, code comments, commit messages, analysis artifacts.
- **Humanizer always on.** Every user-facing artifact  -  Confluence page, Jira description, commit message, PR body, code comments, status updates, chat replies  -  runs through the humanizer punctuation policy. No opt-in flag is needed; assume it is wanted. Per-channel tone differs (Local file = technical-explanatory, Confluence = formal-stakeholder, Jira = informal-technical) but the punctuation policy is fixed.

## Synced Command Hygiene

The `~/.claude/commands/` and `~/.copilot/skills/` trees are synced to a shared / semi-public pipeline repo. Anything that contains a per-user identifier leaks across users.

- **Never embed personal Keychain service names** (e.g. literals containing your username or full-name initials) in files under `~/.claude/commands/` or `~/.copilot/skills/`. Route every token lookup through `~/.claude/lib/credential-store.sh get <logical-key>` where `<logical-key>` is read from `prefs.global.keychainMapping.<provider>` so the mapping lives in the per-user preferences file, not the command file.
- **Never embed personal repo paths, email addresses, machine names** in synced commands. Use config placeholders (`{REPO_OWNER}`, `{IDENTITY_NAME}`, etc.) resolved at run-time.
- The CI sync filter strips these patterns; embedding them is a regression caught after the fact, after the leaked value is already in the public repo's history.

## Subagent Contract

- Every subagent (Explore, code-reviewer, architect, dev task) returns **structured JSON**, not prose:
  ```json
  {"status": "complete", "findings": [...], "files_changed": [...], "blocking": false}
  ```
- Subagents receive **minimum viable context**  -  diff + relevant files, never the whole repo.
- Subagents never write state, never post to external systems  -  the orchestrator owns side effects.
