# yadflow configuration
# The source is skills/sdlc/config.yaml in the yadflow package. `yad setup` and `yad update` copy it into
# each project as .sdlc/config.yaml, and the skills read that copy. You may edit the copy: `yad update`
# records the hash it wrote in .sdlc/managed.json, reports an edited copy as `modified` and keeps it.
# `yad update --overwrite-local` replaces an edited copy, after saving it beside the file as .yad-orig.

# Where epic "thinking" artifacts and per-epic state live (the product repo).
product_root: "{project-root}"
epics_folder: "{project-root}/epics"

# The Foundation (E75) — the Product level, run once per product before any feature epic (greenfield AND
# brownfield). Its own folder, `foundation/`, under the fixed id `EP-foundation`: the id keeps the `EP-`
# prefix so the review gate, the PR/MR bridge, CI sync and `yad next` all operate on it unchanged.
# `yad foundation new` seeds the ledger; `yad-discovery` authors the sections. A 2-step author→review
# chain whose review binds to the required sections; on approval it terminates at `foundation-done` (no
# Build). Its approval is REPORTED, not enforced, before feature work begins. roadmap.md is the menu of
# features each epic reads (yad-epic / yad-analysis Step 2c) — reference-only, never auto-seeds epics.
foundation:
  epic_id: "EP-foundation"                             # reserved id; yad-epic/yad-analysis never pick it
  location: "{project-root}/foundation/"               # the sections + .sdlc/ ledger + reviews/ live here
  optional: true                                       # a team that knows what to build starts at yad-epic
  modes: [greenfield, brownfield]                       # stack.md / repos.md are code-aware in brownfield
  sections: [purpose.md, scope.md, mvp.md, roadmap.md, stack.md, repos.md]
  optional_sections: [market.md, risks.md]

# The OLD spelling of the Product level — the "epic zero" every release before E75 wrote. Still READ for
# this whole major. `yad migrate --apply` converts it into `foundation/` on a local ledger; a verified
# ledger keeps it (CI owns that ledger). A product has ONE product level: never both.
discovery:
  epic_id: "EP-discovery"                              # reserved id; yad-epic/yad-analysis never pick it
  location: "{project-root}/epics/EP-discovery/"       # discovery artifacts + ledger live here
  optional: true                                       # skip-able; a team that knows what to build starts at yad-epic
  modes: [greenfield, brownfield]                       # current-state is code-aware in brownfield; competitor study in both
  artifacts: [market-research.md, competitor-analysis.md, current-state.md, feasibility.md, requirements.md, roadmap.md]

# The language the skills speak in, and the language they write documents in.
communication_language: English
document_output_language: English

