From 56d365f99c8dd18bed7bb8d64c27c32ff1c50348 Mon Sep 17 00:00:00 2001 From: fullsend-code <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 14:57:41 +0000 Subject: [PATCH 1/2] docs(#5677): replace ASCII box table with markdown table Convert the Agent Role Architecture ASCII box-drawing table in customizing-agents.md to a standard markdown table. The box-drawing format was fragile and prone to display-width misalignment when edited (as seen in PR #5413 review). The markdown table preserves the same tabular data (Role, GitHub App, Purpose) while being immune to alignment issues. Naming conventions and the "fix" role note are restructured as a list and blockquote respectively. Closes #5677 --- docs/guides/user/customizing-agents.md | 42 +++++++++++--------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/docs/guides/user/customizing-agents.md b/docs/guides/user/customizing-agents.md index 0dd9d7070b..8d7a91aa1f 100644 --- a/docs/guides/user/customizing-agents.md +++ b/docs/guides/user/customizing-agents.md @@ -326,31 +326,23 @@ See [ADR 0056](../../ADRs/0056-per-repo-precommit-tools-registry.md) for the ful Each agent role has its own identity, permissions, and purpose: -``` -┌─────────────────────────────────────────────────────────────┐ -│ Agent Role Architecture │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ Role GitHub App Purpose │ -│ ───── ────────── ─────── │ -│ fullsend {org}-fullsend[bot] Dispatch/control │ -│ triage {org}-triage[bot] Issue triage │ -│ coder {org}-coder[bot] Code generation │ -│ review {org}-review[bot] PR review │ -│ fix (reuses coder app) Fix failures │ -│ retro {org}-retro[bot] Retrospectives │ -│ prioritize {org}-prioritize[bot] Backlog priority │ -│ │ -│ App naming: {org}-{role} │ -│ Bot naming: {org}-{role}[bot] │ -│ PEM storage: GCP Secret Manager or filesystem (standalone) │ -│ Secret name: fullsend-{role}-app-pem │ -│ │ -│ Note: "fix" role reuses the "coder" app and PEM — no │ -│ separate GitHub App or secret is created for it. │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` +| Role | GitHub App | Purpose | +|------|------------|---------| +| `fullsend` | `{org}-fullsend[bot]` | Dispatch/control | +| `triage` | `{org}-triage[bot]` | Issue triage | +| `coder` | `{org}-coder[bot]` | Code generation | +| `review` | `{org}-review[bot]` | PR review | +| `fix` | (reuses coder app) | Fix failures | +| `retro` | `{org}-retro[bot]` | Retrospectives | +| `prioritize` | `{org}-prioritize[bot]` | Backlog priority | + +**Naming conventions:** +- App naming: `{org}-{role}` +- Bot naming: `{org}-{role}[bot]` +- PEM storage: GCP Secret Manager or filesystem (standalone) +- Secret name: `fullsend-{role}-app-pem` + +> **Note:** The "fix" role reuses the "coder" app and PEM — no separate GitHub App or secret is created for it. ## Customization Examples From 9e3685441928c1d069535c012abd406583aea736 Mon Sep 17 00:00:00 2001 From: fullsend-fix <278716306+fullsend-ai-coder[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 18:21:57 +0000 Subject: [PATCH 2/2] docs(#5677): fix misaligned ASCII box-drawing in docs Fix ASCII box-drawing alignment issues across 5 doc files: - architecture.md: fix 1-char misalignment on 3 lines in MVP diagram - infrastructure-reference.md: widen inner GCF/STS boxes to fit content that overflowed borders; fix padding on 6 lines across 3 diagrams - runtimes.md: fix 6 lines with 1-char-too-wide padding - customizing-agents.md: fix 1 line with 1-char-too-wide padding - cli-internals.md: fix ~25 lines across 3 diagrams with various 1-2 char alignment errors and content overflow Addresses review feedback on #5678 --- docs/architecture.md | 6 +- docs/guides/dev/cli-internals.md | 70 +++++------ .../infrastructure-reference.md | 114 +++++++++--------- docs/guides/user/customizing-agents.md | 2 +- docs/runtimes.md | 12 +- 5 files changed, 102 insertions(+), 102 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index f4406545f5..05e34c68ba 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -629,14 +629,14 @@ GitHub event ──► SHIM WORKFLOW (fullsend.yml in enrolled repo) ╔═══════════════════════════════════════════════════════════════╗ ║ DISPATCH WORKFLOW (.fullsend repo, dispatch.yml) ║ ║ ║ - ║ Mints OIDC token → Cloud Function (token mint) → scoped ║ - ║ GitHub App installation token per agent role. ║ + ║ Mints OIDC token → Cloud Function (token mint) → scoped ║ + ║ GitHub App installation token per agent role. ║ ║ Dispatches per-role agent workflows (code.yml, triage.yml). ║ ╚═══════════════════════════════════════════════════════════════╝ │ ▼ ╔═══════════════════════════════════════════════════════════════╗ - ║ AGENT WORKFLOW (.fullsend repo, e.g. code.yml) ║ + ║ AGENT WORKFLOW (.fullsend repo, e.g. code.yml) ║ ║ ║ ║ Validates source repo is enrolled in config.yaml. ║ ║ Uses scoped GitHub App tokens: ║ diff --git a/docs/guides/dev/cli-internals.md b/docs/guides/dev/cli-internals.md index 385c6d0176..9d713b0aa2 100644 --- a/docs/guides/dev/cli-internals.md +++ b/docs/guides/dev/cli-internals.md @@ -218,21 +218,21 @@ Both per-org and per-repo modes share the same core pipeline. The code follows t ``` ┌─────────────────────────────────────────────────────────────────┐ -│ Unified Install Pipeline (both modes) │ +│ Unified Install Pipeline (both modes) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ fullsend admin install │ │ ┌──────────────────────┐ │ │ │ Parse target │ │ -│ │ "acme" → org │ │ -│ │ "acme/repo" → repo │ │ +│ │ "acme" → org │ │ +│ │ "acme/repo" → repo │ │ │ └──────────┬───────────┘ │ │ ▼ │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ Phase 1: Discover (read-only) │ │ │ │ │ │ │ │ a. Discover mint --mint-url / --mint-project / default │ │ -│ │ └─ DiscoverMint() → check if GCF exists, get URL │ │ +│ │ └─ DiscoverMint() → check if GCF exists, get URL │ │ │ │ b. Resolve existing app IDs from mint env vars │ │ │ │ └─ ROLE_APP_IDS (role → app ID, shared) → skip app │ │ │ │ creation when all roles are present │ │ @@ -242,7 +242,7 @@ Both per-org and per-repo modes share the same core pipeline. The code follows t │ │ Phase 2: App setup (shared: runAppSetup) │ │ │ │ │ │ │ │ For each role in --agents: │ │ -│ │ - Create/reuse GitHub App ({appSet}-{role} via --app-set)│ │ +│ │ - Create/reuse GitHub App ({appSet}-{role} --app-set) │ │ │ │ - Download PEM key from App creation flow │ │ │ │ - Store PEM in GCP Secret Manager │ │ │ │ - Record App ID + Client ID │ │ @@ -269,8 +269,8 @@ Both per-org and per-repo modes share the same core pipeline. The code follows t │ │ │ │ │ │ Both modes: ProvisionWIF() → create pool, provider, IAM │ │ │ │ ┌──────────────────────────────────────────┐ │ │ -│ │ │ Per-org: org-wide WIF provider │ │ │ -│ │ │ Per-repo: repo-scoped (mintcore.BuildRepoProviderID)│ │ │ +│ │ │ Per-org: org-wide WIF provider │ │ │ +│ │ │ Per-repo: repo-scoped WIF provider │ │ │ │ │ └──────────────────────────────────────────┘ │ │ │ └──────────┬─────────────────────────────────────────────────┘ │ │ ▼ │ @@ -278,19 +278,19 @@ Both per-org and per-repo modes share the same core pipeline. The code follows t │ │ Phase 5: Write scaffold + config files │ │ │ │ │ │ │ │ Both modes: write workflow files (customized/ deprecated │ │ -│ │ by ADR-0064; use migrate-customizations to convert) │ │ -│ │ CommitScaffoldFiles() delivery modes: │ │ +│ │ by ADR-0064; use migrate-customizations to convert) │ │ +│ │ CommitScaffoldFiles() delivery modes: │ │ │ │ Default (PR): create feature branch → commit → open PR │ │ │ │ --direct: try CommitFiles (default branch) │ │ │ │ if ErrBranchProtected → fall back to PR mode │ │ │ │ ┌──────────────────────────────────────────┐ │ │ -│ │ │ Per-org: create .fullsend config repo │ │ │ -│ │ │ push reusable workflows │ │ │ -│ │ │ vendor fullsend binary (opt) │ │ │ -│ │ │ │ │ │ -│ │ │ Per-repo: write .fullsend/ dir in repo │ │ │ -│ │ │ push shim workflow template │ │ │ -│ │ │ vendor fullsend binary (opt) │ │ │ +│ │ │ Per-org: create .fullsend config repo │ │ │ +│ │ │ push reusable workflows │ │ │ +│ │ │ vendor fullsend binary (opt) │ │ │ +│ │ │ │ │ │ +│ │ │ Per-repo: write .fullsend/ dir in repo │ │ │ +│ │ │ push shim workflow template │ │ │ +│ │ │ vendor fullsend binary (opt) │ │ │ │ │ └──────────────────────────────────────────┘ │ │ │ └──────────┬─────────────────────────────────────────────────┘ │ │ ▼ │ @@ -300,18 +300,18 @@ Both per-org and per-repo modes share the same core pipeline. The code follows t │ │ Both modes write the same credential set: │ │ │ │ Secrets: FULLSEND_GCP_PROJECT_ID │ │ │ │ FULLSEND_GCP_WIF_PROVIDER │ │ -│ │ Variables: FULLSEND_GCP_REGION │ │ -│ │ FULLSEND_MINT_URL │ │ +│ │ Variables: FULLSEND_GCP_REGION │ │ +│ │ FULLSEND_MINT_URL │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────┐ │ │ -│ │ │ Per-org: secrets → .fullsend config repo │ │ │ -│ │ │ MINT_URL → org variable │ │ │ -│ │ │ + repo var (dot-prefix fix) │ │ │ -│ │ │ + PEM keys as repo secrets │ │ │ -│ │ │ + client IDs as repo variables │ │ │ -│ │ │ │ │ │ -│ │ │ Per-repo: secrets → target repo │ │ │ -│ │ │ + FULLSEND_PER_REPO_GUARD=true │ │ │ +│ │ │ Per-org: secrets → .fullsend config repo│ │ │ +│ │ │ MINT_URL → org variable │ │ │ +│ │ │ + repo var (dot-prefix fix) │ │ │ +│ │ │ + PEM keys as repo secrets │ │ │ +│ │ │ + client IDs as repo variables │ │ │ +│ │ │ │ │ │ +│ │ │ Per-repo: secrets → target repo │ │ │ +│ │ │ + FULLSEND_PER_REPO_GUARD=true │ │ │ │ │ └──────────────────────────────────────────┘ │ │ │ └──────────┬─────────────────────────────────────────────────┘ │ │ ▼ │ @@ -381,11 +381,11 @@ Vendoring commit messages use title + body (upload and stale delete). `github st ``` ┌─────────────────────────────────────────────────────────────────┐ -│ Sandbox Lifecycle (run.go) │ +│ Sandbox Lifecycle (run.go) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ -│ │ Load harness │ LoadWithBase: unmarshal → compose base → │ +│ │ Load harness │ LoadWithBase: unmarshal → compose base → │ │ │ │ ResolveForge(--forge / env) → Validate │ │ └──────┬──────┘ │ │ ▼ │ @@ -412,9 +412,9 @@ Vendoring commit messages use title + body (upload and stale delete). `github st │ └──────┬───────────┘ │ │ ▼ │ │ ┌──────────────────┐ │ -│ │ Create() │ openshell sandbox create │ -│ │ │ --image {harness.image} │ -│ │ │ Returns sandbox ID │ +│ │ Create() │ openshell sandbox create │ +│ │ │ --image {harness.image} │ +│ │ │ Returns sandbox ID │ │ └──────┬───────────┘ │ │ ▼ │ │ ┌──────────────────────────────────────────┐ │ @@ -434,7 +434,7 @@ Vendoring commit messages use title + body (upload and stale delete). `github st │ │ ├── CLAUDE_CONFIG_DIR=/sandbox/claude-config│ │ │ │ ├── FULLSEND_OUTPUT_DIR=... │ │ │ │ ├── FULLSEND_FETCH_URL=... (if allow_runtime_fetch)│ │ -│ │ ├── FULLSEND_FETCH_TOKEN= (if above)│ │ +│ │ ├── FULLSEND_FETCH_TOKEN= (if above)│ │ │ │ └── sources .env.d/*.env files │ │ │ └──────────┬───────────────────────────────┘ │ │ ▼ │ @@ -465,7 +465,7 @@ Vendoring commit messages use title + body (upload and stale delete). `github st │ ▼ │ │ ┌──────────────────┐ │ │ │ Extract output │ SafeDownload() with sanitization: │ -│ │ │ - Remove dangerous symlinks (sandbox escape) │ +│ │ │ - Remove dangerous symlinks (escape) │ │ │ │ - Remove .git/hooks/ (hook injection) │ │ │ │ │ │ │ │ With validation_loop: SafeDownload │ @@ -524,7 +524,7 @@ Vendoring commit messages use title + body (upload and stale delete). `github st │ └──────┬───────────┘ │ │ ▼ │ │ ┌──────────────────┐ │ -│ │ Delete() │ openshell sandbox delete │ +│ │ Delete() │ openshell sandbox delete │ │ │ │ Cleanup sandbox resources │ │ └──────────────────┘ │ │ │ @@ -629,7 +629,7 @@ var executableFiles = map[string]struct{}{ ``` ┌─────────────────────────────────────────────────────────────────┐ -│ End-to-End: Issue Triage → Code → Review │ +│ End-to-End: Issue Triage → Code → Review │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. Issue created on target repo │ diff --git a/docs/guides/infrastructure/infrastructure-reference.md b/docs/guides/infrastructure/infrastructure-reference.md index d31f793ac0..cd0f72851c 100644 --- a/docs/guides/infrastructure/infrastructure-reference.md +++ b/docs/guides/infrastructure/infrastructure-reference.md @@ -31,43 +31,43 @@ The mint exchanges GitHub OIDC tokens for scoped GitHub App installation tokens. │ └──────────┬───────────────────────────────────────┘ │ │ │ │ │ ▼ │ -│ ┌──────────────────────────────────────────────────┐ │ -│ │ GCF: Token Mint │ │ -│ │ │ │ -│ │ 1. Prevalidate OIDC JWT │ │ -│ │ ├─ Check iss == token.actions.githubusercontent.com │ -│ │ ├─ Extract repository_owner → ALLOWED_ORGS check │ -│ │ │ (explicit org list, or * for public mint mode) │ -│ │ └─ Validate job_workflow_ref provenance │ -│ │ (tight: .fullsend / upstream / per-repo; │ -│ │ public: upstream fullsend-ai/fullsend only) │ -│ │ │ │ -│ │ 2. STS Token Exchange │ │ -│ │ ├─ POST securitytoken.googleapis.com │ │ -│ │ │ grant_type=urn:ietf:params:oauth: │ │ -│ │ │ grant-type:token-exchange │ │ -│ │ ├─ WIF pool validates OIDC token │ │ -│ │ └─ Returns GCP federated access token │ │ -│ │ │ │ -│ │ 3. Lookup PEM from Secret Manager │ │ -│ │ ├─ Secret name: fullsend-{role}-app-pem │ │ -│ │ └─ Returns PEM private key bytes │ │ -│ │ │ │ -│ │ 4. Generate GitHub App JWT │ │ -│ │ ├─ Sign with PEM key (RS256) │ │ -│ │ ├─ App ID from ROLE_APP_IDS env │ │ -│ │ └─ 10-minute expiry │ │ -│ │ │ │ -│ │ 5. Find Installation │ │ -│ │ ├─ GET /app/installations │ │ -│ │ └─ Match by org login │ │ -│ │ │ │ -│ │ 6. Create Scoped Installation Token │ │ -│ │ ├─ POST /installations/{id}/access_tokens │ │ -│ │ ├─ Scope to requested repos[] │ │ -│ │ └─ Apply RolePermissions() minimum set │ │ -│ │ │ │ -│ └──────────┬───────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────────────────┐ │ +│ │ GCF: Token Mint │ │ +│ │ │ │ +│ │ 1. Prevalidate OIDC JWT │ │ +│ │ ├─ Check iss == token.actions.githubusercontent.com │ │ +│ │ ├─ Extract repository_owner → ALLOWED_ORGS check │ │ +│ │ │ (explicit org list, or * for public mint mode) │ │ +│ │ └─ Validate job_workflow_ref provenance │ │ +│ │ (tight: .fullsend / upstream / per-repo; │ │ +│ │ public: upstream fullsend-ai/fullsend only) │ │ +│ │ │ │ +│ │ 2. STS Token Exchange │ │ +│ │ ├─ POST securitytoken.googleapis.com │ │ +│ │ │ grant_type=urn:ietf:params:oauth: │ │ +│ │ │ grant-type:token-exchange │ │ +│ │ ├─ WIF pool validates OIDC token │ │ +│ │ └─ Returns GCP federated access token │ │ +│ │ │ │ +│ │ 3. Lookup PEM from Secret Manager │ │ +│ │ ├─ Secret name: fullsend-{role}-app-pem │ │ +│ │ └─ Returns PEM private key bytes │ │ +│ │ │ │ +│ │ 4. Generate GitHub App JWT │ │ +│ │ ├─ Sign with PEM key (RS256) │ │ +│ │ ├─ App ID from ROLE_APP_IDS env │ │ +│ │ └─ 10-minute expiry │ │ +│ │ │ │ +│ │ 5. Find Installation │ │ +│ │ ├─ GET /app/installations │ │ +│ │ └─ Match by org login │ │ +│ │ │ │ +│ │ 6. Create Scoped Installation Token │ │ +│ │ ├─ POST /installations/{id}/access_tokens │ │ +│ │ ├─ Scope to requested repos[] │ │ +│ │ └─ Apply RolePermissions() minimum set │ │ +│ │ │ │ +│ └──────────┬───────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ Response: { "token": "ghs_...", "expires_at": "..." } │ @@ -147,7 +147,7 @@ Inference authentication uses GCP Workload Identity Federation (WIF) to allow Gi ``` ┌─────────────────────────────────────────────────────────────┐ -│ Inference Authentication Flow │ +│ Inference Authentication Flow │ ├─────────────────────────────────────────────────────────────┤ │ │ │ GitHub Actions Runner │ @@ -157,19 +157,19 @@ Inference authentication uses GCP Workload Identity Federation (WIF) to allow Gi │ └──────────┬──────────┘ │ │ │ │ │ ▼ │ -│ ┌─────────────────────────────────┐ │ -│ │ GCP Security Token Service (STS)│ │ -│ │ │ │ -│ │ WIF Pool: fullsend-inference │ │ -│ │ WIF Provider: github-oidc │ │ -│ │ │ │ -│ │ Validates OIDC issuer: │ │ -│ │ token.actions.githubusercontent.com │ -│ │ │ │ -│ │ Attribute mapping: │ │ -│ │ sub → assertion.sub │ │ -│ │ repo → assertion.repository │ │ -│ └──────────┬──────────────────────┘ │ +│ ┌──────────────────────────────────────────┐ │ +│ │ GCP Security Token Service (STS) │ │ +│ │ │ │ +│ │ WIF Pool: fullsend-inference │ │ +│ │ WIF Provider: github-oidc │ │ +│ │ │ │ +│ │ Validates OIDC issuer: │ │ +│ │ token.actions.githubusercontent.com │ │ +│ │ │ │ +│ │ Attribute mapping: │ │ +│ │ sub → assertion.sub │ │ +│ │ repo → assertion.repository │ │ +│ └──────────┬───────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────┐ │ @@ -259,11 +259,11 @@ The GCF provisioner handles full GCP infrastructure deployment: ``` ┌─────────────────────────────────────────────────────────────────┐ -│ GCF Provisioner: Provision() Flow │ +│ GCF Provisioner: Provision() Flow │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────────┐ │ -│ │ Get GCP project │ resourcemanager.projects.get │ +│ │ Get GCP project │ resourcemanager.projects.get │ │ │ number │ │ │ └─────────┬─────────┘ │ │ ▼ │ @@ -273,7 +273,7 @@ The GCF provisioner handles full GCP infrastructure deployment: │ └─────────┬─────────┘ │ │ ▼ │ │ ┌───────────────────┐ │ -│ │ Create WIF Pool │ fullsend-inference (or fullsend-pool) │ +│ │ Create WIF Pool │ fullsend-inference (or fullsend-pool) │ │ │ │ (skip if exists) │ │ └─────────┬─────────┘ │ │ ▼ │ @@ -292,7 +292,7 @@ The GCF provisioner handles full GCP infrastructure deployment: │ ▼ │ │ ┌───────────────────┐ │ │ │ Store PEMs in │ fullsend-{role}-app-pem │ -│ │ Secret Manager │ once per agent role (shared) │ +│ │ Secret Manager │ once per agent role (shared) │ │ └─────────┬─────────┘ │ │ ▼ │ │ ┌───────────────────┐ │ @@ -313,8 +313,8 @@ The GCF provisioner handles full GCP infrastructure deployment: │ │ │ POST /v1/token (expect 401) │ │ └─────────┬─────────┘ │ │ ▼ │ -│ Return: FULLSEND_MINT_URL = https://{region}-{project}. │ -│ cloudfunctions.net/fullsend-mint │ +│ Return: FULLSEND_MINT_URL = https://{region}-{project}. │ +│ cloudfunctions.net/fullsend-mint │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` diff --git a/docs/guides/user/customizing-agents.md b/docs/guides/user/customizing-agents.md index 8d7a91aa1f..eab07550e2 100644 --- a/docs/guides/user/customizing-agents.md +++ b/docs/guides/user/customizing-agents.md @@ -171,7 +171,7 @@ Fullsend uses a three-tier configuration inheritance model for all configuration │ └──────────────────────┘ │ │ │ │ Layered directories: │ -│ agents/ skills/ schemas/ harness/ plugins/ │ +│ agents/ skills/ schemas/ harness/ plugins/ │ │ policies/ scripts/ env/ │ │ │ └──────────────────────────────────────────────────────────────┘ diff --git a/docs/runtimes.md b/docs/runtimes.md index 70ab3837da..d5cce564ec 100644 --- a/docs/runtimes.md +++ b/docs/runtimes.md @@ -83,15 +83,15 @@ slots: ``` ┌────────────────────────────────────────────────────────┐ │ Layer 1: Agent Definition (system prompt) │ -│ Source: /sandbox/claude-config/agents/.md │ +│ Source: /sandbox/claude-config/agents/.md │ │ Loaded via: --agent flag │ │ Controls: role, task, tools, disallowedTools, model, │ -│ built-in skills list │ +│ built-in skills list │ │ Authority: highest — repo cannot modify │ ├────────────────────────────────────────────────────────┤ │ Layer 2: Project Instructions (advisory) │ -│ Source: /sandbox/workspace//CLAUDE.md │ -│ /sandbox/workspace//AGENTS.md │ +│ Source: /sandbox/workspace//CLAUDE.md │ +│ /sandbox/workspace//AGENTS.md │ │ Loaded via: Claude Code auto-loads from working dir │ │ Controls: conventions, architecture, domain context │ │ Authority: advisory — cannot override layer 1 │ @@ -100,9 +100,9 @@ slots: │ Personal: /sandbox/claude-config/skills/ (fullsend) │ │ Project: /.claude/skills/ (repo) │ │ Precedence: personal > project (name collision → │ -│ fullsend wins, repo version shadowed) │ +│ fullsend wins, repo version shadowed) │ │ Repo skills extend the agent; customized/skills/ │ -│ overrides at the config layer before upload │ +│ overrides at the config layer before upload │ │ ⚠ customized/ is deprecated per ADR-0064; use │ │ config-driven agents instead (see `fullsend agent │ │ migrate-customizations`) │