yadflow 3.19.0-next.2 → 4.0.0-next.3

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 (158) hide show
  1. package/CHANGELOG.md +371 -0
  2. package/README.md +160 -22
  3. package/bin/commands.mjs +46 -0
  4. package/bin/yad.mjs +506 -117
  5. package/cli/artifact-status.mjs +30 -11
  6. package/cli/capture.mjs +417 -0
  7. package/cli/checkpoint.mjs +47 -27
  8. package/cli/claims.mjs +165 -0
  9. package/cli/codeowners-command.mjs +170 -0
  10. package/cli/codeowners.mjs +397 -0
  11. package/cli/commit.mjs +13 -9
  12. package/cli/dial.mjs +183 -0
  13. package/cli/docs.mjs +88 -32
  14. package/cli/doctor.mjs +1682 -90
  15. package/cli/epic-state.mjs +3460 -211
  16. package/cli/epic.mjs +506 -0
  17. package/cli/errors.mjs +3 -0
  18. package/cli/fold.mjs +247 -0
  19. package/cli/gate-local.mjs +296 -0
  20. package/cli/gate.mjs +977 -183
  21. package/cli/history.mjs +556 -0
  22. package/cli/hook.mjs +264 -53
  23. package/cli/hubcommit.mjs +9 -19
  24. package/cli/index-command.mjs +87 -0
  25. package/cli/ledger.mjs +54 -4
  26. package/cli/lib.mjs +161 -9
  27. package/cli/manifest.mjs +376 -38
  28. package/cli/migrate.mjs +683 -52
  29. package/cli/mode.mjs +170 -0
  30. package/cli/next.mjs +365 -86
  31. package/cli/openpr.mjs +193 -34
  32. package/cli/owners.mjs +266 -0
  33. package/cli/people.mjs +657 -0
  34. package/cli/plan.mjs +466 -125
  35. package/cli/platform.mjs +109 -128
  36. package/cli/product-index.mjs +287 -0
  37. package/cli/protection.mjs +706 -0
  38. package/cli/reconcile.mjs +40 -12
  39. package/cli/repo-publish.mjs +24 -26
  40. package/cli/repo.mjs +23 -14
  41. package/cli/report.mjs +24 -14
  42. package/cli/review.mjs +19 -22
  43. package/cli/riskmap-command.mjs +289 -0
  44. package/cli/riskmap.mjs +373 -0
  45. package/cli/setup.mjs +127 -279
  46. package/cli/ship.mjs +6 -5
  47. package/cli/skill.mjs +180 -0
  48. package/cli/skip.mjs +211 -30
  49. package/cli/thread.mjs +41 -16
  50. package/cli/tidy.mjs +18 -18
  51. package/cli/update-commit.mjs +21 -21
  52. package/cli/usage.mjs +115 -109
  53. package/package.json +3 -3
  54. package/skills/sdlc/config.yaml +149 -70
  55. package/skills/sdlc/module-help.csv +24 -24
  56. package/skills/yad-analysis/SKILL.md +121 -59
  57. package/skills/yad-architecture/SKILL.md +30 -18
  58. package/skills/yad-architecture/references/contract-format.md +8 -5
  59. package/skills/yad-backfill/SKILL.md +10 -4
  60. package/skills/yad-backfill/references/backfill.md +8 -1
  61. package/skills/yad-backfill/templates/checks/backfill-check.sh +10 -2
  62. package/skills/yad-change/SKILL.md +119 -42
  63. package/skills/yad-change/references/triage.md +42 -27
  64. package/skills/yad-checks/SKILL.md +68 -35
  65. package/skills/yad-checks/references/check-gates.md +310 -85
  66. package/skills/yad-checks/templates/checks/commit-message.sh +17 -3
  67. package/skills/yad-checks/templates/checks/contract-check.sh +130 -9
  68. package/skills/yad-checks/templates/checks/epic-open.sh +2 -2
  69. package/skills/yad-checks/templates/checks/ledger-guard.sh +55 -8
  70. package/skills/yad-checks/templates/checks/lineage-check.sh +23 -9
  71. package/skills/yad-checks/templates/checks/reconcile-debt-check.sh +1 -1
  72. package/skills/yad-checks/templates/checks/risk-map-check.sh +438 -0
  73. package/skills/yad-checks/templates/checks/verified-commits.sh +20 -46
  74. package/skills/yad-checks/templates/github/yad-checks.yml +13 -2
  75. package/skills/yad-checks/templates/github/yad-hub-checks.yml +5 -5
  76. package/skills/yad-checks/templates/github/yad-update-guard.yml +7 -5
  77. package/skills/yad-checks/templates/github/yad-verified-commits.yml +4 -4
  78. package/skills/yad-checks/templates/gitlab/yad-checks.gitlab-ci.yml +10 -1
  79. package/skills/yad-checks/templates/gitlab/yad-hub-checks.gitlab-ci.yml +5 -5
  80. package/skills/yad-checks/templates/gitlab/yad-verified-commits.gitlab-ci.yml +3 -3
  81. package/skills/yad-checks/templates/hooks/ledger-guard-cursor.sh +91 -0
  82. package/skills/yad-checks/templates/hooks/ledger-guard.sh +37 -6
  83. package/skills/yad-checks/templates/hooks/yad-capture.sh +53 -0
  84. package/skills/yad-commit/SKILL.md +4 -4
  85. package/skills/yad-connect-design/SKILL.md +5 -5
  86. package/skills/yad-connect-design/references/design-context.md +1 -1
  87. package/skills/yad-connect-design/references/design-registry.md +2 -2
  88. package/skills/yad-connect-docs/SKILL.md +11 -11
  89. package/skills/yad-connect-docs/references/docs-registry.md +1 -1
  90. package/skills/yad-connect-learning/SKILL.md +5 -5
  91. package/skills/yad-connect-learning/references/learning-registry.md +2 -2
  92. package/skills/yad-connect-repos/SKILL.md +66 -45
  93. package/skills/yad-connect-repos/references/code-context.md +4 -4
  94. package/skills/yad-connect-repos/references/hub-config.md +47 -51
  95. package/skills/yad-connect-repos/references/repos-registry.md +8 -7
  96. package/skills/yad-connect-repos/references/risk-map.md +81 -0
  97. package/skills/yad-connect-testing/SKILL.md +5 -5
  98. package/skills/yad-connect-testing/references/testing-context.md +3 -4
  99. package/skills/yad-connect-testing/references/testing-registry.md +2 -2
  100. package/skills/yad-defects/SKILL.md +7 -7
  101. package/skills/yad-discovery/SKILL.md +131 -94
  102. package/skills/yad-discovery/references/discovery-schema.md +22 -6
  103. package/skills/yad-discovery/references/foundation-schema.md +374 -0
  104. package/skills/yad-docs/SKILL.md +13 -8
  105. package/skills/yad-docs/references/data-mapping.md +9 -7
  106. package/skills/yad-docs/templates/app/package-lock.json +3 -3
  107. package/skills/yad-docs-overview/SKILL.md +30 -15
  108. package/skills/yad-docs-overview/references/pipeline-model.md +33 -20
  109. package/skills/yad-docs-sync/SKILL.md +10 -5
  110. package/skills/yad-docs-sync/references/staleness.md +8 -7
  111. package/skills/yad-engineer-review/SKILL.md +81 -17
  112. package/skills/yad-engineer-review/references/ship-and-record.md +17 -8
  113. package/skills/yad-epic/SKILL.md +173 -92
  114. package/skills/yad-epic/references/state-schema.md +610 -94
  115. package/skills/yad-hub-bridge/SKILL.md +57 -38
  116. package/skills/yad-hub-bridge/references/bridge.md +94 -67
  117. package/skills/yad-hub-bridge/references/login-roster.md +163 -70
  118. package/skills/yad-hub-bridge/templates/checks/hub-route.sh +22 -19
  119. package/skills/yad-hub-bridge/templates/github/yad-gate-sync.yml +34 -14
  120. package/skills/yad-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +2 -2
  121. package/skills/yad-hub-bridge/templates/gitlab/yad-gate-sync.gitlab-ci.yml +21 -11
  122. package/skills/yad-implement/SKILL.md +27 -13
  123. package/skills/yad-implement/references/implement-conventions.md +2 -2
  124. package/skills/yad-learn/SKILL.md +9 -9
  125. package/skills/yad-learn/references/learning-state.md +2 -2
  126. package/skills/yad-open-pr/SKILL.md +61 -26
  127. package/skills/yad-pair-review/SKILL.md +7 -5
  128. package/skills/yad-pair-review/references/session-state.md +1 -1
  129. package/skills/yad-pr-template/SKILL.md +47 -26
  130. package/skills/yad-pr-template/references/risk-routing.md +97 -24
  131. package/skills/yad-pr-template/templates/checks/pr-template.sh +34 -11
  132. package/skills/yad-pr-template/templates/checks/pr-title.sh +32 -9
  133. package/skills/yad-pr-template/templates/checks/risk-route.sh +107 -14
  134. package/skills/yad-pr-template/templates/github/pull_request_template.md +7 -5
  135. package/skills/yad-pr-template/templates/gitlab/merge_request_templates/Default.md +7 -5
  136. package/skills/yad-pr-template/templates/hub/github/pull_request_template.md +14 -13
  137. package/skills/yad-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +14 -12
  138. package/skills/yad-reconcile/SKILL.md +3 -3
  139. package/skills/yad-report/SKILL.md +5 -5
  140. package/skills/yad-review-companion/SKILL.md +6 -3
  141. package/skills/yad-review-gate/SKILL.md +203 -93
  142. package/skills/yad-review-gate/references/gating.md +232 -54
  143. package/skills/yad-run/SKILL.md +81 -51
  144. package/skills/yad-run/references/run-loop.md +61 -39
  145. package/skills/yad-ship/SKILL.md +15 -11
  146. package/skills/yad-spec/SKILL.md +21 -6
  147. package/skills/yad-spec/references/spec-handoff.md +17 -5
  148. package/skills/yad-status/SKILL.md +115 -50
  149. package/skills/yad-stories/SKILL.md +37 -21
  150. package/skills/yad-stories/references/story-schema.md +7 -6
  151. package/skills/yad-stub/SKILL.md +58 -47
  152. package/skills/yad-sync-repos/SKILL.md +2 -2
  153. package/skills/yad-test-cases/SKILL.md +32 -23
  154. package/skills/yad-test-cases/references/test-cases-schema.md +7 -4
  155. package/skills/yad-timeline/SKILL.md +8 -7
  156. package/skills/yad-ui/SKILL.md +41 -18
  157. package/cli/roster.mjs +0 -164
  158. package/skills/sdlc/install.sh +0 -68