# Default dials applied to every new step (build plan §2).
# The lifecycle, top down: three PARTS, six PHASES inside them, and the steps inside those.
#
# A phase is DERIVED from a step's id and is never written into `state.json` — there is nothing to
# author here, nothing to keep in step, and no way for it to disagree with the step it describes.
# `stepPhase` in cli/epic-state.mjs is the one definition; this block is the same table in the file
# people read. A review gate takes the phase of the artifact it reviews, so `epic-review` is Discover.
#
# Release and Operate are NAMED and not built. No step, skill or gate exists for either yet.
#
# NOTE the word: "phase" here means one of these six. It does NOT mean the numbered development
# phases of yadflow itself (docs/phase-N-build-plan.md), which is what "Phase 4a/4b" means elsewhere
# in these files, nor a gate's internal merge phase.
# The step CATALOGUE lives in code — `STEPS` in `cli/epic-state.mjs` (E4). It is the one place that
# says what a step is: its phase, whether it authors or gates, what artifact it writes, which skill
# runs it, and which step a gate reviews. The phase rows below mirror the `steps:` half of it and a
# test holds the two together row for row; everything else about a step is read from the code, not
# from here. `yad doctor` reports a project chain that disagrees with the catalogue and rewrites
# nothing — for this whole major the FILE wins (rule 3), because the five authoring skills still
# hand-write their `state.json` seed. E5 makes a profile seed the chain from the catalogue instead.
lifecycle:
  parts: [Shape, Build, Run]
  # Lifecycle PROFILES (E5) — the route an epic takes through the steps, defined in code as
  # `LIFECYCLE_PROFILES` (`cli/epic-state.mjs`). Three were routes the skills already seeded by hand:
  # `classic` (the 10-step chain, `epic` first — yad-epic / yad-stub / yad-change), `analysis-first`
  # (the 12-step chain, `analysis` before `epic` — yad-analysis) and `discovery` (the product
  # front-zero — yad-discovery, now the OLD spelling of the Product level). `foundation` is that level
  # as E75 writes it — `yad foundation new`. Two are the short lanes E40 added, which nobody seeded before:
  # `chore` (4 steps, `epic` then `stories`) and `spike` (6, the analysis in front of the chore lane).
  # Neither short lane carries an architecture gate, so neither has a contract of its own, and neither
  # marks any step optional. Each epic RECORDS its route as `profile` in `state.json`
  # (file shape 6); `yad epic new` writes it when it seeds the chain, and for an epic that predates the
  # field `yad migrate` fills it in by matching the chain. The chain stays the truth — `yad doctor`
  # compares the two. NOT the `product.json` `profile`, which is the unrelated setup record
  # { codebase, repo_layout, team_size }.
  profiles: [classic, analysis-first, chore, spike, discovery, foundation]
  # The PRODUCT level's phase (E75) — a ladder of its own, run once per product before any epic, and
  # part of no Part. `discovery` is listed because it is the old spelling of the same level.
  product_phases:
    - { id: foundation, name: Foundation, level: product, built: true, steps: [foundation, discovery] }
  phases:
    - { id: discover, name: Discover, part: Shape, built: true,  steps: [analysis, epic] }
    - { id: design,   name: Design,   part: Shape, built: true,  steps: [architecture, ui-design] }
    - { id: plan,     name: Plan,     part: Shape, built: true,  steps: [stories, test-cases] }
    - { id: build,    name: Build,    part: Build, built: true,  steps: [spec, tasks, implement, checks, engineer-review] }
    - { id: release,  name: Release,  part: Run,   built: false, steps: [] }   # E32 builds it
    - { id: operate,  name: Operate,  part: Run,   built: false, steps: [] }   # E33 builds it
  # `currentStep` markers that are NOT steps: they never appear in `steps[]` and have no phase.
  sentinels: [ready-for-build, backfill-pending, backfill-done, discovery-done, foundation-done]

defaults:
  # The two dials: who does the work, and who moves it forward. Renamed in shape 4; BOTH names are
  # written on every step and the OLD one is still the one read, so an older CLI and the skills that
  # hand-write state.json keep working. See docs/migrations/shape-4.md.
  assistance: review          # none | review | heavy         (old name — still authoritative)
  driver: pair                # human | pair | agent          (new name, shape 4)
  automation: human_approve   # human_approve | machine_advance  (old name — still authoritative)
  advance: human              # human | auto                  (new name, shape 4)
  # Shape steps (foundation [optional, the Product level — discovery is its old spelling], analysis [optional], epic, architecture,
  # ui-design [optional — skippable N/A for UI-less epics via `yad skip <epic> ui-design --reason "<why>"`],
  # stories, test-cases) are locked to `advance: human` and may NOT be set to `advance: auto` in this
  # version (build plan §1, §8.7).
  shape_steps_locked: true
  # Each Shape authoring step opens its own branch at the start of the step (the <step> is the step id:
  # discovery | analysis | epic | architecture | ui-design | stories | test-cases). Git/greenfield-safe; distinct
  # from the verified ledger's review branch (product.artifact_branch). See yad-epic/references/state-schema.md.
  shape_authoring_branch: "<step>/EP-<slug>"

