<!-- contamination-scan: skip
     This file legitimately uses the literal paths `docs/design-system/`,
     `docs/references/`, `docs/decisions/`, etc. inside autodetection probe
     descriptions (§ 6). Those are NOT hardcoded references — they are
     literal strings describing what to look for on disk. Autofixing them
     would make the probe nonsensical. -->
# Project Context Protocol

**Status**: MANDATORY pre-read for any BALDART skill or agent invocation that touches project-specific paths, brand identity, or technology stack choices.

This module defines how skills resolve project-specific facts (paths, identity, stack, feature toggles) instead of hard-coding them. It exists so that the same skill can run unchanged across a fidelity-app repo, an internal admin tool, or a marketing site.

## 1) The three layers

BALDART skills resolve project context through three layers, in this order:

1. **`baldart.config.yml`** (repo root) — variables: paths, identity, stack, feature flags. Authoritative source of structural facts.
2. **Skill base content** (`.claude/skills/<name>/`) — generic instructions written against config keys, not hard-coded paths.
3. **`.baldart/overlays/<skill-name>.md`** (consumer-authored, optional) — project-specific extensions: brand voice, custom workflows, project-specific BLOCKING rules.

Skills MUST consult all three layers in this order on every invocation.

## 2) Reading `baldart.config.yml`

Before executing any step that depends on project-specific facts, the skill MUST:

1. Read `baldart.config.yml` from the repo root.
2. Resolve the keys it declares in its **Project Context** header.
3. Apply the **Missing-key protocol** below if any required key is empty / absent / unanswered.

The schema is documented in [`framework/docs/PROJECT-CONFIGURATION.md`](../docs/PROJECT-CONFIGURATION.md). Top-level keys:

| Key | Purpose |
|---|---|
| `version` | Schema version. Skills MAY refuse to run on a higher major than they understand. |
| `paths.*` | Canonical document and source paths. Empty string = concept absent in this project. |
| `identity.*` | Brand name, design philosophy, language, audience segments. |
| `stack.*` | Charting / animation / testing technology choices (canonical + forbidden). |
| `features.*` | Explicit booleans gating BLOCKING reads (e.g. `has_design_system`). |

## 3) Missing-key protocol (option A — always ask)

When a key the skill declares as required is missing, empty, or a `features.*` flag is absent:

1. **Do not assume.** Never default to `false`. Never invent a path.
2. **Ask the user** with a concrete question naming the missing key:
   > "`baldart.config.yml` does not declare `paths.design_system`. Does this project have a design system, and if so where is its entry point?"
3. **Persist the answer.** Suggest running `npx baldart configure` so the answer is written to `baldart.config.yml` and not asked again next time. If the user declines, proceed with the value but note it as ASSUMED.
4. **Do not silently degrade.** If a BLOCKING read is gated by an unanswered `features.*` flag, the skill MUST ask before deciding whether to skip — it MUST NOT default to skipping.

## 4) Skill header convention (3-5 lines)

Every skill that depends on project context MUST include a **Project Context** block immediately after its frontmatter. The block is short and dense — the full protocol (this file) is loaded once, not duplicated per skill.

Template ([`framework/templates/skill-project-context.snippet.md`](../templates/skill-project-context.snippet.md)):

```markdown
## Project Context

**Reads from `baldart.config.yml`:** `paths.X`, `paths.Y`, `identity.Z`, `stack.W`.
**Gated by features:** `features.has_FOO` (BLOCKING when true), `features.has_BAR` (skip step N when false).
**Overlay:** loads `.baldart/overlays/<this-skill>.md` if present (see `framework/agents/project-context.md` § 5).
**On missing/empty keys:** ask the user; do not assume defaults.
```

Skills MUST NOT re-explain the protocol. They MUST cite `framework/agents/project-context.md` for the rules.

## 5) Overlay loading rule

Path: `.baldart/overlays/<skill-name>.md` (matches the skill directory name, e.g. `.baldart/overlays/ui-design.md`).

### Frontmatter (mandatory)

```yaml
---
base_skill: ui-design
base_skill_version: 3.0.0   # version of the framework when this overlay was authored
mode: extend                # extend | override
---
```

- `base_skill_version` — captures the framework version the overlay was written against. `baldart status` and `baldart update` use this to warn when the base skill has evolved (silent-conflict prevention).
- `mode: extend` (default) — overlay rules are ADDED to base skill rules. The skill runs both.
- `mode: override` — overlay rules REPLACE conflicting base-skill rules. Use sparingly.

### Precedence rules

