yadflow 3.18.1 → 4.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +355 -0
  2. package/README.md +79 -26
  3. package/bin/commands.mjs +41 -0
  4. package/bin/yad.mjs +437 -124
  5. package/cli/artifact-status.mjs +34 -15
  6. package/cli/checkpoint.mjs +69 -49
  7. package/cli/codeowners-command.mjs +170 -0
  8. package/cli/codeowners.mjs +397 -0
  9. package/cli/commit.mjs +13 -9
  10. package/cli/companion.mjs +2 -2
  11. package/cli/dial.mjs +183 -0
  12. package/cli/docs.mjs +88 -32
  13. package/cli/doctor.mjs +1472 -97
  14. package/cli/epic-state.mjs +3478 -232
  15. package/cli/epic.mjs +506 -0
  16. package/cli/errors.mjs +4 -1
  17. package/cli/gate.mjs +1002 -209
  18. package/cli/history.mjs +556 -0
  19. package/cli/hook.mjs +266 -55
  20. package/cli/hubcommit.mjs +6 -17
  21. package/cli/index-command.mjs +87 -0
  22. package/cli/ledger.mjs +57 -7
  23. package/cli/lib.mjs +184 -18
  24. package/cli/manifest.mjs +367 -56
  25. package/cli/migrate.mjs +726 -53
  26. package/cli/mode.mjs +170 -0
  27. package/cli/next.mjs +349 -90
  28. package/cli/openpr.mjs +191 -39
  29. package/cli/people.mjs +654 -0
  30. package/cli/plan.mjs +417 -132
  31. package/cli/platform.mjs +110 -129
  32. package/cli/product-index.mjs +287 -0
  33. package/cli/protection.mjs +706 -0
  34. package/cli/reconcile.mjs +38 -12
  35. package/cli/repo-publish.mjs +24 -26
  36. package/cli/repo.mjs +23 -14
  37. package/cli/report.mjs +21 -15
  38. package/cli/review.mjs +24 -27
  39. package/cli/riskmap-command.mjs +289 -0
  40. package/cli/riskmap.mjs +373 -0
  41. package/cli/setup.mjs +139 -287
  42. package/cli/ship.mjs +7 -6
  43. package/cli/skill.mjs +180 -0
  44. package/cli/skip.mjs +211 -30
  45. package/cli/thread.mjs +42 -17
  46. package/cli/tidy.mjs +20 -20
  47. package/cli/update-commit.mjs +22 -22
  48. package/cli/usage.mjs +115 -109
  49. package/package.json +3 -3
  50. package/skills/sdlc/config.yaml +166 -87
  51. package/skills/sdlc/module-help.csv +35 -35
  52. package/skills/yad-analysis/SKILL.md +125 -65
  53. package/skills/yad-architecture/SKILL.md +34 -23
  54. package/skills/yad-architecture/references/contract-format.md +10 -8
  55. package/skills/yad-backfill/SKILL.md +14 -8
  56. package/skills/yad-backfill/references/backfill.md +1 -1
  57. package/skills/yad-change/SKILL.md +127 -52
  58. package/skills/yad-change/references/triage.md +42 -28
  59. package/skills/yad-checks/SKILL.md +89 -45
  60. package/skills/yad-checks/references/check-gates.md +315 -92
  61. package/skills/yad-checks/templates/checks/build-test-lint.sh +25 -7
  62. package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
  63. package/skills/yad-checks/templates/checks/contract-check.sh +58 -2
  64. package/skills/yad-checks/templates/checks/epic-open.sh +3 -3
  65. package/skills/yad-checks/templates/checks/install-deps.sh +46 -0
  66. package/skills/yad-checks/templates/checks/ledger-guard.sh +94 -18
  67. package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
  68. package/skills/yad-checks/templates/checks/package-manager.sh +140 -0
  69. package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +4 -4
  70. package/skills/yad-checks/templates/checks/risk-map-check.sh +438 -0
  71. package/skills/yad-checks/templates/checks/verified-commits.sh +20 -46
  72. package/skills/yad-checks/templates/github/yad-checks.yml +37 -5
  73. package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
  74. package/skills/yad-checks/templates/github/yad-update-guard.yml +3 -4
  75. package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
  76. package/skills/yad-checks/templates/gitlab/.gitlab-ci.yml +7 -1
  77. package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +22 -4
  78. package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +5 -5
  79. package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +4 -4
  80. package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
  81. package/skills/yad-checks/templates/hooks/ledger-guard.sh +38 -7
  82. package/skills/yad-commit/SKILL.md +6 -6
  83. package/skills/yad-connect-design/SKILL.md +6 -6
  84. package/skills/yad-connect-design/references/design-context.md +1 -1
  85. package/skills/yad-connect-design/references/design-registry.md +2 -2
  86. package/skills/yad-connect-docs/SKILL.md +12 -12
  87. package/skills/yad-connect-docs/references/docs-registry.md +1 -1
  88. package/skills/yad-connect-learning/SKILL.md +5 -5
  89. package/skills/yad-connect-learning/references/learning-registry.md +2 -2
  90. package/skills/yad-connect-repos/SKILL.md +92 -54
  91. package/skills/yad-connect-repos/references/code-context.md +6 -6
  92. package/skills/yad-connect-repos/references/hub-config.md +68 -58
  93. package/skills/yad-connect-repos/references/repos-registry.md +10 -9
  94. package/skills/yad-connect-repos/references/risk-map.md +81 -0
  95. package/skills/yad-connect-testing/SKILL.md +6 -6
  96. package/skills/yad-connect-testing/references/testing-context.md +3 -4
  97. package/skills/yad-connect-testing/references/testing-registry.md +2 -2
  98. package/skills/yad-defects/SKILL.md +8 -8
  99. package/skills/yad-discovery/SKILL.md +130 -94
  100. package/skills/yad-discovery/references/discovery-schema.md +23 -7
  101. package/skills/yad-discovery/references/foundation-schema.md +374 -0
  102. package/skills/yad-docs/SKILL.md +16 -11
  103. package/skills/yad-docs/references/data-mapping.md +9 -7
  104. package/skills/yad-docs/templates/app/package-lock.json +3 -3
  105. package/skills/yad-docs-overview/SKILL.md +32 -17
  106. package/skills/yad-docs-overview/references/pipeline-model.md +47 -28
  107. package/skills/yad-docs-sync/SKILL.md +10 -5
  108. package/skills/yad-docs-sync/references/staleness.md +8 -7
  109. package/skills/yad-engineer-review/SKILL.md +88 -24
  110. package/skills/yad-engineer-review/references/ship-and-record.md +25 -16
  111. package/skills/yad-epic/SKILL.md +178 -100
  112. package/skills/yad-epic/references/state-schema.md +626 -117
  113. package/skills/yad-hub-bridge/SKILL.md +66 -48
  114. package/skills/yad-hub-bridge/references/bridge.md +110 -83
  115. package/skills/yad-hub-bridge/references/login-roster.md +163 -70
  116. package/skills/yad-hub-bridge/templates/checks/hub-route.sh +22 -19
  117. package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +34 -14
  118. package/skills/yad-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +2 -2
  119. package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +22 -12
  120. package/skills/yad-implement/SKILL.md +29 -15
  121. package/skills/yad-implement/references/implement-conventions.md +2 -2
  122. package/skills/yad-learn/SKILL.md +9 -9
  123. package/skills/yad-learn/references/learning-state.md +2 -2
  124. package/skills/yad-open-pr/SKILL.md +64 -29
  125. package/skills/yad-pair-review/SKILL.md +18 -16
  126. package/skills/yad-pair-review/references/session-state.md +4 -4
  127. package/skills/yad-pr-template/SKILL.md +48 -27
  128. package/skills/yad-pr-template/references/risk-routing.md +97 -24
  129. package/skills/yad-pr-template/templates/checks/pr-template.sh +37 -15
  130. package/skills/yad-pr-template/templates/checks/pr-title.sh +27 -13
  131. package/skills/yad-pr-template/templates/checks/risk-route.sh +107 -14
  132. package/skills/yad-pr-template/templates/github/pull_request_template.md +7 -5
  133. package/skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md +7 -5
  134. package/skills/yad-pr-template/templates/hub/github/pull_request_template.md +15 -14
  135. package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +15 -13
  136. package/skills/yad-reconcile/SKILL.md +3 -3
  137. package/skills/yad-report/SKILL.md +5 -5
  138. package/skills/yad-review-companion/SKILL.md +12 -9
  139. package/skills/yad-review-gate/SKILL.md +198 -79
  140. package/skills/yad-review-gate/references/gating.md +230 -54
  141. package/skills/yad-run/SKILL.md +86 -56
  142. package/skills/yad-run/references/run-loop.md +67 -45
  143. package/skills/yad-ship/SKILL.md +18 -14
  144. package/skills/yad-spec/SKILL.md +31 -17
  145. package/skills/yad-spec/references/spec-handoff.md +17 -5
  146. package/skills/yad-status/SKILL.md +114 -56
  147. package/skills/yad-stories/SKILL.md +42 -27
  148. package/skills/yad-stories/references/story-schema.md +10 -9
  149. package/skills/yad-stub/SKILL.md +59 -48
  150. package/skills/yad-sync-repos/SKILL.md +3 -3
  151. package/skills/yad-test-cases/SKILL.md +37 -30
  152. package/skills/yad-test-cases/references/test-cases-schema.md +8 -5
  153. package/skills/yad-timeline/SKILL.md +8 -7
  154. package/skills/yad-ui/SKILL.md +46 -25
  155. package/cli/roster.mjs +0 -164
  156. package/skills/sdlc/install.sh +0 -68