# Team review gate defaults (build plan §3 piece 2, §4).
review_gate:
  # `default_reviewers` is no longer read since E62 (the owner + reviewer role rule is gone). A team gate
  # needs 1 approval from someone other than the author (base 1); the full count adds the risk step
  # below, which is advisory. E72 caps that count at the active people less one and reports it;
  # only the base holds a gate.
  default_reviewers: 1          # legacy, not read since E62
  # Solo mode (a lone developer): a relaxed, opt-in safety guarantee, recorded per-project in
  # .sdlc/product.json (`solo: true`). On GitHub you CANNOT approve your own PR, so requiring an approval
  # would deadlock a solo user. Solo waives the APPROVAL requirement only — the review PR/MR and its
  # MERGE stay (CI still runs on the PR, and the merge is what advances the step). Net: the gate passes
  # on `merged + all comment threads resolved`, no approval needed. NOT a default; team gates are
  # unchanged. (Branch protection must not "require approvals", or the solo dev's own merge is blocked —
  # `yad doctor` warns when it does.) Switch it with `yad mode solo --reason "<why>"` / `yad mode team` (E10),
  # which records who, when and why, and writes `mode: solo|team` beside `solo`; a gate that passes in solo
  # mode records `waived: "solo"` on its closing record.
  solo: false
  escalate_when: [contract, auth, payments]   # risk tags that add a risk step (contract +2, auth/payments +1; the largest, never the sum)
  # PR-driven automation (the `yad gate` CLI). With a Product platform, the review rides the per-step
  # PR/MR: `yad gate sync` maps platform reviews/threads into the file ledger and the step
  # AUTO-ADVANCES on merge, once (a) the approval rule is met, (b) every comment thread is resolved,
  # and (c) the review PR/MR is merged. The merge is the human approval act, so Shape steps still never
  # `advance: auto`. The file ledger stays the source of truth; no platform / no gh|glab => local.
  advance_on: merge             # merge of the approved, fully-resolved review PR advances the step
  revoke_on: artifact-change    # re-hash the artifact (contract surface for architecture); a changed
                                # hash drops the bound approvals so reviewers re-approve. NOT per-commit.
  block_on_unresolved_comments: true   # any unresolved thread / CHANGES_REQUESTED holds it in_review
  # Review Companion engagement (yad-review-companion). Each approval records engagement: verified|none
  # — verified when reviewed through the companion (trailer/cards/chat), none for a bare click. SOFT by
  # default: both count, a bare approve still passes but draws a friendly public @-mention nudge so
  # review quality is VISIBLE without blocking anyone ("visible, not impossible"). Flip to true to make
  # only verified approvals count (the signal is gameable by design — it raises the cost of a rubber-
  # stamp, it does not prove a human read the artifact). Persisted per-project in product.json as
  # `review.requireEngagement`; companion comments carry `<!-- yad:noblock -->` so they never block.
  require_engagement: false
  # Pair Review (yad-pair-review) — the guided, two-way, TEACHING walkthrough; the AI-driven 5th
  # companion face. The AI walks the human through the change one risk-ordered stop at a time, asks
  # Socratic questions, and both sign off when satisfied; the session doubles as a learning session and
  # records the engineer's review-skill growth in the local-only yad-learn ledger. SOFT and additive —
  # it NEVER blocks a merge or gate (no strict switch); it rides the same `engagement: verified` signal
  # as the companion and surfaces genuine concerns as normal blocking comments. `mode: optional` is the
  # default posture (offered, never required). The quiz/comprehension signal reuses learning.capabilities.
  pair_review:
    enabled: true
    mode: optional              # optional | encouraged — never `required` (it can never gate)
    never_blocks: true          # invariant: a pair session is advisory; the gate predicate is untouched
    rubric: review-rubric.md    # the transferable review method (skills/yad-pair-review/references/)