1. **Default**: overlays *extend* base skills. If overlay and base both apply, both run.
2. **Explicit override**: inside a skill overlay, a section marked `## [OVERRIDE] <topic>` signals the agent to prefer the overlay's version of `<topic>` over the base skill's. **This is a textual convention, not a parsed merge.** Skill overlays use runtime concatenation (§5.bis): nothing in the CLI parses `[OVERRIDE]`/`[APPEND]`/`[PREPEND]` for skills — the merger (`overlay-merger.js`) parses those markers **only for agent/command overlays**, which are compiled at `add`/`update` time. For skills, the marker is an instruction the executing agent honours when it reads the overlay; honouring it is best-effort, not structurally enforced. (Agent/command overrides, by contrast, are materially fused into the generated file — see §5.bis.)
3. **Stack/identity conflicts**: when an overlay's stack/identity rule contradicts `baldart.config.yml`, the overlay wins (the user wrote it deliberately) — but the skill MUST log a one-line "overlay overrides config" notice in its output.

### Version drift handling

If `base_skill_version` in the overlay is older than the installed framework version:
- The skill loads the overlay normally.
- The skill emits a single-line WARNING at the start of its output: `overlay <name> targets v<X>, installed v<Y> — review for drift`.
- `npx baldart status` lists drifted overlays and suggests `npx baldart update --review-overlays`.

This is non-blocking by design — silent overrides are worse than noisy ones.

## 5.bis) Overlays for agents and commands (v3.8.0+)

Skill overlays use **runtime concatenation** — the SKILL.md body instructs Claude to read `.baldart/overlays/<skill>.md`. Agent and command files do not have that affordance (Claude Code reads a subagent file as a single unit), so BALDART uses a different model: **compile-time merge**.

### Layout

```
.baldart/overlays/
├── ui-design.md                  (skill overlay — runtime, unchanged)
├── agents/
│   └── coder.md                  (agent overlay — compile-time merge)
└── commands/
    └── codexreview.md            (command overlay — compile-time merge)
```

### Frontmatter (mandatory for agent / command overlays)

```yaml
---
base_agent: coder                # or base_command: codexreview
base_agent_version: 3.8.0        # framework version when the overlay was authored
mode: extend                     # extend | replace
---
```

### Section markers in the overlay body

- `## [OVERRIDE] <heading>` — replace the matching `## <heading>` of the base verbatim.
- `## [APPEND] <heading>`   — add content after the base section.
- `## [PREPEND] <heading>`  — add content before the base section.
- `## <heading>` (no marker) — append as a new H2 at the end of the merged file.

If the overlay targets a heading that no longer exists in the base (e.g. it was renamed upstream), BALDART emits a warning at update time and falls back to appending the overlay block as a new section, so content is never silently dropped.

### Generation

At `baldart add` / `baldart update`, for each framework agent/command file `<x>.md`:

- No overlay → BALDART installs a **per-item symlink** in `.claude/agents/<x>.md` (or `.claude/commands/<x>.md`) pointing at the framework source.
- Overlay present → BALDART writes a **generated file** at `.claude/agents/<x>.md` containing base + overlay merged, with a leading marker:

```html
<!-- baldart-generated: kind=agent name=coder base_version=3.8.0 overlay_sha=ab12cd34
     DO NOT EDIT MANUALLY. Edit .baldart/overlays/agents/coder.md instead;
     this file is regenerated by `npx baldart update`. -->
```

The base's frontmatter (`name`, `description`, `tools`, `model`) is preserved verbatim — the overlay's frontmatter is metadata for BALDART only and is NOT propagated to the generated file.

### Drift detection

When `base_agent_version` in the overlay is older than the installed framework version, BALDART compares the base sections the overlay overrides between the two versions. If a targeted section has changed upstream, `baldart update` emits a one-line warning per overlay and suggests bumping `base_agent_version` after reviewing the new base content.

### Hard rules

- Never edit `.claude/agents/<x>.md` or `.claude/commands/<x>.md` if it carries the `baldart-generated` marker — your edits will be overwritten on the next update. Edit the overlay instead. The `framework-edit-gate` hook blocks direct edits to generated files.
- Overlay files (`.baldart/overlays/**`) live in the consumer repo only. `baldart push` operates on `.framework/...` exclusively, so overlays are structurally excluded from upstream contributions.
- A user-authored agent/command with NO framework counterpart (e.g. `.claude/agents/mayo-billing-expert.md`) is left alone forever. Per-item merge only touches names that exist in the framework.

## 6) Autodetection (used by `baldart configure`)

`npx baldart configure` populates `baldart.config.yml` by probing the filesystem. The complete probe table (source of truth: `src/commands/configure.js` `detect()`):