@@ -1,87 +1,96 @@
1
- # Hub config — schema, detection, and the reviewer roster
1
+ # Product config — schema, detection, and who is recorded
2
2
 
3
- The hub config is the product hub's record of **its own** platform (so the front-half review/comment/
4
- approval cycle can run through a real PR/MR on the hub) and the **reviewer roster** that maps a platform
5
- login to an SDLC name + role. It is a single object for the hub itself — the sibling of the per-repo
3
+ The Product config is the Product's record of **its own** platform (so the Shape review/comment/
4
+ approval cycle can run through a real PR/MR on the Product). It holds **no list of people** — no roster,
5
+ no roles, no commit emails (E62). It is a single object for the Product itself — the sibling of the per-repo
6
6
  `repos.json` registry (see `repos-registry.md`), kept separate so it never pollutes that array.
7
7
 
8
8
  ## Location
9
9
 
10
10
  `{project-root}/.sdlc/hub.json`
11
11
 
12
- (`config.yaml` `hub.config`.) Created/updated by `yad-connect-repos action: detect-hub`.
12
+ (`config.yaml` `product.config` (older projects: `hub.config`).) Created/updated by `yad-connect-repos action: detect-hub`.
13
13
 
14
14
  ## Schema
15
15
 
16
16
  ```json
17
17
  {
18
- "platform": "github", // github | gitlab (from the hub's own remote host); null when local-only
18
+ "schemaVersion": 10, // the file's shape. Absent means 1 (rule 1). `yad migrate` moves it; see docs/migrations/shape-2.md
19
+ "platform": "github", // github | gitlab (from the Product's own remote host); null when local-only
19
20
  "git_url": "https://github.com/abdelrahmannasr/yadflow.git", // REQUIRED when platform is non-null (scopes auth + opens PRs); yad doctor warns YAD-CFG-005 if absent
20
21
  "default_branch": "main",
21
- "bridge_enabled": true, // open review PRs/MRs on the hub for front-half reviews; travels WITH platform — bridge mode is both (isBridge), so never true beside platform: null (#186)
22
- "gate_sync_version": "3.15.3", // OPTIONAL exact pin for the wired gate-sync job; an exact 3.x.y, prereleases included (3.16.0-rc.1) — anything else is skipped. Omitted => the .sdlc/cli-version.json stamp if that qualifies, else floating 3
22
+ "ledger": "verified", // WHO WRITES THE LEDGER, and the one that decides: "verified" = CI only, signed; "local" = this machine. Travels WITH platform — verified is both (isVerifiedLedger), so never "verified" beside platform: null (#186)
23
+ "bridge_enabled": true, // the older spelling of the same switch, kept so a check gate that predates `yad update` still reads it. Write it to MATCH `ledger`, never against it
24
+ "bridge": true, // older still. Same rule
25
+ "gate_sync_version": "4.0.0", // OPTIONAL exact pin for the wired gate-sync job; an exact release of the wired fragment's major (4.x.y in this release), prereleases included (4.1.0-rc.1) — anything else, a 3.x pin included, is skipped. Omitted => the .sdlc/cli-version.json stamp if that qualifies, else floating on that major
23
26
  "review": { "requireEngagement": false }, // Review Companion: false (soft) counts bare approves but nudges; true counts only verified-engagement approvals
24
- "detectedAt": "2026-06-08", // last detect-hub run (YYYY-MM-DD)
25
- "roster": [
26
- { "login": "abdelrahmannasr", "name": "alice", "email": "alice@example.com",
27
- "roles": { "hub": ["owner", "reviewer"] } },
28
- { "login": "carol-gh", "name": "carol", "email": "carol@example.com",
29
- "roles": { "hub": ["reviewer"], "backend": ["domain-owner", "owner"], "payments": ["reviewer"] } }
30
- ]
27
+ "solo": false, // SOLO MODE, and the one that decides: true waives the approval requirement on every review gate (the merge + resolved threads still gate). Set by `yad setup --solo` / `--team <n>` or `yad mode`
28
+ "mode": "team", // the roadmap's name for the same switch (E10): "solo" | "team", written beside `solo` by `yad mode` and `yad setup`. NOT read by the gates this major — `yad doctor` warns `mode:disagree` when it contradicts `solo`. Not the ledger switch: "verified mode" elsewhere means `ledger`
29
+ "mode_set": { "from": "solo", "to": "team", "by": "al", "date": "2026-09-15", "reason": null }, // the last change of mode (E10): who, when, why. `yad mode solo` requires the reason
30
+ "detectedAt": "2026-06-08" // last detect-hub run (YYYY-MM-DD)
31
31
  }
32
32
  ```