# Build (Phase 3). Code repos are SEPARATE git repos (one .git each), not subfolders
# of the product repo — faithful to "per-repo specs in each code repo, contract singular in the
# product repo" (phase-3-build-plan.md, Cross-cutting). Documentation-as-config for yad-spec (Step A).
build:
  code_repos_root: "{project-root}/demo-repos"   # throwaway demo code repos for this Build
  feature_id: story_id          # specs/<story-id>/ — pinned to the permanent story ID, not Spec Kit's auto-slug
  spec_layout: speckit          # follow Spec Kit's native spec/plan/tasks layout
  speckit_ceremony: [specify, clarify, plan, analyze, checklist, tasks]   # heavy run, once per story per repo
  # Step B (yad-implement) — the light per-task loop. One atomic task = one branch = one PR/MR.
  branch_convention: "feat/<story-id>-<task-id>-<short-slug>"   # e.g. feat/EP-checkout-S01-T01-create-order
  commit_task_trailer: "Task: <story-id>-<task-id>"             # final commit trailer; anchors the spec-link check (Step C)
  contract_change_trailer: "Contract-Change: yes"              # ONLY when the locked contract surface is touched (routes back to architecture gate)
  # Commit subject + PR/MR title style (Conventional Commits — see CONTRIBUTING.md). PRs are squash-merged,
  # so the title becomes the subject; both follow one rule. Type lowercase; description lowercase + imperative
  # + no trailing period; proper nouns/acronyms keep their case.
  commit_subject_style: "<type>: <lowercase imperative description, no trailing period>"   # types: feat|fix|docs|refactor|test|perf|build|ci|chore|revert
  pr_title_style: same_as_commit_subject                       # one atomic task = one branch = one PR/MR; title defaults to the task's commit subject
  # Commit ownership + per-commit AI co-author (yad-implement installs the .gitmessage template).
  # The human git author OWNS the commit; the assisting AI is recorded per-commit as a Co-Authored-By
  # trailer, chosen by the owner from `ai_coauthor.allowed`. The AI is never the author. `none` is an
  # explicit human-only choice. Trailer order is Task: -> Contract-Change: (if any) ->
  # Ledger-Override: (only with `yad commit --manual --reason`) -> Co-Authored-By:.
  commit_owner: git_author
  ai_coauthor:
    trailer: "Co-Authored-By"
    required: false                  # the trailer is optional; owners pick `none` for human-only commits
    allowed:
      - { id: claude,     name: "Claude",             email: "noreply@anthropic.com" }
      - { id: copilot,    name: "GitHub Copilot",     email: "copilot@users.noreply.github.com" }
      - { id: cursor,     name: "Cursor",             email: "noreply@cursor.com" }
      - { id: coderabbit, name: "CodeRabbit",         email: "noreply@coderabbit.ai" }
      - { id: none,       name: "(no AI assistance)", email: "" }
  # Step C (yad-checks) — the CI gates that must pass before merge. CI-agnostic bash in checks/.
  # Phase 6 adds three thread-aware gates: lineage-check (every change links a real threaded epic),
  # epic-open (a SEALED epic — all stories shipped — refuses new behaviour, forcing a change-epic so the
  # Shape artifacts can never go stale), and reconcile-debt (a hotfix's ship-first debt blocks the next
  # change on its thread until paid). See the `change:` block below.
  gates: [spec-link, contract-check, build-test-lint, lineage-check, epic-open, reconcile-debt]
  # Pattern gates — commit subject + PR/MR title + PR/MR template-usage. Profile-aware (code|product; `hub` is the old name for product): code
  # repos validate the Conventional-Commits / task-PR conventions; the Product validates its
  # artifact-review conventions (review/EP-<slug>/<artifact>, the Product PR template). verified-commits is
  # the separate security gate (a platform-Verified signature only; no author allowlist since E62). All blocking in CI; both repos + Product.
  pattern_gates: [commit-message, pr-title, pr-template]
  ci_platforms: [github, gitlab]   # both wired from skills/yad-checks/templates/; GitHub is this product repo's platform
  contract_surface_glob: "specs/*/contracts/**"   # the repo's quoted contract slice; a diff here needs Contract-Change + a re-locked contract
  # Step D (yad-pr-template) — platform-matched PR/MR template + risk routing.
  pr_templates:
    github: ".github/pull_request_template.md"
    gitlab: ".gitlab/merge_request_templates/Default.md"
    product:                            # Shape artifact-review PR/MR on the Product (yad-pr-template repo:product; older projects: `hub:`)
      github: ".github/pull_request_template.md"
      gitlab: ".gitlab/merge_request_templates/Default.md"
  risk_levels: [low, medium, high]   # high +1 / a contract surface +2 to the approver count (advisory; yad-review-gate count)
  # Step E (yad-engineer-review) — AI review (advisory) + engineer review (the human gate) + merge.
  ai_review: coderabbit            # advisory first pass; never the authority (.coderabbit.yaml)
  # Append-only ship ledger (Build analogue of approvals.json), stored shard-then-fold: writers add one
  # shard per ship under build_log_dir; `yad tidy up` folds a SHIPPED story's shards into build_log.
  # READERS MUST UNION the two (dedupe by (story, task, repo); a shard wins) — build_log alone omits every
  # unfolded ship, e.g. a `yad checkpoint --retro-ship` backfill or any ship on an `in-build` story.
  build_log: "epics/EP-<slug>/.sdlc/build-log.json"   # folded ships
  build_log_dir: "epics/EP-<slug>/.sdlc/build-log/"   # one <story>-<task>-<repo>.json shard per ship
  story_build_states: [in-build, shipped]   # in-build = some tasks shipped; shipped = all tasks in tasks.md shipped
  # Backfill (yad-backfill) — specs for already-built features in an existing repo.
  backfill:
    tool: repomix                  # the one true CLI subprocess: `npx repomix@1.18.1` (Phase 0 / RESEARCH-NOTES §3)
    pack_flags: "--compress --include <globs> --include-logs --style markdown"   # one feature at a time; Secretlint by default
    spec_location: "specs/backfill/<feature>/spec.md"   # draft (verified: false) until a human approves
    gate_scope: touched-features   # block a change only until the features it touches have approved specs
    promote: yad-stub   # `yad-backfill promote EP-<slug>` flips a brownfield stub epic (change.stub) to a real, verified feature epic once its spec is approved