@@ -1,8 +1,8 @@
1
1
  # yad-managed: yad-checks
2
- # verified-commits gate for the PRODUCT HUB: every PR (including the Shape review/EP-* PRs)
3
- # must contain only commits whose signature the platform marks Verified AND whose author email is a
4
- # known identity (.sdlc/verified-authors — generated by `yad check --fix` from the hub roster).
5
- # Standalone workflow so it never collides with the hub-flavored yad-checks workflow.
2
+ # verified-commits gate for the PRODUCT: every PR (including the Shape review/EP-* PRs)
3
+ # must contain only commits whose signature the platform marks Verified. Who may author them is who has
4
+ # write access to the repo — yadflow keeps no author list.
5
+ # Standalone workflow so it never collides with the Product-flavored yad-checks workflow.
6
6
  name: yad-verified-commits
7
7
  on:
8
8
  pull_request:
@@ -53,6 +53,15 @@ yad-contract-check:
53
53
  script:
54
54
  - bash checks/contract-check.sh "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
55
55
 
56
+ # E65/E66 — the risk map (.sdlc/risk-map: a risk level per directory, no names). Warnings, and the
57
+ # approver count read from the base branch's map. It never fails the pipeline (the count is reported,
58
+ # not enforced), so the job needs no allow_failure.
59
+ yad-risk-map:
60
+ extends: .sdlc_mr_only
61
+ needs: []
62
+ script:
63
+ - bash checks/risk-map-check.sh "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
64
+
56
65
  yad-build-test-lint:
57
66
  extends: .sdlc_mr_only
58
67
  needs: []
@@ -102,7 +111,7 @@ yad-pr-template:
102
111
  - body="$(mktemp)"; printf '%s' "$CI_MERGE_REQUEST_DESCRIPTION" > "$body"
103
112
  - bash checks/pr-template.sh --profile code "$body"
104
113
 