33
33
 
34
- ## The roster — login → name → per-scope roles
35
-
36
- The roster is how a platform identity (a GitHub/GitLab **login**) becomes an SDLC **name + role(s)** in
37
- the file ledger (`approvals.json` / `comments.json`). Roles are the same three the gate uses:
38
- `owner | reviewer | domain-owner`. Populate and edit it any time with the **`yad roster`** CLI command
39
- (`list` / `add` / `grant` / `revoke` / `remove`) — `add` walks the connected repos asking for each
40
- one's role, and a `domain-owner` grant keeps `repos.json` `domain_owners` in sync.
41
-
42
- - **`login`** — the platform username whose PR review / approval is being mapped.
43
- - **`name`** — the SDLC name written into the ledger (the same names used across `approvals.json`,
44
- `comments.json`, and `epic.md` `owner`). Keep it stable.
45
- - **`email`** — the commit email; drives the **committer → login** reverse lookup that auto-assigns PRs.
46
- - **`roles`** — a **per-scope map**: scope (`hub`, or a connected repo name) → the roles held there. A
47
- person can be **owner + reviewer + domain-owner at once** and across scopes; a repo gets **several**
48
- people per role by appearing in several entries' maps. Validated against the hub during `yad setup` /
49
- `yad doctor`; a login that does not resolve is flagged `unverified` (warn-only, never blocks).
50
-
51
- **Back-compat:** readers also accept a flat array `"roles": ["owner","reviewer"]` (treated as `hub`
52
- roles) and the legacy single `"role": "owner"` (a `hub` role).
53
-
54
- **`domain-owner` may also be DERIVED from `repos.json`.** A roster entry whose `name` equals a repo's
55
- `domain_owner`/`domain_owners` in `repos.json` is treated as that repo's domain-owner **when that repo is
56
- a touched domain for the step under review** — kept as a fallback so pre per-scope projects still resolve.
57
- New setups write the grant directly into the person's `roles[<repo>]` map.
58
-
59
- - **Unmapped login fallback.** A login absent from the roster maps to `name: <login>`, `role: reviewer`,
60
- and is flagged `<!-- unverified login: <login> -->` in the review record (mirrors the code-map
61
- `unverified` convention). An unmapped login is **never** auto-promoted to `owner` or `domain-owner` —
62
- it stays a plain reviewer until a human adds it to the roster, so a stranger cannot satisfy the
63
- owner/domain-owner requirement.
34
+ ## People — no roster (E62)
35
+
36
+ yadflow keeps **no stored list of people**. A list is a claim that goes stale; the platform already knows
37
+ who is logged in and who has access.
38
+
39
+ - **Who a record names.** A record's `by` is the platform login that `gh api user` / `glab api user`
40
+ reports, else git `user.name` (no platform, CLI missing or logged out, no network).
41
+ `YAD_PLATFORM_LOGIN=0` turns the lookup off.
42
+ - **Approvals.** Each approval records the platform login that gave it as `approver`, with no `role` and
43
+ no `domain`. A team gate needs at least **1 approval from someone other than the author**, plus
44
+ resolved threads and a merged PR. The full count is `base 1 + risk step` (`contract` +2, `auth` /
45
+ `payments` +1, the largest and never the sum); the gate caps it at the active people less one and
46
+ reports that (E72); the risk step is advisory. Solo
47
+ mode still waives the approval.
48
+ - **Reviewers.** Nothing requests reviewers on a PR/MR. Ask them on the PR/MR itself. `yad open-pr`
49
+ prints a suggestion for a code-repo PR (E68) from recent history and CODEOWNERS — a hint only.
50
+
51
+ **Legacy data an older release wrote.** A `roster` array (with `login`/`name`/`email`/`roles`, or the
52
+ older `role`) and a `verified_authors` list may still sit in this file. Nothing adds them, and nothing
53
+ deletes them (`yad migrate`'s shape-3 step still adds a `product` role key beside `hub` in an old roster, so
54
+ an older project upgrades the same way it always did). `verified_authors` decides nothing — only `yad doctor`
55
+ reads it, to warn. The roster is read for one thing only: its `name` →
56
+ `login` pairs let a gate write record the login on older approval and comment records
57
+ (`../../yad-hub-bridge/references/login-roster.md` → "Recording the login on older records"). `yad doctor`
58
+ warns `people:roster-unused` and `people:verified-authors-unused` so nobody edits them believing they decide
59
+ something. Delete `verified_authors` when convenient; delete `roster` when `people:roster-unused` says it
60
+ can go — no older record needs it any more, or only records it cannot place are left and their reviews are
61
+ closed.
64
62
 