# Code context (yad-connect-repos) — the front/"brain" phases are made code-aware. Code repos are
# connected to the Product once (or any time a new repo is added), and an AI-readable picture of
# each is cached so epic/architecture/ui/stories consider what already exists in the code. The product
# repo is the Shape phase TOOLCHAIN: repomix (and Impeccable) are installed/run here and target the
# connected code repos by path — code repos need no install for this. (The Build CI gates are the
# exception: they live INSIDE each code repo and run in that repo's CI — see build.gates above.)
code_context:
  registry: "{project-root}/.sdlc/repos.json"        # project-wide repo registry (NOT per-epic)
  cache_dir: "{project-root}/.sdlc/code-context"      # per-repo pack.md + code-map.md live here
  tools: [repomix, impeccable]                        # Shape phase toolchain, installed in the Product
  pack_flags: "--compress --include-logs --style markdown"   # reuse backfill flags; Secretlint by default
  staleness: head-sha                                 # stale when a repo's HEAD != registry syncedHead
  refresh: human                                      # a stale repo is a HUMAN decision: Shape phase
                                                      # skills FLAG it and stop (pointing at
                                                      # `yad repo refresh <repo>`) — they never silently
                                                      # re-pack. `yad repo list` shows fresh/stale;
                                                      # `yad repo refresh` and `yad check --fix` re-pack.
  # `yad repo sync [<repo>]` (yad-sync-repos) switches every connected repo to its default_branch and
  # fast-forwards it from origin before work starts — working-tree only (never writes the registry);
  # a dirty repo is skipped, a diverged branch is left for manual resolution (fast-forward only).
  load_in_shape_phases: true                          # epic, architecture, ui-design, stories read the maps
  # Auth: `yad-connect-repos` clones/fetches as the LOCAL user (SSH key or git credential helper),
  # works for both github and gitlab (and self-hosted), and stores NO tokens in the registry.
  platforms: [github, gitlab]

# Design tool (yad-connect-design) — the UI design step (yad-ui) materializes the actual feature design
# (mobile screens / web pages) inside a connected design tool, alongside ui-design.md + DESIGN.md. The
# tool is reached through its MCP (a harness MCP server, like Impeccable's slash-commands — NOT a CLI),
# detected per provider and DEGRADING to markdown-only when absent. Figma-first but PLUGGABLE: a
# design-tool adapter, like the github/gitlab platform adapter. Connection is one-per-project, recorded
# at setup; the per-epic screen->frame map (design-links.json) is written by yad-ui per epic.
design:
  registry: "{project-root}/.sdlc/design.json"       # project-wide design connection (NOT per-epic)
  tools: [figma, pencil]                              # supported adapters; an unknown tool falls back to `primary`
  primary: figma                                      # the default/named provider
  degrade: markdown-only                              # no tool / no MCP => yad-ui authors ui-design.md + DESIGN.md only
  links: "{project-root}/epics/EP-<slug>/.sdlc/design-links.json"   # per-epic screen->frame map (yad-ui)
  # Auth: `yad-connect-design` connects through the LOCAL user's own authenticated MCP session and stores
  # NO tokens in the registry (project_url/files are plain references, never credentials).
  auth: user