| Probe | Filesystem check | Config keys set |
|---|---|---|
| Design system | `docs/design-system/INDEX.md` exists | `paths.design_system: docs/design-system`, `features.has_design_system: true` |
| UI guidelines | first match of `docs/references/ui-guidelines.md`, `docs/ui-guidelines.md`, `docs/references/brand-guidelines.md` | `paths.ui_guidelines: <found>` |
| API index | first match of `docs/references/api/index.md`, `docs/api/index.md` | `paths.api_index: <found>` |
| API schemas | first match of `docs/references/api/schemas.md`, `docs/api/schemas.md` | `paths.api_schemas: <found>`, `features.has_api_docs: true` |
| API errors | first match of `docs/references/errors.md`, `docs/errors.md` | `paths.api_errors: <found>` |
| Components — primitives | first match of `src/components/ui`, `app/components/ui`, `components/ui` | `paths.components_primitives: <found>` |
| Components — root | first match of `src/components`, `app/components`, `components` | `paths.components_root: <found>` |
| Global styles | first match of `src/app/globals.css`, `app/globals.css`, `src/styles/globals.css`, `styles/globals.css` | `paths.global_styles: <found>` |
| Backlog | `backlog/*.yml` count > 0 | `paths.backlog_dir: backlog`, `features.has_backlog: true` |
| ADR | `docs/decisions/ADR-*.md` count > 0 | `paths.adrs_dir: docs/decisions`, `features.has_adrs: true` |
| PRD | `docs/prd/` exists | `paths.prd_dir: docs/prd`, `features.has_prd_workflow: true` |
| Docs root | `docs/` exists | `paths.docs_dir: docs` |
| References dir | `docs/references/` exists | `paths.references_dir: docs/references` |
| LLM wiki | `docs/wiki/` exists | `paths.wiki_dir: docs/wiki`, `features.has_wiki_overlay: true` |
| E2E tests | first match of `tests/e2e`, `e2e`, `tests/playwright`, `tests/cypress` | `paths.e2e_tests_dir: <found>` |
| Brand name | `package.json#name` | `identity.brand_name: <name>` |
| Charting canonical | `package.json` lists any of `recharts`, `@nivo/heatmap`, `@nivo/bar`, `@nivo/line` | `stack.charting.canonical: [<found>]` |
| Charting wrappers | first match of `src/components/charts`, `app/components/charts` | `stack.charting.wrappers_root: <found>` |
| Animation canonical | `package.json` lists any of `framer-motion`, `lottie-react`, `gsap`, `motion` | `stack.animation.canonical: [<found>]` |
| E2E framework | `playwright.config.{ts,js}` → `playwright`; `cypress.config.{ts,js}` → `cypress` | `stack.testing.e2e: <name>` |

Autodetection PROPOSES values; the user always confirms. The point is to make first-run feel like one prompt-confirmation cycle, not 20 hand-typed YAML keys.

The `multi_tenant_theming` feature is not auto-detectable (no filesystem signal). `configure` always prompts for it explicitly; non-interactive mode omits the key from the written YAML so the always-ask contract kicks in on first skill invocation.

## 7) Why this design

- **Symlinks survive updates.** The skill base content is symlinked from `.framework/`. Without indirection, every `npx baldart update` would overwrite consumer customizations. With indirection, customizations live in `baldart.config.yml` (copy, never overwritten) and `.baldart/overlays/` (consumer-owned).
- **Generic + opinionated coexist.** Generic skill content is the framework's IP. The opinionated parts (Neo-Brutalism, merchant/customer split, Recharts-only) live in the consumer's overlay. The same `ui-design` skill can be reused with a different overlay in every project.
- **Always-ask beats silent-default.** The cost of one extra question per missing key is trivial. The cost of a skill silently using `docs/design-system/INDEX.md` in a project that has no design system is silent failure, hallucinated paths, broken output.

## 8) Author checklist (when adding/modifying a skill)

- [ ] Skill declares its config dependencies in a **Project Context** header (3-5 lines).
- [ ] No hard-coded paths in skill body — every path references a `baldart.config.yml` key.
- [ ] No hard-coded identity claims — brand voice / design philosophy / language come from `identity.*`.
- [ ] No hard-coded stack imposition — canonical/forbidden libraries come from `stack.*`.
- [ ] BLOCKING reads are gated by `features.*` flags, not assumed-always-present.
- [ ] Skill explicitly invokes overlay loading: "load `.baldart/overlays/<name>.md` if present".
- [ ] If the skill ships an opinionated default that was previously hard-coded, the opinion is moved to `framework/templates/overlays/<name>.<flavour>-example.md` and the skill body stays neutral.