65
63
  ## Detection
66
64
 
67
65
  `detect-hub` reuses the same host-detection logic this skill already applies to code repos:
68
- run `git remote get-url origin` **on the hub itself** and read the host —
66
+ run `git remote get-url origin` **on the Product itself** and read the host —
69
67
  `github.com` → `github`, `gitlab.com`/self-hosted GitLab → `gitlab`, no remote → `platform: null`.
70
68
  Auth is the **local user's own** `gh`/`glab`/git credentials; **no tokens are ever stored** (same rule
71
69
  as the registry). `detect-hub` upserts `hub.json` in place — it is idempotent and safe to re-run.
72
70
 
73
71
  **`git_url` is required whenever `platform` is non-null.** `yad doctor` uses it to scope the auth
74
- probe to the hub's own host (an unscoped `glab auth status` fails on any unrelated broken instance),
75
- and the bridge/PR flow uses it to open PRs. Doctor flags its absence with a warn (`YAD-CFG-005`);
72
+ probe to the Product's own host (an unscoped `glab auth status` fails on any unrelated broken instance),
73
+ and the verified ledger/PR flow uses it to open PRs. Doctor flags its absence with a warn (`YAD-CFG-005`);
76
74
  re-running `yad setup` backfills it from the origin remote (idempotent, non-interactive).
77
75
 
78
- ## Bridge enable / degradation
76
+ ## Who writes the ledger, and what happens when it degrades
79
77
 
80
- - `bridge_enabled: true` **and** a non-null `platform` **and** `gh`/`glab` authenticated → the front-half
81
- review opens a PR/MR on the hub and `yad-review-gate action: sync` pulls platform state into the ledger.
82
- - `bridge_enabled: false`, `platform: null`, or no/unauthenticated CLI → the gate falls back to the
83
- existing **file-only** flow with no error. The file ledger is the source of truth in both modes.
84
- - The master switch `config.yaml` `hub.bridge: false` disables the bridge globally regardless of `hub.json`.
78
+ The switch is read in this order — `isVerifiedLedger` (`cli/manifest.mjs`) and the bash copy in
79
+ `checks/ledger-guard.sh` both do exactly this, and a test asserts they agree on every shape:
80
+
81
+ 1. **`ledger`**, whenever the key is present. `"verified"` and nothing else means verified; any other
82
+ value, including an empty string, means local.
83
+ 2. **otherwise** the older booleans `bridge_enabled`, then `bridge`.
84
+ 3. and a non-null `platform` is required either way — without one there is no Verified badge to read.
85
+
86
+ So on a migrated project `ledger` wins, and writing a boolean that contradicts it changes nothing.
87
+ Keep all three in step.
88
+
89
+ - `ledger: "verified"` **and** a non-null `platform` **and** `gh`/`glab` authenticated → the Shape
90
+ review opens a PR/MR on the Product and `yad-review-gate action: sync` pulls platform state into the ledger.
91
+ - `ledger: "local"`, `platform: null`, or no/unauthenticated CLI → the gate falls back to the
92
+ existing **local** flow with no error. The file ledger is the source of truth in both modes.
93
+ - The master switch `config.yaml` `product.bridge: false` (older projects: `hub.bridge`) disables the verified ledger globally regardless of `hub.json`.
85
94
 
86
95
  ## Review Companion engagement (`review.requireEngagement`)
87
96
 