# Testing tool (yad-connect-testing) — the test-cases step (yad-test-cases) materializes the actual
# automation tests inside a connected testing tool, alongside test-cases.md. The tool is reached through
# its MCP (a harness MCP server, like the design tool — NOT a CLI), detected per provider and DEGRADING
# to artifacts-only when absent. Playwright-first but PLUGGABLE: a testing-tool adapter, like the
# design-tool adapter. Connection is one-per-project, recorded at setup; the per-epic case->test map
# (test-links.json) is written by yad-test-cases per epic.
testing:
  registry: "{project-root}/.sdlc/testing.json"      # project-wide testing connection (NOT per-epic)
  tools: [playwright, cypress, pytest, maestro]       # supported adapters; an unknown tool falls back to `primary`
  primary: playwright                                 # the default/named provider
  degrade: artifacts-only                             # no tool / no MCP => yad-test-cases authors test-cases.md only
  links: "{project-root}/epics/EP-<slug>/.sdlc/test-links.json"   # per-epic case->test map (yad-test-cases)
  # Auth: `yad-connect-testing` connects through the LOCAL user's own authenticated MCP session and stores
  # NO tokens in the registry (project_url/suites are plain references, never credentials).
  auth: user

# Learning tool (yad-connect-learning) — the cross-cutting LEARNING LAYER. At any SDLC stage a team member
# can invoke `yad-learn` to be tutored on a concept IN THE CONTEXT of what is being built, and the request
# is recorded in a personal, LOCAL-ONLY skills log (gitignored, never committed/pushed; surfaced read-only
# by yad-status for the local learner). The tool is
# DeepTutor (https://github.com/HKUDS/DeepTutor) — an Apache-2.0 Python CLI, reached as a SUBPROCESS like
# Repomix's `npx` (it has NO MCP), NOT the MCP shape of the design/testing tools. DeepTutor-first but
# PLUGGABLE: a learning-tool adapter. Connection is one-per-project, recorded at setup; the per-epic,
# per-member learning ledger (learning-records.json) + rendered tutorials are written by yad-learn.
learning:
  registry: "{project-root}/.sdlc/learning.json"     # project-wide learning connection (NOT per-epic) — the ONLY committed learning file (no secrets/personal data)
  tools: [deeptutor]                                  # supported adapters; an unknown tool falls back to `primary`
  primary: deeptutor                                  # the default/named provider
  degrade: harness-native                             # no deeptutor CLI => yad-learn tutors via the harness model
                                                      # reading the project artifacts directly (always works, never blocks)
  records: "{project-root}/epics/EP-<slug>/.sdlc/learning-records.json"   # per-epic learning ledger (yad-learn) — LOCAL-ONLY, gitignored, never committed/pushed
  artifacts: "{project-root}/epics/EP-<slug>/learning/"   # rendered tutorial markdown (yad-learn) — LOCAL-ONLY, gitignored, never committed/pushed
  capabilities: { explain: chat, deep: deep_research, quiz: deep_question }   # yad-learn mode -> deeptutor capability
  # Auth: `yad-connect-learning` uses the LOCAL user's own deeptutor CLI config/session and stores NO
  # tokens in the registry (kb name + sources are plain references, never credentials). Opt-in, never a gate.
  auth: user

# Interactive documentation (yad-connect-docs / yad-docs / yad-docs-overview / yad-docs-sync) — the
# generated React+Vite+Tailwind doc SITES. yad-docs builds a per-epic site (epics/EP-<slug>/docs-site/)
# from the authored artifacts, themed by the connected design system; yad-docs-overview builds the
# project SDLC-overview site (docs/sdlc-site/) from the pipeline definition, superseding docs/index.html.
# The deploy TARGET is a Pages host, auto-detected from the Product's `platform` (github->github-pages,
# gitlab->gitlab-pages, null->build-only). The yad CLI does the npm build + Pages wiring + staleness;
# the content generation is the AI step. Docs are an OUTPUT ENRICHMENT — never a gate, never touch state.
docs:
  registry: "{project-root}/.sdlc/docs.json"         # project-wide docs/Pages connection (NOT per-epic)
  targets: [github-pages, gitlab-pages]               # supported Pages hosts; auto-detected from the Product's platform
  scope: product                                      # publish from: product (default; older projects: `hub`) | <repo-name> | dedicated
  degrade: build-only                                 # no Pages host / no gh|glab => build the dist, publish via CI
  login_gate: false                                   # public docs by default (the client-side gate is presentational only)
  manifest: "{project-root}/epics/EP-<slug>/.sdlc/docs-build.json"   # per-epic staleness baseline (yad-docs)
  overview: "{project-root}/docs/sdlc-site/"          # the project SDLC-overview site (yad-docs-overview)
  # Auth: `yad-connect-docs` records only how to reach the Pages host (platform + base path); it stores NO
  # tokens — publishing runs as the LOCAL user's own gh/glab or the platform CI. Idempotent, refreshable.
  auth: user