105
- # No unverified commits from unverified users: platform-Verified signature + allowlisted author.
114
+ # No unverified commits: a platform-Verified signature on every commit (write access decides who authors).
106
115
  # Needs a CI/CD variable GITLAB_TOKEN (or SDLC_API_TOKEN) with read_api scope — CI_JOB_TOKEN cannot
107
116
  # read the commit-signature API. Without it the job FAILS closed with guidance.
108
117
  yad-verified-commits:
@@ -1,11 +1,11 @@
1
1
  # yad-managed-include: yad-checks
2
- # Pattern gates for the PRODUCT HUB, as an INCLUDABLE fragment. Pulled into the hub's root
2
+ # Pattern gates for the PRODUCT, as an INCLUDABLE fragment. Pulled into the Product's root
3
3
  # .gitlab-ci.yml via:
4
4
  # include:
5
5
  # - local: '.gitlab/ci/yad-hub-checks.yml'
6
6
  # MRs run with `--profile hub` and split by source branch: review/EP-* MRs are Shape artifact-
7
- # review vehicles (a `review: <artifact> (EP-<slug>)` title + the hub artifact-review template body);
8
- # every other MR is a tooling/code change to the hub itself and follows the code convention (a
7
+ # review vehicles (a `review: <artifact> (EP-<slug>)` title + the Product artifact-review template body);
8
+ # every other MR is a tooling/code change to the Product itself and follows the code convention (a
9
9
  # Conventional-Commits title + the code task template body). The source branch is passed via --head so
10
10
  # the gate can tell the two apart. Commit subjects are always Conventional-Commits.
11
11
  # Job names are yad-hub-prefixed so they coexist with any code-repo fragment in one pipeline; jobs use
@@ -33,14 +33,14 @@ yad-hub-pr-title:
33
33
  extends: .yad_hub_mr_only
34
34
  needs: []
35
35
  script:
36
- - changed="$(mktemp)"; git diff --name-only "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD" > "$changed"
36
+ - changed="$(mktemp)"; git -c core.quotePath=false diff --no-renames --name-only "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD" > "$changed"
37
37
  - bash checks/pr-title.sh --profile hub --head "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" --changed "$changed" "$CI_MERGE_REQUEST_TITLE"
38
38
 
39
39
  yad-hub-pr-template:
40
40
  extends: .yad_hub_mr_only
41
41
  needs: []
42
42
  script:
43
- - changed="$(mktemp)"; git diff --name-only "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD" > "$changed"
43
+ - changed="$(mktemp)"; git -c core.quotePath=false diff --no-renames --name-only "origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD" > "$changed"
44
44
  - body="$(mktemp)"; printf '%s' "$CI_MERGE_REQUEST_DESCRIPTION" > "$body"
45
45
  - bash checks/pr-template.sh --profile hub --head "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" --changed "$changed" "$body"
46
46
 
@@ -1,13 +1,13 @@
1
1
  # yad-managed-include: yad-checks
2
- # verified-commits gate for the PRODUCT HUB, as an INCLUDABLE fragment. Pulled into the hub's root
2
+ # verified-commits gate for the PRODUCT, as an INCLUDABLE fragment. Pulled into the Product's root
3
3
  # .gitlab-ci.yml via:
4
4
  # include:
5
5
  # - local: '.gitlab/ci/yad-verified-commits.yml'
6
6
  # Every MR (including the Shape review/EP-* MRs) must contain only commits whose signature the
7
- # platform marks Verified AND whose author email is a known identity (.sdlc/verified-authors).
7
+ # platform marks Verified. Who may author them is who has write access to the repo — no author list.
8
8
  #
9
9
  # Needs a CI/CD variable GITLAB_TOKEN (or SDLC_API_TOKEN) with read_api scope — CI_JOB_TOKEN cannot
10
- # read the commit-signature API. The hub's SDLC_GATE_TOKEN (from the gate-sync wiring) also works:
10
+ # read the commit-signature API. The Product's SDLC_GATE_TOKEN (from the gate-sync wiring) also works:
11
11
  # set GITLAB_TOKEN: $SDLC_GATE_TOKEN in the job if you prefer one token.
12
12
  # Job name is yad-hub-prefixed so it can coexist with the code-repo fragment in one pipeline.