@@ -90,15 +99,16 @@ engagement gate. Each approval records `engagement: verified | none`. **Soft (`f
90
99
  bare approve still passes but draws a friendly public nudge, so review *quality* is visible without
91
100
  blocking. **Strict (`true`):** the predicate counts only `verified` approvals. The signal is gameable by
92
101
  design ("visible, not impossible") — it raises the cost of a rubber-stamp, it does not prove a human
93
- read the artifact. Applies to both the front gate and the back-half engineer review.
102
+ read the artifact. Applies to both the Shape gate and the Build engineer review.
94
103
 
95
104
  ## Git tracking
96
105
 
97
- Commit `hub.json` — it is small, reviewable, and carries no secrets (logins and names only, never tokens).
106
+ Commit `hub.json` — it is small, reviewable, and carries no secrets or tokens. The only people data in it is the login or git name in `mode_set.by`, plus
107
+ whatever an older `roster` still lists.
98
108
  This mirrors how `repos.json` and the per-epic `.sdlc/` state are committed.
99
109
 
100
110
  ## Greenfield
101
111
 
102
- A brand-new hub has no `hub.json`. That is valid — the front-half gate runs file-only until `detect-hub`
103
- records a platform. The bridge is purely additive; nothing about authoring or the gate predicate changes.
112
+ A brand-new Product has no `hub.json`. That is valid — the Shape gate runs local until `detect-hub`
113
+ records a platform. The verified ledger is purely additive; nothing about authoring or the gate predicate changes.
104
114
  ```
@@ -1,6 +1,6 @@
1
1
  # Repos registry — schema + freshness rule
2
2
 
3
- The registry is the product hub's record of which code repos are connected and where their cached
3
+ The registry is the Product's record of which code repos are connected and where their cached
4
4
  code-context lives. It is **project-wide** (shared across every epic), so it lives at the product root,
5
5
  not under any `epics/EP-<slug>/.sdlc/`.
6
6
 
@@ -21,8 +21,6 @@ not under any `epics/EP-<slug>/.sdlc/`.
21
21
  "path": "demo-repos/backend", // path to the code repo, rel. to {project-root} (or absolute)
22
22
  "git_url": "git@github.com:org/backend.git", // optional remote; SSH or HTTPS; GitHub or GitLab; null if local-only
23
23
  "platform": "github", // github | gitlab (from the URL host); null when local-only
24
- "domain_owners": ["carol", "dave"], // engineers who own this repo's domain (review routing); a repo may have several
25
- "domain_owner": "carol", // legacy single-owner mirror = domain_owners[0] (kept for back-compat readers)
26
24
  "default_branch": "main",
27
25
  "connectedAt": "2026-06-08", // first connect (YYYY-MM-DD)
28
26
  "lastSyncedAt": "2026-06-08", // last connect/refresh
@@ -37,7 +35,10 @@ not under any `epics/EP-<slug>/.sdlc/`.
37
35
 
38
36
  ## Rules
39
37
 
40
- - **`name`** is the join key. It MUST match the names used in epic/story `repos:` tags so the front
38
+ - **No owners.** An entry names no people (E62). `connect` writes no `domain_owner`/`domain_owners`. An
39
+ entry an older release wrote with them keeps them; nothing reads them, nothing deletes them, and
40
+ `yad doctor` warns `people:domain-owners-unused`.
41
+ - **`name`** is the join key. It MUST match the names used in epic/story `repos:` tags so the Shape
41
42
  phases can map `epic.repos` → registry entries → code-maps. Keep it stable.
42
43
  - **Auth is never stored.** No tokens, passwords, or PATs in the registry. `git_url` is a plain remote;
43
44
  `connect` clones/fetches as the local user (SSH key or git credential helper).
@@ -51,15 +52,15 @@ not under any `epics/EP-<slug>/.sdlc/`.
51
52
  ## Git tracking
52
53
 
53
54
  Commit the **registry** (`repos.json`) and each repo's **`code-map.md`** — they are small, reviewable,
54
- and are what the front phases actually read (a diff on a code-map shows when a repo's surface moved).
55
- `yad repo refresh --push` commits and pushes exactly these (never `pack.md`) to the hub's default
55
+ and are what the Shape phases actually read (a diff on a code-map shows when a repo's surface moved).
56
+ `yad repo refresh --push` commits and pushes exactly these (never `pack.md`) to the Product's default
56
57
  branch as one `chore(hub): sync code-context … [skip ci]` audit commit.
57
- **Ignore** the full Repomix `pack.md` — it is large and regenerable (`action: refresh`). The product
58
- hub's `.gitignore` carries `.sdlc/code-context/*/pack.md` for this. This mirrors how the per-epic
58
+ **Ignore** the full Repomix `pack.md` — it is large and regenerable (`action: refresh`). The Product
59
+ 's `.gitignore` carries `.sdlc/code-context/*/pack.md` for this. This mirrors how the per-epic
59
60
  `.sdlc/` state (state.json, approvals.json, build-log.json) is committed.
60
61
 
61
62
  ## Greenfield
62
63
 
63
- A brand-new product hub has no `repos.json` (or an empty `{ "repos": [] }`). That is valid — the front
64
+ A brand-new Product has no `repos.json` (or an empty `{ "repos": [] }`). That is valid — the Shape
64
65
  phases treat "no repos connected" as "nothing to consider yet" and proceed unchanged. The registry
65
66
  appears the first time `connect` runs.
@@ -0,0 +1,81 @@
1
+ # Risk map — format, the classification rubric, and the rules for the agent
2
+
3
+ A code repo's **risk map** is one file, `.sdlc/risk-map`, **in the code repo** (not the Product). It
4
+ gives each directory a risk level. It holds **no names** — no people, no roles, no teams, no `@login`.
5
+ It is the team's file: `yad update` never installs, owns or overwrites it, and it changes only through
6
+ that repo's PRs (E65).
7
+
8
+ **What the levels do (E66).** A change touching a `high` directory asks for one more approver. The count
9
+ reads the map on the **base branch**, so a PR cannot lower its own count, and a `guessed` level counts
10
+ exactly as a `confirmed` one. That is why a guess matters: a `high` guess adds a reviewer to every change
11
+ under that directory until a person confirms or changes it. `medium` and `low` add nothing. The count is
12
+ reported, not enforced: branch protection holds the merge, and `yad open-pr` shows the count capped by the
13
+ active people (E72). A `high` directory also asks for an approval from someone who has committed there in the last 30 days
14
+ (E67), named live from the base branch's history — so a guess here decides who can review, too.
15
+
16
+ ## Format
17
+
18
+ ```
19
+ # yad-risk-map v1
20
+ src/payments/ high confirmed # charge.js calls the card processor
21
+ src/catalog/ low guessed # read-only product listing
22
+ docs/ unset
23
+ ./ low confirmed # the files at the repo root
24
+ ```
25
+
26
+ | Rule | Meaning |
27
+ |---|---|
28
+ | Header | The first line is `# yad-risk-map v1`. |
29
+ | One line per directory | `<dir>/ <level> <guessed\|confirmed>`, fields split on spaces or tabs, so a path holds none. |
30
+ | Directory | Written from the repo root, ending in `/`. No leading `/` or `#`, no `.` or `..` segment, no space, tab, `*`, `?`, `[` or `\`. A directory whose name holds one of those (`app/[slug]/`) cannot have a line of its own: its parent's line covers it. At the top level (`my docs/`) nothing can, and the check says to rename it. |
31
+ | `./` | The files **at** the repo root only — not everything below it. |
32
+ | Which line decides | The deepest listed directory above a file. |
33
+ | Levels | `high`, `medium`, `low`, or `unset` (listed, not classified; its state may be left out). |
34
+ | States | `guessed` (an AI agent filled it in) or `confirmed` (a person checked it). |
35
+ | Comment | Everything from the first `#` that follows a space or tab. |
36
+ | Names | A word starting `@` (`@alice`, `@org/team`) is a name and is warned about. A word ending in `/` (`@types/`) is a directory. |
37
+ | No `contract` | The contract surface keeps its own lock, `contract-check` and `Contract-Change` trailer. |
38
+
39
+ The rules are code: `cli/riskmap.mjs`, with a bash twin in `checks/risk-map-check.sh`.
40
+
41
+ ## Rubric — read the CODE, not the folder name (one exception, below)
42
+
43
+ Decide from what the code in the directory **does**: the calls it makes, the data it writes, the
44
+ libraries it imports. A folder called `utils/` can move money; a folder called `payments/` can hold only
45
+ display text. Use the pack and the code-map first, and open the files themselves when they do not say.
46
+
47
+ | Level | The code in the directory… |
48
+ |---|---|
49
+ | `high` | moves money or bills anyone; handles login, sessions, tokens, permissions, secrets or keys; deletes stored data or changes its shape (migrations); reads or writes personal data (identity, contact, health, payment details); builds, deploys or releases (CI workflows, deploy scripts, infrastructure); and `.sdlc/` — see the exception below the table |
50
+ | `medium` | writes to a database, a queue or an outside service in ordinary ways; serves a public API or event others depend on; is a shared library most of the code imports; sets build or dependency configuration |
51
+ | `low` | only reads and shows data; styling and UI text; documentation; tests, fixtures and examples; developer tooling that never ships |
52
+
53
+ - **When unsure between two levels, choose the higher one** and say what you could not tell in the reason.
54
+ - **Split a directory when its parts differ.** If `src/` holds `payments/` (`high`) and `catalog/` (`low`),
55
+ write a line for each part, and keep a line for `src/` itself for the files directly in it.
56
+ - **The one exception: `.sdlc/` is `high`**, judged by what it HOLDS — this map, which decides how much
57
+ review every later change needs — not by what its code does (the user's decision, 2026-09-17). A
58
+ `yad update` PR touches that folder too, so those PRs ask for the extra approver as well; accepted,
59
+ because they rewrite the gate scripts the repo runs.
60
+ - **The reason is one line, taken from the code**: a file and what it does (`charge.js calls the card
61
+ processor`). Never a name, a secret, a customer value or an address.
62
+
63
+ ## What the agent may and may not change
64
+
65
+ | Line | The agent may… | The agent may not… |
66
+ |---|---|---|
67
+ | `unset` | give it a level, a reason and `guessed` | mark it `confirmed` |
68
+ | `guessed` | change its level or reason, keeping `guessed` | mark it `confirmed` |
69
+ | `confirmed` | nothing — print a **suggestion** when the code now says otherwise | edit, re-level or delete it |
70
+ | any | add a deeper line when a directory's parts differ | delete a line, or add a name |
71
+
72
+ Only a person turns `guessed` into `confirmed`, in a PR in the code repo. The commit records who.
73
+
74
+ ## Staying true
75
+
76
+ | Where | What it says |
77
+ |---|---|
78
+ | `checks/risk-map-check.sh` on every PR (both CI templates) | a file this change adds or edits that no line covers (names the directory to add); a line whose directory holds no file; a touched line still `unset` or `guessed`; a line it cannot read or that names a person; a change that edits the map itself. **Warnings only** — it never fails the build |
79
+ | `yad risk-map check [repo]` | the same, for the whole repo |
80
+ | `yad doctor`, section `risk-map` | one line per connected repo on disk |
81
+ | `yad repo refresh` + this skill | a new directory is drafted `unset` and classified; a `confirmed` line the code now contradicts is printed as a suggestion |
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  name: yad-connect-testing
3
- description: 'Connects a testing tool (Playwright, or another tool — pluggable) to the product hub so the test-cases step can implement the actual automation tests, not just Markdown test cases. Registers the tool into the project-wide .sdlc/testing.json (local-user / MCP-session auth, no stored tokens), detecting whether a testing-tool MCP is available and degrading to artifacts-only when it is not. Run at setup or any time the testing tool changes. Reusable, idempotent, refreshable. Use when the user says "connect Playwright", "connect a testing tool", "refresh the testing connection", or "list the testing connection".'
3
+ description: 'Connects a testing tool (Playwright, or another tool — pluggable) to the Product so the test-cases step can implement the actual automation tests, not just Markdown test cases. Registers the tool into the project-wide .sdlc/testing.json (local-user / MCP-session auth, no stored tokens), detecting whether a testing-tool MCP is available and degrading to artifacts-only when it is not. Run at setup or any time the testing tool changes. Reusable, idempotent, refreshable. Use when the user says "connect Playwright", "connect a testing tool", "refresh the testing connection", or "list the testing connection".'
4
4
  ---
5
5
 
6
6
  # SDLC — Connect a Testing Tool (make the test-cases step automation-aware)
7
7
 
8
8
  **Goal:** Let the test-cases step (`yad-test-cases`) produce the **actual automation tests** — the
9
9
  runnable specs in a connected code repo — alongside the Markdown artifact (`test-cases.md`). This skill
10
- **connects** a testing tool such as **Playwright** to the product hub and records *how* to reach it (the
10
+ **connects** a testing tool such as **Playwright** to the Product and records *how* to reach it (the
11
11
  tool, the suite references, which MCP runs it) — never a credential.
12
12
 
13
- This is **setup/maintenance**, not a gated front state — it never touches `.sdlc/state.json` or any
13
+ This is **setup/maintenance**, not a gated Shape step — it never touches `.sdlc/state.json` or any
14
14
  epic's approvals. It only writes the project-wide testing registry. `yad-test-cases` consumes it: when a
15
15
  tool is connected and its MCP is available, the `test architect` lens **generates** automation tests
16
16
  into the connected repo(s) (or **links** an existing suite and reads it back); when nothing is
@@ -18,7 +18,7 @@ connected, `yad-test-cases` runs artifacts-only exactly as before.
18
18
 
19
19
  ## Conventions
20
20
 
21
- - `{project-root}` resolves from the project working directory (the **product hub**).
21
+ - `{project-root}` resolves from the project working directory (the **Product**).
22
22
  - The integration is **Playwright-first but pluggable** (`config.yaml` `testing.tools`): a testing-tool
23
23
  *adapter*, like the GitHub/GitLab platform adapter or the design-tool adapter. Playwright is the
24
24
  primary provider; `cypress`, `pytest` and `maestro` are second providers; `none` → artifacts-only.
@@ -29,7 +29,7 @@ connected, `yad-test-cases` runs artifacts-only exactly as before.
29
29
  the sibling of `.sdlc/repos.json`, `.sdlc/hub.json`, and `.sdlc/design.json`.
30
30
  - Per-epic test→suite links are written later by `yad-test-cases`
31
31
  (`epics/EP-<slug>/.sdlc/test-links.json`), not here.
32
- - Speak in the configured `communication_language`; write documents in `document_output_language`.
32
+ - Speak in the `communication_language` set in `{project-root}/.sdlc/config.yaml`; write documents in `document_output_language`.
33
33
 
34
34
  ## Inputs
35
35
 
@@ -101,7 +101,7 @@ automation tests here**. Nothing auto-advances; this is setup.
101
101
  **available/unavailable** flag for the MCP (best-effort, the user's own session). No testing tool
102
102
  connected ⇒ "artifacts-only".
103
103
  - **`disconnect`** — remove the registry file (or set `tool: "none"`). The testing tool's own
104
- project/suites are **never touched** — only the hub's record of them.
104
+ project/suites are **never touched** — only the Product's record of them.
105
105
 
106
106
  ## Hard rules
107
107
 
@@ -27,8 +27,7 @@ produce.
27
27
 
28
28
  ## Generate (write automation tests into the repo)
29
29
 
30
- When the connected provider is write-capable, the `test architect` lens (Murat, `bmad-tea` +
31
- `bmad-testarch-automate`) produces the epic's automation tests in the connected code repo(s), covering
30
+ When the connected provider is write-capable, the `test architect` lens produces the epic's automation tests in the connected code repo(s), covering
32
31
  the cases `test-cases.md` enumerates and the acceptance criteria the stories define:
33
32
 
34
33
  - **Playwright** — the lens authors `*.spec.ts` E2E/API specs (reusing the repo's existing fixtures and
@@ -43,7 +42,7 @@ the cases `test-cases.md` enumerates and the acceptance criteria the stories def
43
42
 
44
43
  Reuse what already exists: load the connected code repos' code-maps (`yad-test-cases` Step 2b) so
45
44
  generated tests target real endpoints/components, not invented ones, and prefer the lowest useful test
46
- level (unit > integration > E2E) per Murat's principles.
45
+ level (unit > integration > E2E).
47
46
 
48
47
  ## Link (reference an existing suite)
49
48
 
@@ -55,7 +54,7 @@ path/name + URL, and map it to the story it covers.
55
54
 
56
55
  Either direction ends by writing `epics/EP-<slug>/.sdlc/test-links.json` — the machine-readable
57
56
  case→test map — and a `## Automation (<tool>)` section in `test-cases.md` linking each case to its test.
58
- The tests themselves live in the code repo; the hub keeps the *links* and the Markdown spec beside the
57
+ The tests themselves live in the code repo; the Product keeps the *links* and the Markdown spec beside the
59
58
  other epic artifacts.
60
59
 
61
60
  ## Degrade path (no MCP / no tool)
@@ -1,6 +1,6 @@
1
1
  # Testing registry — schema + freshness rule
2
2
 
3
- The registry is the product hub's record of which testing tool is connected and how to reach it. It is
3
+ The registry is the Product's record of which testing tool is connected and how to reach it. It is
4
4
  **project-wide** (one testing tool per project, shared across every epic), so it lives at the product
5
5
  root, not under any `epics/EP-<slug>/.sdlc/`.
6
6
 
@@ -50,6 +50,6 @@ only). This mirrors how `repos.json`, `hub.json`, and `design.json` are committe
50
50
 
51
51
  ## Greenfield
52
52
 
53
- A brand-new product hub has no `testing.json`. That is valid — `yad-test-cases` treats "no testing tool
53
+ A brand-new Product has no `testing.json`. That is valid — `yad-test-cases` treats "no testing tool
54
54
  connected" the same as `tool: "none"` and produces the Markdown test-case artifact only. The registry
55
55
  appears the first time `connect` runs.
@@ -1,21 +1,21 @@
1
1
  ---
2
2
  name: yad-defects
3
- description: 'Phase 6 output enrichment (never a gate) — the quality-gap report. Generates a per-epic AND per-thread defect/bug report (the vendored React/Vite/Tailwind shell HTML + a DEFECTS.md) that aggregates every kind:defect change-epic + each change.json defect block + shipped regressions in the build ledger (the folded build-log.json unioned with every build-log/ shard) BY escape_stage (the SDLC gate that should have caught the defect) and root_cause, and visualizes WHERE quality gaps systematically come from — e.g. "% of this feature''s defects that escaped at the test-cases gate" — so the team hardens the originating stage instead of just fixing symptoms. Degrades to markdown-only when no docs target is connected. Use when the user says "show the defect report", "where are our quality gaps", "generate the bug report for this epic", or "which gate is leaking defects".'
3
+ description: 'Phase 6 output enrichment (never a gate) — the quality-gap report. Generates a per-epic AND per-thread defect/bug report (the vendored React/Vite/Tailwind shell HTML + a DEFECTS.md) that aggregates every defect-type change-epic + each change.json defect block + shipped regressions in the build ledger (the folded build-log.json unioned with every build-log/ shard) BY escape_stage (the SDLC gate that should have caught the defect) and root_cause, and visualizes WHERE quality gaps systematically come from — e.g. "% of this feature''s defects that escaped at the test-cases gate" — so the team hardens the originating stage instead of just fixing symptoms. Degrades to markdown-only when no docs target is connected. Use when the user says "show the defect report", "where are our quality gaps", "generate the bug report for this epic", or "which gate is leaking defects".'
4
4
  ---
5
5
 
6
6
  # SDLC — Quality-Gap Report (Phase 6, output enrichment)
7
7
 
8
8
  **Goal:** Turn the thread's defects into a **systemic quality signal**. Because every defect is a
9
- first-class `kind: defect` change-epic carrying an `escape_stage` (the gate that *should* have caught it)
9
+ first-class `defect`-type change-epic carrying an `escape_stage` (the gate that *should* have caught it)
10
10
  and a `root_cause`, this report can show not just *what* broke but *where the SDLC let it through* — so
11
11
  the team fixes the originating stage (weak test design, an under-specified story, a missed architecture
12
12
  risk), not just the symptom. It is an **output enrichment**, exactly like `yad-docs` — **never a gate**.
13
13
 
14
14
  ## Conventions
15
15
 
16
- - `{project-root}` resolves from the product hub.
16
+ - `{project-root}` resolves from the Product.
17
17
  - Reuses the **`yad-docs` shell** verbatim (`../yad-docs/templates/app/`) — generated `src/data/*.ts`,
18
- themed, deployed via `yad docs deploy`; build-only / markdown-only when no docs target.
18
+ themed; build-only / markdown-only (`yad docs deploy` does not build `defects-site/` yet).
19
19
  - Per **epic** (one epic's defects) and per **thread** (the whole feature; the thread report lives under
20
20
  the genesis epic, since `thread == genesis id`). The thread is derived from `parent:` frontmatter.
21
21
  - Deterministic generation, like `yad-docs` (stable sort, fixed key order, no timestamps in data).
@@ -30,11 +30,11 @@ risk), not just the symptom. It is an **output enrichment**, exactly like `yad-d
30
30
 
31
31
  ### Step 1 — Collect the defects
32
32
  Resolve the scope (`yad thread <id> --json` for a thread). Collect, across the scoped epic(s):
33
- - every `kind: defect` (and `kind: hotfix`) change-epic + its `.sdlc/change.json` `defect` block
33
+ - every `defect` (and `hotfix`) type change-epic + its `.sdlc/change.json` `defect` block
34
34
  (`origin`, `severity`, `escape_stage`, `root_cause`);
35
35
  - the shipped regression fixes from each epic's build ledger (the fix that closed the defect, linking
36
36
  the change-epic → its regression story/test);
37
- - open reconcile debt (a hotfix whose front truth is not yet restored).
37
+ - open reconcile debt (a hotfix whose Shape truth is not yet restored).
38
38
 
39
39
  The build ledger is **shard-then-fold**: read it as the **union** of the folded `.sdlc/build-log.json`
40
40
  `ships` PLUS every loose `.sdlc/build-log/` shard, deduped by `(story, task, repo)` — a shard WINS over a
@@ -66,8 +66,8 @@ Generate the site into `epics/<scope>/defects-site/` with sections:
66
66
  5. **Severity & age.**
67
67
  6. **Recommendations** — which originating stage to harden, derived from the top escape-stages.
68
68
 
69
- Also write a plain `epics/<scope>/DEFECTS.md` mirror. On `action: deploy`, `yad docs deploy` the site
70
- (build-only when no target).
69
+ Also write a plain `epics/<scope>/DEFECTS.md` mirror. On `action: deploy`, note that
70
+ `yad docs deploy` does not build this folder yet (it builds only the epic `docs-site/` and the overview) — build it with `npm ci` (or `npm install` when it has no lockfile) and `npm run build` inside the folder, and report it as build-only; a failed npm build is reported as a failure, never as a deploy.
71
71
 
72
72
  ## Hard rules
73
73