# Product platform + Shape review bridge (yad-connect-repos `detect-product`; yad-review-gate + yad-product-bridge).
# The Product is itself a git repo on a platform. With a verified ledger, the Shape review/
# comment/approval cycle runs through a real PR/MR on the Product: a review PR is opened per artifact, reviewers
# approve/comment on the platform (their own gh/glab auth — NO stored tokens), and `yad-review-gate action: sync`
# pulls that state into the file ledger (approvals.json/reviews/*.md) and runs the UNCHANGED gate predicate.
# The file ledger stays the source of truth. Degrades to the local gate when there is no platform / no CLI.
# Renamed from `hub:` (shape 3). Nothing in the engine parses this file — the skills read it — so
# there is no compatibility shim here: an older project simply still says `hub:`, and the skills name
# both. The paths below carry both names too, for the same reason the files do.
product:
  config: "{project-root}/.sdlc/product.json"          # product platform (committed, like repos.json); no roster since E62
  config_legacy: "{project-root}/.sdlc/hub.json"       # the older name: still written with identical bytes until v5; read only when product.json is absent (E122)
  pr_ledger: "{project-root}/epics/EP-<slug>/.sdlc/product-prs.json"       # per-step review-PR record (sibling of approvals.json)
  pr_ledger_legacy: "{project-root}/epics/EP-<slug>/.sdlc/hub-prs.json"    # the older name — written alongside it; read only when product-prs.json is absent, like config_legacy
  bridge: true                                         # master enable; false => local Shape gates everywhere
  platforms: [github, gitlab]                          # detected from the Product's own `git remote get-url origin`
  artifact_branch: "review/EP-<slug>/<artifact-base>"  # branch a review PR is opened on, per artifact
  # No roster since E62: yadflow stores no people. Approvals record the platform login as `approver`. A
  # `roster` in the Product settings or `domain_owner(s)` in repos.json left by an older release decides nothing (the
  # roster's name -> login pairs only let the first sync recognise older approvals).

# Phase 4 (automation) — the SECOND dial made real: the orchestrator (yad-run) reads it and acts. Since
# E34 the dial is SET, not earned. `yad dial` sets it — a Build lane step's dial in build-state, a Shape
# author step's for the whole project in `.sdlc/automation.json` — and shows the step's run record as
# advice, never as a refusal. A review gate is never auto (rule 1).
# The KILL SWITCH is not in this file any more: `yad kill --reason "<why>"` / `yad unkill` record it in
# `.sdlc/automation.json`. This file is installed and hash-managed by `yad update`, so a value edited here
# stops the file being updated — and nothing reads `kill_switch` now. `yad doctor` fails on one left `true`.
automation:
  # The only steps that MAY be automated, safest-end first (phase-4-build-plan.md build order).
  back_steps: [spec, tasks, implement, checks]
  default: human_approve            # = `advance: human`. Every lane step starts manual; `yad dial` turns one to auto