13
13
  yad-hub-verified-commits:
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env bash
2
+ # ledger-guard CURSOR ADAPTER — the same guard, answered in the protocol Cursor requires (E11).
3
+ #
4
+ # Cursor's `preToolUse` is a PERMISSION hook, and its docs are explicit: for a permission hook,
5
+ # "invalid JSON or a response that doesn't match the hook's schema blocks the action". Empty stdout is
6
+ # invalid JSON. The plain `ledger-guard.sh` prints nothing when it allows — so wiring it directly
7
+ # would have blocked EVERY file write in a verified project, which is the exact opposite of this
8
+ # guard's fail-open design.
9
+ #
10
+ # This wrapper guarantees one thing: a permission answer on stdout, always, whatever happens below.
11
+ #
12
+ # allow {"permission":"allow"} exit 0
13
+ # deny {"permission":"deny","user_message":"<reason>","agent_message":"<reason>"} exit 0
14
+ #
15
+ # A deny exits 0 because the JSON is the authoritative answer and exit 0 is what tells Cursor to read
16
+ # it; exit 2 blocks too, but is documented as the code for "no JSON to read" and would discard the
17
+ # reason — and naming the command that owns the transition is the whole point of speaking at edit time.
18
+ #
19
+ # It takes NO arguments, deliberately. Cursor's `hooks.json` holds one command STRING and does not
20
+ # document whether it is run through a shell, so a command with a flag in it might never be split into
21
+ # program and argument — a command not found, silently, on every call.
22
+ set -uo pipefail
23
+
24
+ # `CDPATH=` is load-bearing, and the Cursor entry is what makes it so: that entry invokes the script
25
+ # by a RELATIVE path, so `${BASH_SOURCE[0]}` is relative and `cd` consults $CDPATH before the current
26
+ # directory. An exported CDPATH in a login profile — an ordinary thing to have, and inherited by the
27
+ # harness — would resolve `hooks` to some other tree entirely, and the guard would fail open on every
28
+ # write for as long as that profile lived.
29
+ #
30
+ # The loop resolves a symlinked SCRIPT, which `cd -P` does not: `-P` resolves the directory path, and
31
+ # a script reached through a link in another directory would otherwise look for its sibling beside the
32
+ # LINK and not find it. `readlink` without `-f` because `-f` is not portable to macOS.
33
+ _src="${BASH_SOURCE[0]}"
34
+ while [ -L "$_src" ]; do
35
+ _dir="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
36
+ _src="$(readlink -- "$_src")"
37
+ case "$_src" in /*) ;; *) _src="$_dir/$_src" ;; esac
38
+ done
39
+ HOOK_DIR="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
40
+ ALLOW='{"permission":"allow"}'
41
+
42
+ # Every failure below this line resolves to ALLOW, because this guard fails open by design and CI is
43
+ # what fails closed. A wrapper that blocked when it could not reach `yad` would stop an agent editing
44
+ # anything at all the moment an install went sideways.
45
+ if [ ! -x "$HOOK_DIR/ledger-guard.sh" ]; then
46
+ echo " • yad hook: $HOOK_DIR/ledger-guard.sh is missing or not executable — allowing (run \`yad check --fix\`)" >&2
47
+ echo "$ALLOW"
48
+ exit 0
49
+ fi
50
+
51
+ # stdout is CAPTURED (the JSON verdict), stderr is not (it flows through to Cursor's log). stdin is
52
+ # inherited by the command substitution, so the tool-call payload still reaches the guard.
53
+ raw="$("$HOOK_DIR/ledger-guard.sh" --format cursor)"
54
+ rc=$?
55
+
56
+ # THE VERDICT IS THE LAST NON-EMPTY LINE, not the whole of stdout, and it is recognised by containing
57
+ # a `"permission"` key rather than by starting with one.
58
+ #
59
+ # Both halves of that matter. `yad`'s own top-level failure handler prints to STDOUT, so a usage error
60
+ # or a crash banner can land in front of the JSON; a prefix test would then fail to recognise our own
61
+ # deny and fall through to the allow below, turning a real refusal into a permitted write with nothing
62
+ # on stderr to say so. And a containment test does not care what order the JSON's properties are in,
63
+ # so the deny answer's shape on the other side of this language boundary is not silently load-bearing.
64
+ verdict="$(printf '%s\n' "$raw" | awk 'NF { line = $0 } END { print line }')"
65
+
66
+ # `ledger-guard.sh` has its own fail-open branches that exit 0 with EMPTY stdout — no `yad` on PATH,
67
+ # an install it cannot resolve — and each one of those would otherwise reach Cursor as invalid JSON
68
+ # and block the write.
69
+ case "$verdict" in
70
+ *'"permission"'*) echo "$verdict"; exit 0 ;;
71
+ esac
72
+
73
+ # EXIT 2 IS A DENY, even with nothing on stdout — and this branch is reached in a real, ordinary case.
74
+ # `ledger-guard.sh` resolves `yad` from the Product's own `node_modules/yadflow` BEFORE `PATH`, so a
75
+ # project pinned to a yadflow older than `--format` parses the flag, ignores it, and answers in the
76
+ # exit protocol: exit 2, empty stdout. Falling through to the generic allow below would turn a real
77
+ # refusal into a permitted write, and print "exited 2 — allowing" while doing it.
78
+ #
79
+ # The message is a FIXED literal with nothing interpolated. The reason the older `yad` printed has
80
+ # already gone to stderr, where Cursor logs it; escaping arbitrary multi-line text into JSON in bash
81
+ # is exactly the sort of thing that produces an off-schema response, and there is no need to risk it.
82
+ if [ "$rc" -eq 2 ]; then
83
+ echo '{"permission":"deny","user_message":"yad: this edit touches CI-owned gate state.","agent_message":"This edit touches CI-owned gate state, which CI is the sole writer of. Do not edit the ledger by hand: use `yad gate open` for the transition, and commit only the artifact. The installed yadflow is older than this hook, so the full reason is in the hook log rather than here — `yad doctor` will name the version gap."}'
84
+ exit 0
85
+ fi
86
+
87
+ if [ "$rc" -ne 0 ]; then
88
+ echo " • yad hook: ledger-guard.sh exited $rc — allowing (run \`yad doctor\` to check the install)" >&2
89
+ fi
90
+ echo "$ALLOW"
91
+ exit 0
@@ -2,7 +2,8 @@
2
2
  # ledger-guard HARNESS HOOK — the local half of the CI gate of the same name (#171).
3
3
  #
4
4
  # The gate ledger is CI-owned in verified mode: `checks/ledger-guard.sh` rejects any non-bot commit
5
- # that changes `epics/*/.sdlc/{state,approvals,comments,hub-prs}.json` or `epics/*/reviews/*.md`.
5
+ # that changes `epics/*/.sdlc/{state,approvals,comments,product-prs,hub-prs}.json` or `epics/*/reviews/*.md`,
6
+ # and the same files for the Foundation in `foundation/.sdlc/` and `foundation/reviews/` (E75).
6
7
  # This hook says so at the moment an agent tries the edit, instead of twenty minutes later in a
7
8
  # failed pipeline, and names the command that owns the transition (`yad gate open`).
8
9
  #
@@ -14,21 +15,51 @@
14
15
  # exit 0 allow
15
16
  # exit 2 deny, reason on stderr
16
17
  #
17
- # Wired for Claude Code as a `PreToolUse` hook in `.claude/settings.json` (`yad check --fix` writes
18
- # that entry). Any harness that can run a command and read those two exit codes can use it.
18
+ # Wired by `yad check --fix` for every harness that can refuse a write BEFORE it lands:
19
+ # Claude Code a `PreToolUse` hook in `.claude/settings.json`, pointing straight at THIS script
20
+ # Cursor a `preToolUse` hook in `.cursor/hooks.json`, pointing at `ledger-guard-cursor.sh`
21
+ #
22
+ # DO NOT WIRE THIS SCRIPT INTO CURSOR DIRECTLY, and check the same thing before wiring it into any
23
+ # other harness by hand. Cursor's `preToolUse` is a PERMISSION hook: it wants a JSON verdict on
24
+ # stdout, and treats an empty or off-schema answer as a refusal. This script prints NOTHING when it
25
+ # allows — so wired there directly it would block every file write in the project, which is the
26
+ # opposite of the fail-open stance below. `ledger-guard-cursor.sh` exists to speak that protocol.
27
+ #
28
+ # A harness that reads the EXIT CODE can use this script as it stands. A hook that only fires AFTER
29
+ # the write — Cursor's `afterFileEdit`, for one — is deliberately not used: it could report the edit
30
+ # but never refuse it, and reporting is what the CI gate already does.
31
+ #
32
+ # The project root is resolved from this script's own location below, so it does not matter which
33
+ # variable the harness sets ($CLAUDE_PROJECT_DIR, $CURSOR_PROJECT_DIR) or where it runs the command
34
+ # from; those variables appear only in the wiring, to point at this file.
19
35
  #
20
36
  # FAIL-OPEN: if no `yad` can be found, this ALLOWS and says why on stderr. A guardrail that blocked
21
37
  # every edit the moment an install went sideways would be worse than the problem. The CI gate fails
22
38
  # CLOSED and is what actually protects the ledger.
23
39
  set -uo pipefail
24
40
 
25
- # The hub root is this script's grandparent — hooks/ledger-guard.sh — so the resolution below does
41
+ # The Product root is this script's grandparent — hooks/ledger-guard.sh — so the resolution below does
26
42
  # not depend on the harness's working directory.
27
- HOOK_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
43
+ # `CDPATH=` is load-bearing, and the Cursor entry is what makes it so: that entry invokes the script
44
+ # by a RELATIVE path, so `${BASH_SOURCE[0]}` is relative and `cd` consults $CDPATH before the current
45
+ # directory. An exported CDPATH in a login profile — an ordinary thing to have, and inherited by the
46
+ # harness — would resolve `hooks` to some other tree entirely, and the guard would fail open on every
47
+ # write for as long as that profile lived.
48
+ #
49
+ # The loop resolves a symlinked SCRIPT, which `cd -P` does not: `-P` resolves the directory path, and
50
+ # a script reached through a link in another directory would otherwise look for its sibling beside the
51
+ # LINK and not find it. `readlink` without `-f` because `-f` is not portable to macOS.
52
+ _src="${BASH_SOURCE[0]}"
53
+ while [ -L "$_src" ]; do
54
+ _dir="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
55
+ _src="$(readlink -- "$_src")"
56
+ case "$_src" in /*) ;; *) _src="$_dir/$_src" ;; esac
57
+ done
58
+ HOOK_DIR="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
28
59
  HUB_ROOT="$(dirname -- "$HOOK_DIR")"
29
60
 
30
61
  # Resolution order, cheapest and most specific first: an explicit override, then the copy installed
31
- # in this hub, then whatever is on PATH, then a network-free npx. `--no-install` matters — a hook
62
+ # in this Product, then whatever is on PATH, then a network-free npx. `--no-install` matters — a hook
32
63
  # runs on every tool call and must never pause an agent to download a package.
33
64
  CMD=()
34
65
  _yad_bin="${YAD_BIN:-}"
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env bash
2
+ # yad-capture HARNESS HOOK — background capture of Shape artifacts (E43).
3
+ #
4
+ # Runs AFTER an agent edits a file, and snapshots every changed artifact of the Product onto the
5
+ # person's private `yad/wip/<name>/<epic>` branches — built with git plumbing, so the checkout, the
6
+ # index and the current branch are never touched. The push runs in the background, at most once
7
+ # every few minutes. The decision lives in `yad capture --hook`; this file only locates `yad`.
8
+ #
9
+ # Wired by `yad check --fix`, in BOTH ledger modes:
10
+ # Claude Code a `PostToolUse` hook in `.claude/settings.json`
11
+ # Cursor an `afterFileEdit` hook in `.cursor/hooks.json`
12
+ # Any other harness: run this script (or `yad capture --hook`) after a file write.
13
+ #
14
+ # The contract: it ALWAYS exits 0 — a capture must never block or slow an agent. A problem is one line on
15
+ # stderr. Stdout carries ONE thing, and only under Claude Code: when a file this edit changed is also being
16
+ # edited by someone else (E46 claims), a PostToolUse JSON note the agent reads. Otherwise it prints nothing.
17
+ # `YAD_CAPTURE=0`, or `"capture": false` in the Product config, turns it off.
18
+ set -uo pipefail
19
+ # Drain the payload the harness sends on stdin, so it never sees a broken pipe — but never wait on it: not
20
+ # at all from a terminal (someone ran this by hand), and at most a second on a pipe that never closes.
21
+ if [ ! -t 0 ]; then
22
+ while IFS= read -r -t 1 _ 2>/dev/null; do :; done
23
+ fi
24
+ _src="${BASH_SOURCE[0]}"
25
+ while [ -L "$_src" ]; do
26
+ _dir="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
27
+ _src="$(readlink -- "$_src")"
28
+ case "$_src" in /*) ;; *) _src="$_dir/$_src" ;; esac
29
+ done
30
+ HOOK_DIR="$(CDPATH= cd -P -- "$(dirname -- "$_src")" && pwd)"
31
+ HUB_ROOT="$(dirname -- "$HOOK_DIR")"
32
+ [ "${YAD_CAPTURE:-}" = "0" ] && exit 0
33
+ CMD=()
34
+ _yad_bin="${YAD_BIN:-}"
35
+ if [ -n "${_yad_bin//[[:space:]]/}" ]; then
36
+ read -r -a CMD <<< "$_yad_bin"
37
+ elif [ -f "$HUB_ROOT/node_modules/yadflow/bin/yad.mjs" ] && command -v node >/dev/null 2>&1; then
38
+ CMD=(node "$HUB_ROOT/node_modules/yadflow/bin/yad.mjs")
39
+ elif command -v yad >/dev/null 2>&1; then
40
+ CMD=(yad)
41
+ elif command -v npx >/dev/null 2>&1; then
42
+ CMD=(npx --no-install yadflow)
43
+ else
44
+ echo " • yad capture: no \`yad\` on PATH and none installed in $HUB_ROOT — nothing captured" >&2
45
+ exit 0
46
+ fi
47
+ [ "${#CMD[@]}" -eq 0 ] && exit 0
48
+ # Claude Code sets CLAUDE_PROJECT_DIR for its hooks; it reads JSON back. Cursor reads nothing back.
49
+ FORMAT=()
50
+ [ -n "${CLAUDE_PROJECT_DIR:-}" ] && FORMAT=(--format claude)
51
+ # `${FORMAT[@]+…}`: bash 3.2 (macOS) calls an empty array unbound under `set -u`.
52
+ "${CMD[@]}" capture --hook ${FORMAT[@]+"${FORMAT[@]}"} --dir "$HUB_ROOT" </dev/null
53
+ exit 0
@@ -14,7 +14,7 @@ and `yad-ship` use. It **never auto-advances**; it just commits.
14
14
  ## Conventions
15
15
 
16
16
  - Run **inside the repo holding the staged change** — a code repo under
17
- `{project-root}/demo-repos/<repo>/`, or the product hub itself. Use absolute paths.
17
+ `{project-root}/demo-repos/<repo>/`, or the Product itself. Use absolute paths.
18
18
  - **Stage first.** Only the staged (`git add`) atomic change is committed. The guard refuses more than
19
19
  `ATOMIC_FILE_LIMIT` (3) staged files unless `--force` — split the change instead.
20
20
  - **Subject** — `<type>: <lowercase imperative description, no trailing period>`; types are
@@ -22,8 +22,8 @@ and `yad-ship` use. It **never auto-advances**; it just commits.
22
22
  - **Task trailer** — required on a code repo (anchors the `spec-link` + `commit-message` gates). Given
23
23
  with `--task` (a `<story>-T<NN>` id — an explicit value is validated against the `spec-link`
24
24
  contract, so a malformed id like a bare `EP-x-S01` fails locally instead of after a push), else
25
- derived from the branch (`feat/<story>-<task>-…`). Hub commits are not
26
- task-scoped, so the trailer is optional there — a missing-Task warning is informational on the hub
25
+ derived from the branch (`feat/<story>-<task>-…`). Product commits are not
26
+ task-scoped, so the trailer is optional there — a missing-Task warning is informational on the Product
27
27
  (`spec-link` is a code-repo gate) and only flags a real gate failure in a code repo.
28
28
  - **Contract-Change trailer** — `--contract-change` only when the diff touches the locked contract
29
29
  surface; it routes the change back to the architecture gate.
@@ -70,6 +70,6 @@ the architecture gate. To also open the PR/MR in the same step, use `yad-ship`.
70
70
 
71
71
  ## Reference
72
72
  - Branch/commit conventions + the file-boundary rule: `../yad-implement/references/implement-conventions.md`.
73
- - The full convention text: `CONTRIBUTING.md`; the config: `skills/sdlc/config.yaml` `build`.
73
+ - The full convention text: `CONTRIBUTING.md`; the config: `.sdlc/config.yaml` `build`.
74
74
  - The gate that enforces the subject pattern: `../yad-checks/references/check-gates.md` (`commit-message`).
75
75
  - Open the PR/MR after committing: `../yad-open-pr/SKILL.md`; both at once: `../yad-ship/SKILL.md`.
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: yad-connect-design
3
- description: 'Connects a design tool (Figma, or another tool — pluggable) to the product hub so the UI design step can materialize the full feature design (mobile screens / web pages) inside it, not just Markdown. Registers the tool into the project-wide .sdlc/design.json (local-user / MCP-session auth, no stored tokens), detecting whether a design-tool MCP is available and degrading to markdown-only when it is not. Run at setup or any time the design tool changes. Reusable, idempotent, refreshable. Use when the user says "connect Figma", "connect a design tool", "refresh the design connection", or "list the design connection".'
3
+ description: 'Connects a design tool (Figma, or another tool — pluggable) to the Product so the UI design step can materialize the full feature design (mobile screens / web pages) inside it, not just Markdown. Registers the tool into the project-wide .sdlc/design.json (local-user / MCP-session auth, no stored tokens), detecting whether a design-tool MCP is available and degrading to markdown-only when it is not. Run at setup or any time the design tool changes. Reusable, idempotent, refreshable. Use when the user says "connect Figma", "connect a design tool", "refresh the design connection", or "list the design connection".'
4
4
  ---
5
5
 
6
6
  # SDLC — Connect a Design Tool (make the UI step design-tool aware)
7
7
 
8
8
  **Goal:** Let the UI design step (`yad-ui`) produce the **actual feature design** — the mobile screens
9
9
  and/or web pages — inside a design tool such as **Figma**, alongside the Markdown artifacts
10
- (`ui-design.md` / `DESIGN.md`). This skill **connects** a design tool to the product hub and records
10
+ (`ui-design.md` / `DESIGN.md`). This skill **connects** a design tool to the Product and records
11
11
  *how* to reach it (the tool, the project/file references, which MCP renders it) — never a credential.
12
12
 
13
13
  This is **setup/maintenance**, not a gated Shape step — it never touches `.sdlc/state.json` or any
@@ -18,7 +18,7 @@ markdown-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 **Figma-first but pluggable** (`config.yaml` `design.tools`): a design-tool
23
23
  *adapter*, like the GitHub/GitLab platform adapter. Figma is the primary provider; `pencil`
24
24
  (the `.pen` web/mobile editor) is a second, write-capable provider; `none` → markdown-only.
@@ -29,7 +29,7 @@ markdown-only exactly as before.
29
29
  the sibling of `.sdlc/repos.json` and `.sdlc/hub.json`.
30
30
  - Per-epic screen→frame links are written later by `yad-ui` (`epics/EP-<slug>/.sdlc/design-links.json`),
31
31
  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
 
@@ -94,7 +94,7 @@ Nothing auto-advances; this is setup.
94
94
  **available/unavailable** flag for the MCP (best-effort, the user's own session). No design tool
95
95
  connected ⇒ "markdown-only".
96
96
  - **`disconnect`** — remove the registry file (or set `tool: "none"`). The design tool's own
97
- project/files are **never touched** — only the hub's record of them.
97
+ project/files are **never touched** — only the Product's record of them.
98
98
 
99
99
  ## Hard rules
100
100
 
@@ -46,7 +46,7 @@ a screen, capture its node id + URL, and map components/tokens into `DESIGN.md`.
46
46
 
47
47
  Either direction ends by writing `epics/EP-<slug>/.sdlc/design-links.json` — the machine-readable
48
48
  screen→frame map — and a `## Design (<tool>)` section in `ui-design.md` linking each screen to its frame
49
- URL. The design itself lives in the tool; the hub keeps the *links* and the Markdown spec beside the
49
+ URL. The design itself lives in the tool; the Product keeps the *links* and the Markdown spec beside the
50
50
  other epic artifacts.
51
51
 
52
52
  ## Degrade path (no MCP / no tool)
@@ -1,6 +1,6 @@
1
1
  # Design registry — schema + freshness rule
2
2
 
3
- The registry is the product hub's record of which design tool is connected and how to reach it. It is
3
+ The registry is the Product's record of which design tool is connected and how to reach it. It is
4
4
  **project-wide** (one design 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
 
@@ -51,6 +51,6 @@ only). This mirrors how `repos.json` and `hub.json` are committed.
51
51
 
52
52
  ## Greenfield
53
53
 
54
- A brand-new product hub has no `design.json`. That is valid — `yad-ui` treats "no design tool connected"
54
+ A brand-new Product has no `design.json`. That is valid — `yad-ui` treats "no design tool connected"
55
55
  the same as `tool: "none"` and produces the Markdown artifacts only. The registry appears the first time
56
56
  `connect` runs.
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: yad-connect-docs
3
- description: 'Connects a docs/Pages publishing target to the product hub so the interactive-docs steps can build and deploy the generated SPA — not just commit its source. Registers the target into the project-wide .sdlc/docs.json (GitHub Pages / GitLab Pages / build-only), auto-detecting the platform from .sdlc/hub.json and resolving the Vite base path, with local-user auth and no stored tokens. Detects whether gh/glab is present and degrades to build-only when absent. Run at setup or any time the publish target changes. Reusable, idempotent, refreshable. Use when the user says "connect docs", "connect Pages", "refresh the docs connection", or "list the docs connection".'
3
+ description: 'Connects a docs/Pages publishing target to the Product so the interactive-docs steps can build and deploy the generated SPA — not just commit its source. Registers the target into the project-wide .sdlc/docs.json (GitHub Pages / GitLab Pages / build-only), auto-detecting the platform from .sdlc/hub.json and resolving the Vite base path, with local-user auth and no stored tokens. Detects whether gh/glab is present and degrades to build-only when absent. Run at setup or any time the publish target changes. Reusable, idempotent, refreshable. Use when the user says "connect docs", "connect Pages", "refresh the docs connection", or "list the docs connection".'
4
4
  ---
5
5
 
6
6
  # SDLC — Connect a Docs/Pages Target (make the docs steps publishable)
7
7
 
8
8
  **Goal:** Let the interactive-docs steps (`yad-docs` per epic, `yad-docs-overview` project-wide)
9
9
  **build and deploy** the generated React/Vite SPA to a real URL — a GitHub Pages or GitLab Pages site —
10
- instead of only committing its source. This skill **connects** a publishing target to the product hub
10
+ instead of only committing its source. This skill **connects** a publishing target to the Product
11
11
  and records *how* to reach it (the platform, the publish scope, the base path) — never a credential.
12
12
 
13
13
  This is **setup/maintenance**, not a gated Shape step — it never touches `.sdlc/state.json` or any
@@ -18,9 +18,9 @@ at a local `dist/` — build-only, no publish, 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 target is **GitHub-Pages-first but pluggable** — a *publish adapter*, mirroring the GitHub/GitLab
23
- platform adapter the hub already uses. `github-pages` and `gitlab-pages` are the providers; `none` →
23
+ platform adapter the Product already uses. `github-pages` and `gitlab-pages` are the providers; `none` →
24
24
  build-only (deliberate, no error).
25
25
  - The platform CLI (`gh` / `glab`) is a **subprocess**, used read/deploy-only via the user's own auth —
26
26
  never installed by this skill, never given a token. Absent ⇒ degrade to build-only (`source:
@@ -29,15 +29,15 @@ at a local `dist/` — build-only, no publish, exactly as before.
29
29
  NOT per-epic), the sibling of `.sdlc/hub.json`, `.sdlc/repos.json`, and `.sdlc/design.json`.
30
30
  - Per-epic / overview build manifests (`docs-build.json`) are written later by `yad-docs` /
31
31
  `yad-docs-overview`, not here. This skill describes the *connection*; it does not build.
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
 
36
36
  - `action` — `connect` (default) | `refresh` | `list` | `disconnect`.
37
37
  - `target` — `github-pages` | `gitlab-pages` | `none`. Default **auto-detected** from `.sdlc/hub.json`
38
- `platform` (github → `github-pages`, gitlab → `gitlab-pages`, null/no hub → `none`).
38
+ `platform` (github → `github-pages`, gitlab → `gitlab-pages`, null/no Product → `none`).
39
39
  - `scope` — `hub` (default) | `<repo-name>` | `dedicated`. Where the Pages site is published from (the
40
- hub repo, one connected code repo, or a dedicated docs repo).
40
+ Product repo, one connected code repo, or a dedicated docs repo).
41
41
  - `public` — `true` (default) | `false`. Whether the published site is public.
42
42
  - `base_path` — optional explicit override of the Vite `base` (otherwise resolved, Step 2).
43
43
 
@@ -45,7 +45,7 @@ at a local `dist/` — build-only, no publish, exactly as before.
45
45
 
46
46
  ### Step 1 — Resolve the target + detect the platform (the publish adapter)
47
47
  Determine the `target`. If not given, read `{project-root}/.sdlc/hub.json` `platform` and map it the same
48
- way the hub bridge maps repos: `github` → `github-pages`, `gitlab` → `gitlab-pages`, `null`/no hub →
48
+ way the Product bridge maps repos: `github` → `github-pages`, `gitlab` → `gitlab-pages`, `null`/no Product →
49
49
  `none` (deliberate build-only). Reject a `target` value outside the three providers (fall back to the
50
50
  detected default with a warning, the way `registerRepo` falls back on an unknown platform).
51
51
 
@@ -61,7 +61,7 @@ tokens**; everything in the registry is a plain reference. Do **not** install a
61
61
 
62
62
  ### Step 2 — Decide the publish scope + resolve the base path
63
63
  Resolve `scope` → `publishRepo`:
64
- - `hub` (default) → publish from the hub repo (read its name from `hub.json` `git_url`).
64
+ - `hub` (default) → publish from the Product repo (read its name from `hub.json` `git_url`).
65
65
  - `<repo-name>` → publish from that connected code repo (must exist in `.sdlc/repos.json`).
66
66
  - `dedicated` → a dedicated docs repo the user names (recorded as `publishRepo`).
67
67
 
@@ -109,7 +109,7 @@ the site here — `yad-docs` builds.**
109
109
  **available/unavailable** flag for the platform CLI (best-effort, the user's own session). No target
110
110
  connected ⇒ "build-only".
111
111
  - **`disconnect`** — remove the registry file (or set `target: "none"`). The platform's own Pages site is
112
- **never touched** — only the hub's record of it.
112
+ **never touched** — only the Product's record of it.
113
113
 
114
114
  ## Hard rules
115
115
 
@@ -127,6 +127,6 @@ the site here — `yad-docs` builds.**
127
127
  - Registry schema, the base-path resolution table, and the freshness/degrade rules:
128
128
  `references/docs-registry.md`.
129
129
  - The connect pattern this mirrors (design tool): `../yad-connect-design/SKILL.md`.
130
- - The connect pattern this mirrors (code repos + hub detection): `../yad-connect-repos/SKILL.md`.
130
+ - The connect pattern this mirrors (code repos + Product detection): `../yad-connect-repos/SKILL.md`.
131
131
  - The consumers — how `yad-docs` / `yad-docs-overview` build + deploy: `../yad-docs/SKILL.md`,
132
132
  `../yad-docs-overview/SKILL.md`.
@@ -38,7 +38,7 @@ Holds **no credentials** — every field is a plain reference. Auth is always th
38
38
 
39
39
  ## Platform auto-detection (from `.sdlc/hub.json`)
40
40
 
41
- When `target` is not given, map the hub's `platform` the same way `yad-connect-repos` maps a repo host:
41
+ When `target` is not given, map the Product's `platform` the same way `yad-connect-repos` maps a repo host:
42
42
 
43
43
  | `hub.json` `platform` | default `target` |
44
44
  |-----------------------|------------------|
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: yad-connect-learning
3
- description: 'Connects a learning/tutoring tool (DeepTutor, or another tool — pluggable) to the product hub so the cross-cutting learning layer can tutor any team member, at any SDLC stage, in the context of what is being built. Registers the tool into the project-wide .sdlc/learning.json (local-user auth, no stored tokens), detecting whether the DeepTutor CLI is on PATH and degrading to harness-native tutoring (the harness model reading project artifacts) when it is not. Optionally builds a project knowledge base from the SDLC artifacts + secret-scanned code-maps so tutoring is grounded. Run at setup or any time the learning tool changes. Reusable, idempotent, refreshable. Use when the user says "connect DeepTutor", "connect a learning tool", "refresh the learning connection", or "list the learning connection".'
3
+ description: 'Connects a learning/tutoring tool (DeepTutor, or another tool — pluggable) to the Product so the cross-cutting learning layer can tutor any team member, at any SDLC stage, in the context of what is being built. Registers the tool into the project-wide .sdlc/learning.json (local-user auth, no stored tokens), detecting whether the DeepTutor CLI is on PATH and degrading to harness-native tutoring (the harness model reading project artifacts) when it is not. Optionally builds a project knowledge base from the SDLC artifacts + secret-scanned code-maps so tutoring is grounded. Run at setup or any time the learning tool changes. Reusable, idempotent, refreshable. Use when the user says "connect DeepTutor", "connect a learning tool", "refresh the learning connection", or "list the learning connection".'
4
4
  ---
5
5
 
6
6
  # SDLC — Connect a Learning Tool (the cross-cutting learning layer)
7
7
 
8
8
  **Goal:** Let any team member pause at **any** SDLC stage and ask to learn a concept — and get tutored
9
9
  *in the context of what the team is actually building*. This skill **connects** a learning tool such as
10
- **DeepTutor** to the product hub and records *how* to reach it (the tool, the CLI, an optional grounded
10
+ **DeepTutor** to the Product and records *how* to reach it (the tool, the CLI, an optional grounded
11
11
  knowledge base) — never a credential. The consumer skill **`yad-learn`** does the tutoring per request
12
12
  and records the team's skills.
13
13
 
@@ -19,7 +19,7 @@ explains the concept. The learning layer is **purely opt-in and never blocks a g
19
19
 
20
20
  ## Conventions
21
21
 
22
- - `{project-root}` resolves from the project working directory (the **product hub**).
22
+ - `{project-root}` resolves from the project working directory (the **Product**).
23
23
  - The integration is **DeepTutor-first but pluggable** (`config.yaml` `learning.tools`): a learning-tool
24
24
  *adapter*, like the design/testing adapters. `none` → harness-native (yad-learn still tutors via the
25
25
  harness model).
@@ -30,7 +30,7 @@ explains the concept. The learning layer is **purely opt-in and never blocks a g
30
30
  the sibling of `.sdlc/repos.json`, `.sdlc/hub.json`, `.sdlc/design.json`, and `.sdlc/testing.json`.
31
31
  - Per-epic, per-member learning records + rendered tutorials are written later by `yad-learn`
32
32
  (`epics/EP-<slug>/.sdlc/learning-records.json` and `epics/EP-<slug>/learning/`), not here.
33
- - Speak in the configured `communication_language`; write documents in `document_output_language`.
33
+ - Speak in the `communication_language` set in `{project-root}/.sdlc/config.yaml`; write documents in `document_output_language`.
34
34
 
35
35
  ## Inputs
36
36
 
@@ -113,7 +113,7 @@ auto-advances; this is setup.
113
113
  **available/harness-native** flag for the CLI (best-effort). No learning tool connected ⇒
114
114
  "harness-native".
115
115
  - **`disconnect`** — remove the registry file (or set `tool: "none"`). DeepTutor's own config and
116
- knowledge bases are **never touched** — only the hub's record of them.
116
+ knowledge bases are **never touched** — only the Product's record of them.
117
117
 
118
118
  ## Hard rules
119
119
 
@@ -1,6 +1,6 @@
1
1
  # Learning registry — schema + freshness rule
2
2
 
3
- The registry is the product hub's record of which learning tool is connected and how to reach it. It is
3
+ The registry is the Product's record of which learning tool is connected and how to reach it. It is
4
4
  **project-wide** (one learning 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
 
@@ -55,6 +55,6 @@ only). This mirrors how `repos.json`, `hub.json`, `design.json`, and `testing.js
55
55
 
56
56
  ## Greenfield
57
57
 
58
- A brand-new product hub has no `learning.json`. That is valid — `yad-learn` treats "no learning tool
58
+ A brand-new Product has no `learning.json`. That is valid — `yad-learn` treats "no learning tool
59
59
  connected" the same as `tool: "none"` and tutors harness-native. The registry appears the first time
60
60
  `connect` runs.