# Phase 6 (post-lock change management) — FEATURE THREADS. After the contract locks and code ships, a
# change must not MUTATE a locked artifact (that destroys the audit trail and the lock). Instead every
# change request becomes a NEW epic, threaded to its parent: a feature is a thread of linked epics
# (genesis -> change -> defect -> ...). A change-epic INHERITS unchanged Shape artifacts from its parent
# BY REFERENCE and only RE-AUTHORS what it changes — so artifacts are never stale, only superseded; the
# feature's current truth is the head of the thread, and the chain IS the evolution timeline. See
# docs/phase-6-build-plan.md and skills/yad-change. yad-defects/yad-timeline render it; yad-reconcile
# (read-only, like yad-docs-sync) flags orphan/drift; the three gates above enforce it.
change:
  # The work-item TYPE (epic.md frontmatter). Two names carry it: `kind:` is the original and is still
  # the one that is READ; `type:` is the same value under the name from shape 5 on. Write both.
  # `feature` and `chore` are the genesis types — they may stand alone with no `parent:`.
  kinds: [feature, change, defect, hotfix, chore]
  types: [feature, change, defect, hotfix, chore]     # the same list under the newer name
  genesis_types: [feature, chore]                     # may have no `parent:` (cli isGenesisType)
  # GROUPING. `theme:` on epic.md is a FREE tag — one word or short phrase shared by every epic that
  # belongs together. It is what stands in for a rung above the Epic: the ladder is
  # Product -> Epic -> Story -> Task, and grouping is a label rather than a level. There is no list of
  # allowed themes and nothing to register, it is optional, and it is ONE tag (`theme: [a, b]` reads
  # as no theme at all). It lives only in epic.md — no copy in state.json, so no file shape changed.
  # Any language. `yad doctor` reports one theme spelled two ways (theme:variants), because two
  # spellings group as two, and a `#` inside the value (theme:commented), because these frontmatter
  # readers keep the whole rest of the line and the printed `#…` is only decoration.
  grouping_frontmatter: [theme]
  # Triage DEPTH (yad-change auto-proposes, human confirms) -> which Shape steps are re-authored vs
  # inherited. defect-fix re-authors stories+test-cases only; contract-surface re-authors architecture
  # and RE-LOCKS (a new hash sends architecture-review back through review, with the contract risk step, as today).
  depths: [defect-fix, behavioral-no-surface, contract-surface, new-capability]
  thread_id: genesis_epic_id            # the thread id = the genesis epic's id (never renamed -> stablest anchor)
  # Lineage frontmatter added to epic.md (mirrors the design:/testing: enrichment blocks — the locked
  # state.json step shape is untouched). `thread` is a DERIVED cache (authoritative = walk `parent` to
  # the root); a mismatch is detectable corruption (yad doctor threadChecks). defect/hotfix also carry
  # origin/severity/escape_stage/root_cause for the quality report (yad-defects).
  lineage_frontmatter: [kind, type, thread, parent, inherits, supersedes, origin, severity, escape_stage, root_cause]
  artifact_bases: [epic, architecture, contract, ui-design, stories, test-cases]   # what `inherits` may list
  severity: [sev1, sev2, sev3, sev4]    # defect/hotfix severity
  # Per-change-epic ledgers (siblings of approvals.json; the locked state.json shape is untouched).
  ledgers:
    change: "{project-root}/epics/EP-<slug>/.sdlc/change.json"          # intake + triage (one per change/defect/hotfix epic)
    debt:   "{project-root}/epics/EP-<slug>/.sdlc/reconcile-debt.json"  # append-only hotfix ship-first debt
  # An inherited artifact is taken by REFERENCE: the change-epic's contract-lock.json is a POINTER-LOCK
  # carrying the parent's hash verbatim (+ inheritedFrom/ref), so contract-check.sh passes UNCHANGED and
  # the surface physically cannot drift (there is no contract.md in the child to edit). Omitting
  # `architecture` from `inherits` is what triggers a real re-lock + the architecture gate.
  inherit_by: reference
  # An epic is SEALED once every story is `shipped` (build.story_build_states). epic-open.sh refuses new
  # behaviour on a sealed epic -> the change must land in a new threaded change-epic (Shape is
  # forced to stay current; staleness is unshippable).
  seal_on: all-stories-shipped
  # Brownfield stub anchor (yad-stub): an already-built feature with NO epic can't be a change parent
  # (yad-change requires a real parent; lineage-check rejects a missing one). yad-stub mints the smallest
  # real thread anchor — a genesis epic.md (kind+type: feature, thread:self) marked `stub:
  # backfill-pending` / `verified: false`, with a state.json carrying the lifecycle marker `kind: stub`
  # BESIDE the work-item `type: feature` + `currentStep: backfill-pending`. A change
  # threaded off a stub inherits only what exists (boundHash: null for the undocumented surface bases) and
  # writes NO pointer-lock; change.json records `parentStub: true`. `yad-backfill promote` flips the stub
  # to verified (clears the marker), at which point contract protection begins for the thread.
  stub:
    marker: backfill-pending            # epic.md `stub:` value while the anchor is un-promoted
    state_kind: stub                    # state.json top-level `kind` + `currentStep: backfill-pending` sentinel.
                                        # A LIFECYCLE marker, not the work-item type — a stub carries both.
    allow_stub_parent: true             # a change/defect MAY thread off an un-promoted stub (default on)
    promote_by: yad-backfill            # `yad-backfill promote EP-<slug>` documents + flips the stub to real
  hotfix:
    ship_first: true                    # a hotfix may run Build BEFORE its Shape gates approve
    debt_blocks_next_change: true       # but opens reconcile-debt.json; the next change on the thread is blocked until paid
    debt_requires: [artifacts-updated, regression-test]   # evidence that clears the debt
