yadflow 1.0.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 (77) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/LICENSE +21 -0
  3. package/README.md +559 -0
  4. package/bin/sdlc.mjs +135 -0
  5. package/cli/commit.mjs +81 -0
  6. package/cli/epic-state.mjs +220 -0
  7. package/cli/gate.mjs +456 -0
  8. package/cli/lib.mjs +142 -0
  9. package/cli/manifest.mjs +119 -0
  10. package/cli/openpr.mjs +65 -0
  11. package/cli/plan.mjs +127 -0
  12. package/cli/platform.mjs +151 -0
  13. package/cli/reconcile.mjs +83 -0
  14. package/cli/repo.mjs +61 -0
  15. package/cli/setup.mjs +208 -0
  16. package/package.json +51 -0
  17. package/skills/sdlc/config.yaml +156 -0
  18. package/skills/sdlc/install.sh +51 -0
  19. package/skills/sdlc/module-help.csv +17 -0
  20. package/skills/sdlc-author-analysis/SKILL.md +136 -0
  21. package/skills/sdlc-author-architecture/SKILL.md +180 -0
  22. package/skills/sdlc-author-architecture/references/contract-format.md +72 -0
  23. package/skills/sdlc-author-epic/SKILL.md +154 -0
  24. package/skills/sdlc-author-epic/references/state-schema.md +187 -0
  25. package/skills/sdlc-author-stories/SKILL.md +109 -0
  26. package/skills/sdlc-author-stories/references/story-schema.md +46 -0
  27. package/skills/sdlc-author-ui/SKILL.md +113 -0
  28. package/skills/sdlc-backfill/SKILL.md +91 -0
  29. package/skills/sdlc-backfill/references/backfill.md +66 -0
  30. package/skills/sdlc-backfill/templates/checks/backfill-check.sh +42 -0
  31. package/skills/sdlc-checks/SKILL.md +138 -0
  32. package/skills/sdlc-checks/references/check-gates.md +168 -0
  33. package/skills/sdlc-checks/templates/checks/build-test-lint.sh +14 -0
  34. package/skills/sdlc-checks/templates/checks/contract-check.sh +62 -0
  35. package/skills/sdlc-checks/templates/checks/spec-link.sh +38 -0
  36. package/skills/sdlc-checks/templates/checks/verified-commits.sh +120 -0
  37. package/skills/sdlc-checks/templates/github/sdlc-checks.yml +45 -0
  38. package/skills/sdlc-checks/templates/github/sdlc-verified-commits.yml +22 -0
  39. package/skills/sdlc-checks/templates/gitlab/.gitlab-ci.yml +40 -0
  40. package/skills/sdlc-checks/templates/gitlab/gitlab-ci.include-root.yml +7 -0
  41. package/skills/sdlc-checks/templates/gitlab/sdlc-checks.gitlab-ci.yml +47 -0
  42. package/skills/sdlc-checks/templates/gitlab/sdlc-verified-commits.gitlab-ci.yml +21 -0
  43. package/skills/sdlc-connect-repos/SKILL.md +159 -0
  44. package/skills/sdlc-connect-repos/references/code-context.md +92 -0
  45. package/skills/sdlc-connect-repos/references/hub-config.md +77 -0
  46. package/skills/sdlc-connect-repos/references/repos-registry.md +62 -0
  47. package/skills/sdlc-hub-bridge/SKILL.md +119 -0
  48. package/skills/sdlc-hub-bridge/references/bridge.md +136 -0
  49. package/skills/sdlc-hub-bridge/references/login-roster.md +42 -0
  50. package/skills/sdlc-hub-bridge/templates/checks/hub-route.sh +50 -0
  51. package/skills/sdlc-hub-bridge/templates/github/sdlc-gate-sync.yml +63 -0
  52. package/skills/sdlc-hub-bridge/templates/gitlab/gitlab-ci.include-root.yml +7 -0
  53. package/skills/sdlc-hub-bridge/templates/gitlab/sdlc-gate-sync.gitlab-ci.yml +64 -0
  54. package/skills/sdlc-implement/SKILL.md +143 -0
  55. package/skills/sdlc-implement/references/implement-conventions.md +103 -0
  56. package/skills/sdlc-implement/templates/.gitmessage +17 -0
  57. package/skills/sdlc-pr-template/SKILL.md +86 -0
  58. package/skills/sdlc-pr-template/references/risk-routing.md +54 -0
  59. package/skills/sdlc-pr-template/templates/checks/risk-route.sh +44 -0
  60. package/skills/sdlc-pr-template/templates/github/pull_request_template.md +30 -0
  61. package/skills/sdlc-pr-template/templates/gitlab/merge_request_templates/Default.md +32 -0
  62. package/skills/sdlc-pr-template/templates/hub/github/pull_request_template.md +36 -0
  63. package/skills/sdlc-pr-template/templates/hub/gitlab/merge_request_templates/Default.md +37 -0
  64. package/skills/sdlc-review-comments/SKILL.md +63 -0
  65. package/skills/sdlc-review-comments/references/comment-conventions.md +55 -0
  66. package/skills/sdlc-review-comments/templates/github/REVIEW_COMMENTS.md +49 -0
  67. package/skills/sdlc-review-comments/templates/gitlab/REVIEW_COMMENTS.md +49 -0
  68. package/skills/sdlc-review-gate/SKILL.md +196 -0
  69. package/skills/sdlc-review-gate/references/gating.md +79 -0
  70. package/skills/sdlc-run/SKILL.md +109 -0
  71. package/skills/sdlc-run/references/run-loop.md +121 -0
  72. package/skills/sdlc-ship/SKILL.md +86 -0
  73. package/skills/sdlc-ship/references/ship-and-record.md +67 -0
  74. package/skills/sdlc-ship/templates/.coderabbit.yaml +19 -0
  75. package/skills/sdlc-spec/SKILL.md +119 -0
  76. package/skills/sdlc-spec/references/spec-handoff.md +101 -0
  77. package/skills/sdlc-status/SKILL.md +92 -0
@@ -0,0 +1,42 @@
1
+ # Login roster — schema, resolution, per-repo routing
2
+
3
+ The roster lives in `.sdlc/hub.json` (`roster: [...]`) and is the only thing that turns a platform
4
+ **login** into an SDLC **name + role** for the ledger. Schema and the no-tokens rule are documented once
5
+ in `../../sdlc-connect-repos/references/hub-config.md`; this file covers how the bridge *uses* it.
6
+
7
+ ## Entry
8
+
9
+ ```json
10
+ { "login": "abdelrahmannasr", "name": "alice", "role": "owner" }
11
+ ```
12
+
13
+ - `login` — the GitHub/GitLab username whose review/approval is being mapped.
14
+ - `name` — the SDLC name written to `approvals.json` / `comments.json` (the same names as `epic.md`
15
+ `owner` and `repos.json` `domain_owner`). Keep it stable.
16
+ - `role` — the person's default role: `owner` or `reviewer`. **Not** `domain-owner` — that is derived.
17
+
18
+ ## Resolution
19
+
20
+ 1. **login → name + role** from the roster.
21
+ 2. **domain-owner is derived:** if the resolved `name` equals a repo's `domain_owner` in `repos.json`,
22
+ and that repo is a **touched domain** for the step under review, the bridge also emits a
23
+ `domain-owner` approval scoped to that repo (`domain: <repo>`). One person owning several repos yields
24
+ several `domain-owner` records with different `domain` values — exactly what the gate predicate allows.
25
+ 3. **Unmapped login → reviewer (flagged).** A login not in the roster maps to `name: <login>`,
26
+ `role: reviewer`, with `<!-- unverified login: <login> -->` in the review record. It counts as a
27
+ reviewer but is **never** auto-promoted to owner/domain-owner, so a stranger can never satisfy the
28
+ owner/domain-owner requirement. The marker prompts a human to add the login to the roster.
29
+
30
+ ## Per-repo routing (stories review, and any escalated step)
31
+
32
+ The stories review needs a `domain-owner` per repo in the **union of every story's `repos`**. On the
33
+ review PR the bridge makes this legible and enforceable:
34
+
35
+ - Add a `domain:<repo>` **label** per touched repo.
36
+ - **Request** each touched repo's `domain_owner` login as a reviewer (resolved via the roster).
37
+ - On `sync`, an approval from login *L* maps to `domain-owner` for repo *R* **iff**
38
+ `repos.json[R].domain_owner == roster[L].name`. So a domain owner's approval is scoped to exactly the
39
+ repos they own, and a repo with no approving owner shows up as still-required in the gate report.
40
+
41
+ This is the same touched-domains computation the gate uses (`../sdlc-review-gate/references/gating.md`):
42
+ architecture+contract → `epic.repos`; stories → union of story `repos`.
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bash
2
+ # Hub review routing — the front-half analogue of sdlc-pr-template's risk-route.sh. Reads a hub review
3
+ # PR/MR description's "Impact & Risk (front-half)" block and prints the required reviewers, reusing
4
+ # sdlc-review-gate's rule: base = owner + 1 reviewer; if a risk tag (contract|auth|payments) is set OR
5
+ # the artifact is the stories set, ALSO a domain-owner per touched repo. Advisory: it ROUTES the human
6
+ # review; it does not approve or merge.
7
+ set -euo pipefail
8
+
9
+ BODY="${1:?usage: hub-route.sh <hub-pr-description-file>}"
10
+ [ -f "$BODY" ] || { echo "hub-route: file not found: $BODY" >&2; exit 2; }
11
+
12
+ # Value side of the FIRST line matching a label regex, comments + markdown markers stripped. Tolerant:
13
+ # a missing label yields empty (never aborts) — an advisory helper must still print for a half-filled body.
14
+ value_of() {
15
+ grep -iE "$1" "$BODY" 2>/dev/null | head -1 \
16
+ | sed -E 's/<!--.*$//; s/^[^:]*://; s/[*`]//g; s/^[[:space:]]*//; s/[[:space:]]*$//' || true
17
+ }
18
+
19
+ risk_tags="$(printf '%s' "$(value_of 'Risk tags:')" | tr 'A-Z' 'a-z')"
20
+ repos="$(value_of 'Domains.*touched:')"
21
+ artifact="$(value_of 'Artifact:')"
22
+
23
+ echo "Risk tags: ${risk_tags:-none}"
24
+ echo "Repos touched: ${repos:-unspecified}"
25
+
26
+ escalate=no
27
+ why=""
28
+ case "$risk_tags" in
29
+ *contract*) escalate=yes; why="risk tag: contract" ;;
30
+ esac
31
+ case "$risk_tags" in *auth*) escalate=yes; why="${why:+$why, }risk tag: auth" ;; esac
32
+ case "$risk_tags" in *payments*) escalate=yes; why="${why:+$why, }risk tag: payments" ;; esac
33
+ # The stories review routes per-repo even with no risk tag.
34
+ case "$artifact" in *stories*) escalate=yes; why="${why:+$why, }stories per-repo routing" ;; esac
35
+
36
+ if [ "$escalate" = "yes" ]; then
37
+ echo "ROUTE: ESCALATED (${why}) -> owner + 1 reviewer PLUS one domain-owner approval per touched repo"
38
+ echo " (same escalation as sdlc-review-gate; map each repo to its domain_owner in repos.json)."
39
+ case "$repos" in
40
+ ""|*"<"*|*"…"*|*"|"*)
41
+ echo " (Repos line not filled in — list each touched repo to route the domain owners.)" ;;
42
+ *)
43
+ printf '%s\n' "$repos" | tr ',' '\n' | while IFS= read -r r; do
44
+ r="$(printf '%s' "$r" | sed -E 's/^[[:space:]]*//; s/[[:space:]]*$//')"
45
+ [ -n "$r" ] && echo " - domain-owner: $r"
46
+ done ;;
47
+ esac
48
+ else
49
+ echo "ROUTE: base rule -> owner + 1 reviewer (no domain-owner escalation)."
50
+ fi
@@ -0,0 +1,63 @@
1
+ # sdlc-managed: sdlc-hub-bridge
2
+ # Event-driven gate sync for the PRODUCT HUB. When a reviewer approves, requests changes, or a
3
+ # human merges a review/EP-* PR, this workflow runs `sdlc gate ci`, which maps the platform state
4
+ # into the file ledger (epics/<epic>/.sdlc/*.json + reviews/*.md) and commits ONLY those ledger
5
+ # files to the default branch. It never approves and never merges — the merge click remains the
6
+ # human approval act; the gate predicate is the same one `sdlc gate sync` runs locally.
7
+ #
8
+ # Loop prevention: neither `pull_request_review` nor `pull_request` fires on a push to the default
9
+ # branch, and the ledger commit carries [skip ci] to guard every other workflow.
10
+ #
11
+ # Protected default branch? The github.token push will fail (visibly — the run goes red and manual
12
+ # `sdlc gate sync` still works). Options, in order of preference:
13
+ # a) add a ruleset bypass so GitHub Actions may push to the default branch, or
14
+ # b) store a fine-grained PAT as the SDLC_GATE_TOKEN secret and pass it to actions/checkout via
15
+ # `with: { token: ${{ secrets.SDLC_GATE_TOKEN }} }` — the one exception to the bridge's
16
+ # no-stored-tokens rule; see skills/sdlc-hub-bridge/references/bridge.md.
17
+ name: sdlc-gate-sync
18
+ on:
19
+ pull_request_review:
20
+ types: [submitted, dismissed]
21
+ pull_request:
22
+ types: [closed, synchronize] # closed -> merged advance; synchronize -> prompt revoke-on-change
23
+
24
+ permissions:
25
+ contents: write # push the ledger commit
26
+ pull-requests: read # gh pr view + reviewThreads GraphQL
27
+
28
+ # Serialize runs repo-wide so ledger pushes never race; sync reads the FULL platform state each
29
+ # time, so a queued superseded run loses nothing.
30
+ concurrency:
31
+ group: sdlc-gate-sync
32
+ cancel-in-progress: false
33
+
34
+ jobs:
35
+ sync:
36
+ if: >
37
+ startsWith(github.event.pull_request.head.ref, 'review/EP-') &&
38
+ (github.event_name == 'pull_request_review' ||
39
+ github.event.action == 'synchronize' ||
40
+ github.event.pull_request.merged == true)
41
+ runs-on: ubuntu-latest
42
+ env:
43
+ GH_TOKEN: ${{ github.token }}
44
+ steps:
45
+ # Check out the BASE branch (not the PR merge ref): the ledger lives there, and `gate ci`
46
+ # overlays the artifact from the head ref itself so approvals bind to the reviewed content.
47
+ # A head branch auto-deleted on merge is fine: the event payload still carries head.ref (the
48
+ # string `gate ci` parses), and a failed fetch just skips the overlay — by then the artifact
49
+ # is already on the base branch via the merge.
50
+ - uses: actions/checkout@v4
51
+ with:
52
+ ref: ${{ github.event.pull_request.base.ref }}
53
+ fetch-depth: 0
54
+ - uses: actions/setup-node@v4
55
+ with:
56
+ node-version: "20"
57
+ - name: Sync the gate ledger
58
+ run: |
59
+ git config user.name "sdlc-gate-sync[bot]"
60
+ git config user.email "sdlc-gate-sync[bot]@users.noreply.github.com"
61
+ npx -y -p yadflow@1 sdlc gate ci \
62
+ --branch "${{ github.event.pull_request.head.ref }}" \
63
+ --pr "${{ github.event.pull_request.number }}"
@@ -0,0 +1,7 @@
1
+ # SDLC gate sync — minimal root pipeline for the PRODUCT HUB.
2
+ # Written by the sdlc-hub-bridge `wire` action ONLY when the hub has no existing root
3
+ # `.gitlab-ci.yml`. The job itself lives in the included fragment so the same single source is
4
+ # reused whether or not a root pipeline already exists. If a root later grows real jobs, they
5
+ # coexist with the included sdlc-* job (which carries `needs: []` and no `stage:`).
6
+ include:
7
+ - local: '.gitlab/ci/sdlc-gate-sync.yml'
@@ -0,0 +1,64 @@
1
+ # sdlc-managed-include: sdlc-hub-bridge
2
+ # Event-driven gate sync for the PRODUCT HUB, as an INCLUDABLE fragment. Pulled into the hub's
3
+ # root .gitlab-ci.yml via:
4
+ # include:
5
+ # - local: '.gitlab/ci/sdlc-gate-sync.yml'
6
+ # so wiring never edits the foreign root pipeline beyond that one include line. The job carries
7
+ # `needs: []` and no `stage:` (same merge-safety as the sdlc-checks fragment).
8
+ #
9
+ # What it does: run `sdlc gate ci`, which maps MR approvals / change-request discussions / the
10
+ # merge into the file ledger (epics/<epic>/.sdlc/*.json + reviews/*.md) and commits ONLY those
11
+ # ledger files to the default branch. It never approves and never merges — the merge click remains
12
+ # the human approval act.
13
+ #
14
+ # GitLab is the DEGRADED path, stated honestly: GitLab fires no pipeline on an approval alone.
15
+ # - MR events (open / push to the review branch) and the merge are picked up near-immediately.
16
+ # - Approvals are only seen by the SCHEDULED sweep. Create a pipeline schedule (one-time, cannot
17
+ # be committed as code): cron `*/15 * * * *` with variable SDLC_GATE_SYNC=true — so an approval
18
+ # can take up to ~15 minutes to reach the ledger. UI: CI/CD > Schedules, or:
19
+ # glab api projects/:id/pipeline_schedules -X POST \
20
+ # -f description='sdlc gate sync' -f ref=main -f cron='*/15 * * * *'
21
+ # glab api "projects/:id/pipeline_schedules/<id>/variables" -X POST \
22
+ # -f key=SDLC_GATE_SYNC -f value=true
23
+ #
24
+ # Token (the one documented bend of the bridge's no-stored-tokens rule): CI_JOB_TOKEN can neither
25
+ # read the approvals API nor push to a protected branch. Create a PROJECT ACCESS TOKEN with
26
+ # `read_api` + `write_repository` (role Developer+, allowed to push to the default branch) and
27
+ # store it as a masked CI/CD variable SDLC_GATE_TOKEN. Without it the job fails visibly and
28
+ # manual `sdlc gate sync` remains the fallback.
29
+ variables:
30
+ GIT_DEPTH: "0" # full history: gate ci fetches the review branch and pushes the ledger
31
+
32
+ sdlc-gate-sync:
33
+ needs: []
34
+ image: node:20
35
+ rules:
36
+ # MR-event path: fires on MR open / push to the review branch — NOT on approval (see header).
37
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^review\/EP-/
38
+ # Merge path: branch pipeline on the default branch whose merge commit names a review branch.
39
+ # Squash/fast-forward merges may omit the branch name — those advance on the next scheduled sweep.
40
+ - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_COMMIT_MESSAGE =~ /review\/EP-/
41
+ # Scheduled sweep: the only path that picks up approvals.
42
+ - if: $CI_PIPELINE_SOURCE == "schedule" && $SDLC_GATE_SYNC == "true"
43
+ script:
44
+ # Pinned glab binary (node:20 has no glab). Alternative: image registry.gitlab.com/gitlab-org/cli.
45
+ - GLAB_VERSION=1.55.0
46
+ - curl -fsSL "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/glab_${GLAB_VERSION}_linux_amd64.deb" -o /tmp/glab.deb && dpkg -i /tmp/glab.deb
47
+ # The ledger lives on the default branch; gate ci overlays the artifact from the review branch itself.
48
+ - git fetch origin "$CI_DEFAULT_BRANCH"
49
+ - git checkout -B "$CI_DEFAULT_BRANCH" "origin/$CI_DEFAULT_BRANCH"
50
+ - git config user.name "sdlc-gate-sync" && git config user.email "sdlc-gate-sync@noreply.${CI_SERVER_HOST}"
51
+ - git remote set-url origin "https://oauth2:${SDLC_GATE_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
52
+ - export GITLAB_TOKEN="$SDLC_GATE_TOKEN" GITLAB_HOST="$CI_SERVER_URL"
53
+ - |
54
+ # Merge pushes have no CI_MERGE_REQUEST_IID — derive the just-merged review branch from the
55
+ # merge commit message so the merge advances in event mode; the scheduled sweep stays the
56
+ # catch-all (squash/FF merges whose message omits the branch land there).
57
+ REVIEW_BRANCH="$(printf '%s' "$CI_COMMIT_MESSAGE" | grep -oE 'review/EP-[A-Za-z0-9._/-]+' | head -n1 || true)"
58
+ if [ -n "$CI_MERGE_REQUEST_IID" ]; then
59
+ npx -y -p yadflow@1 sdlc gate ci --branch "$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME" --pr "$CI_MERGE_REQUEST_IID"
60
+ elif [ -n "$REVIEW_BRANCH" ]; then
61
+ npx -y -p yadflow@1 sdlc gate ci --branch "$REVIEW_BRANCH"
62
+ else
63
+ npx -y -p yadflow@1 sdlc gate ci
64
+ fi
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: sdlc-implement
3
+ description: 'Build-half Step B of the gated SDLC. With the dev lens, implement ONE atomic task from a story''s Spec Kit tasks.md as a small diff (≤3 files) on its own branch in the code repo. The diff stays inside the files the task declared — flag and STOP if it would grow beyond them. Commit per convention, ending with the task ID; add Contract-Change: yes only if the diff touches the locked contract surface (which routes back to the architecture gate). The step never advances itself; it produces a committed branch and hands off to the check gates, which the orchestrator (sdlc-run) may auto-run once `implement` is earned to machine_advance (Phase 4b Step D) — the merge still needs the gates and the engineer review. Use when the user says "implement task <id>" or after a story is spec''d.'
4
+ ---
5
+
6
+ # SDLC — Implement Task (build-half Step B)
7
+
8
+ **Goal:** Turn ONE atomic task from a story's `tasks.md` (produced by Step A `sdlc-spec`) into a small,
9
+ reviewable diff on its own branch in the code repo. **One atomic task = one branch = one PR/MR**
10
+ (build plan §B). This is the **light per-task loop**: it runs once per task; the heavy spec ceremony
11
+ already ran once for the story.
12
+
13
+ This step **never auto-advances**. When the task is implemented and committed, control passes to the
14
+ **check gates** (Step C — `sdlc-checks`: spec-link, contract-check, build/test/lint) and then human/AI
15
+ review (Steps D–E, not built yet). Implementation here produces a branch + commit and stops.
16
+
17
+ The implementing lens is **`dev`** (`bmad-agent-dev`, Amelia). The dev writes only what the task
18
+ declares; it does not redesign, does not widen the contract, and does not pick up sibling tasks.
19
+
20
+ ## Conventions
21
+
22
+ - `{project-root}` resolves from the project working directory — the **product** repo.
23
+ - The work happens **inside the code repo** (a separate git repo) at
24
+ `{project-root}/demo-repos/<repo>/` (`config.yaml` `build.code_repos_root`). Use absolute paths.
25
+ - **Branch name:** `feat/<story-id>-<task-id>-<short-slug>` (e.g.
26
+ `feat/EP-istifta-inquiries-S01-T01-create-inquiry`). Branched off the code repo's default branch.
27
+ - **Commit message:** a conventional subject, body describing the change, and a **required `Task:`
28
+ trailer** (e.g. `Task: EP-istifta-inquiries-S01-T01`) in the trailer block. Add `Contract-Change: yes`
29
+ **only** if the diff touches the locked contract surface (see Step 5), and a per-commit
30
+ `Co-Authored-By:` for any AI tool that helped author the diff (the human author owns the commit;
31
+ trailer order `Task:` → `Contract-Change:` → `Co-Authored-By:`). The skill installs a `.gitmessage`
32
+ template that scaffolds these (Step 2).
33
+ - Speak in the configured `communication_language`; write code/comments in `document_output_language`.
34
+
35
+ ## Inputs
36
+
37
+ - `epic` — the `EP-<slug>` (ask if not provided).
38
+ - `story` — the `EP-<slug>-S0N` whose spec is being implemented (ask if not provided).
39
+ - `repo` — the code repo the story's spec lives in (one of the story's `repos`).
40
+ - `task` — the ONE atomic task ID to implement, e.g. `T01` (ask if not provided).
41
+
42
+ ## On Activation
43
+
44
+ ### Step 1 — Resolve the spec and the task
45
+ Read `demo-repos/<repo>/specs/<story>/tasks.md`. Find the block for `<task>` (`## <task> — …`). Read
46
+ its one-line goal and its **Files:** list (the declared file boundary) and the acceptance criterion it
47
+ satisfies. If the task ID is not in `tasks.md`, STOP. Confirm the spec exists (Step A ran); if not,
48
+ STOP and point at `sdlc-spec`.
49
+
50
+ ### Step 2 — Resolve the code repo and branch
51
+ Confirm `demo-repos/<repo>/` is its own git repo (`.git` present). From its default branch, create the
52
+ task branch `feat/<story>-<task>-<short-slug>`. If a branch for this task already exists, reuse it
53
+ rather than forking a second one (one task = one branch).
54
+
55
+ **On the first implement in a repo, install the commit template** (idempotent): copy this skill's
56
+ `templates/.gitmessage` to `<repo>/.gitmessage` and run `git -C <repo> config commit.template .gitmessage`
57
+ so every commit is pre-scaffolded with the `Task:` trailer and the commented per-commit `Co-Authored-By:`
58
+ choices (`config.yaml` `build.ai_coauthor.allowed`). Do the same at the hub root for hub commits. Skip if
59
+ already configured.
60
+
61
+ ### Step 3 — Read the spec inputs (do NOT re-derive the contract)
62
+ Read the story's `spec.md`, `plan.md`, `data-model.md`, and `contracts/` for this task's context. The
63
+ contract slice is **quoted** from the product repo's locked `contract.md` — implement to it, never
64
+ change it here.
65
+
66
+ ### Step 4 — Implement the task (dev lens), inside the declared files ONLY
67
+ Adopt the **dev** lens. Implement the task's goal so it satisfies its acceptance criterion, touching
68
+ **only the files in the task's `Files:` list** (≤3 where possible). Write real, working code — tests
69
+ must exercise behavior, not just pass (build plan §C). **Before committing, run a smoke or test that
70
+ actually exercises the task's acceptance criterion** — the task is not done until the criterion is
71
+ demonstrably met (record the result in the Step 7 report).
72
+
73
+ **File-boundary rule (hard):** if the implementation genuinely needs a file **not** in the declared
74
+ list, **flag and STOP** — do not silently widen the diff. Report the extra file(s) needed so the
75
+ task's spec can be corrected (re-run `sdlc-spec` / re-scope the task) before implementing. A task whose
76
+ declared files are wrong is a spec bug, not an implementation decision.
77
+
78
+ This stop is a **scope overrun** — a halt condition that pulls in a human regardless of any automation
79
+ dial. When this step is driven by the orchestrator (`sdlc-run`, Phase 4), the stop must be legible to
80
+ it: mark the `implement` step `status: blocked` in `build-state/<story>.json` and surface
81
+ `scope_overrun: true` so the run records a `rejected` trust entry and halts (it never advances past a
82
+ boundary breach). The same applies to the Step 5 contract-surface stop (`contract_touch: true`).
83
+
84
+ ### Step 5 — Contract-surface check (local pre-flight for Step C)
85
+ Determine whether the diff touches the **locked contract surface** (the API/event/data-model shapes in
86
+ `epics/<epic>/contract.md`'s `CONTRACT-SURFACE` block). Normal implementation **consumes** the
87
+ contract (e.g. implementing `POST /inquiries` to the agreed shape) — that is **not** a contract change.
88
+ A contract change means the diff alters the agreed cross-repo shape itself.
89
+
90
+ - If the diff does **not** change the surface: proceed; no `Contract-Change` trailer.
91
+ - If the task **requires** changing the surface: **flag and STOP**. The contract is owned upstream;
92
+ route back to the **architecture gate** to amend and re-lock `contract.md` first. Only then return
93
+ here, and record `Contract-Change: yes` in the commit body (the Step C contract-check will require a
94
+ matching, already-updated contract).
95
+
96
+ ### Step 6 — Commit on the task branch
97
+ Stage only the declared files. Commit with the convention: a conventional subject, a short body, and a
98
+ `Task: <story>-<task>` trailer (plus `Contract-Change: yes` if Step 5 applies). The human author owns the
99
+ commit; if an AI tool helped author this diff, add its `Co-Authored-By:` line from
100
+ `config.yaml` `build.ai_coauthor.allowed` (the `.gitmessage` template scaffolds the choices). Keep all
101
+ trailers in one contiguous block. Do not commit sibling tasks' work.
102
+
103
+ ### Step 7 — Report; the advance decision belongs to the dial (Phase 4)
104
+ Report: the branch name, the files changed, how the change satisfies the task's acceptance criterion,
105
+ the result of any test/smoke run, and the next action — the **check gates** (Step C — `sdlc-checks`)
106
+ then the PR and review (Steps D–E). Do **not** open a PR, merge, or hand-edit the epic's front-half
107
+ `state.json`. Step B ends at a committed task branch.
108
+
109
+ - **Run standalone:** stop here; a human triggers the gates.
110
+ - **Run by the orchestrator** (`sdlc-run`): this skill still just produces the committed branch and
111
+ signals success (or a scope/contract halt). The orchestrator records the `implement` step's status
112
+ and trust entry and, when `implement` is earned to `machine_advance` (Step D, Phase 4b), **auto-runs
113
+ the check gates** instead of waiting for a manual nudge. The diff still cannot merge without the
114
+ gates passing and the engineer review — Step D removes only the "now run the gates" hand-off.
115
+
116
+ ### Step 8 — Record the `tasks` trust signal on first consume (Phase 4b)
117
+ Resolving a task from `tasks.md` (Step 1) is the moment the generated task list "survives contact" —
118
+ the evidence that could later earn the `tasks` step a `machine_advance`. When driven by `sdlc-run`,
119
+ finalize a `tasks` trust entry, anchored to what the human/dev actually did with the list:
120
+ - the task is implemented with its declared `Files:`/scope **as generated** → `approved-unchanged`;
121
+ - the task is **re-scoped** first (its `Files:`/boundary edited) → `approved-with-edits`
122
+ (signal `task_rescoped: true`);
123
+ - the task list is discarded / regenerated → `rejected`.
124
+ Append the entry to `epics/<epic>/.sdlc/trust-log.json` (schema:
125
+ `../sdlc-author-epic/references/state-schema.md`). `tasks` stays `human_approve` until its slice clears
126
+ the threshold — this only *gathers* evidence. (The `implement` step's own verdict is finalized later,
127
+ at the engineer review in `sdlc-ship`: merged as authored → `approved-unchanged`; edited first →
128
+ `approved-with-edits`; scope/contract/checks halt → `rejected`.)
129
+
130
+ ## Hard rules (build plan §B, Cross-cutting)
131
+
132
+ - **One atomic task = one branch = one PR/MR.** Never bundle tasks; never exceed the declared files.
133
+ - **Light loop per task.** Do not re-run the heavy spec ceremony; that already ran once in Step A.
134
+ - **Never widen the contract here.** Surface changes go back to the architecture gate (Step 5).
135
+ - **The step never advances itself.** A scope overrun or contract touch always halts. The
136
+ `implement → checks` hand-off advances only when the orchestrator's `implement` dial is
137
+ `machine_advance` (earned, Step D) — and never past the engineer review, which is always human.
138
+ Standalone, the step stops at a committed branch.
139
+
140
+ ## Reference
141
+ - Branch/commit conventions, the file-boundary rule, the Contract-Change rule: `references/implement-conventions.md`.
142
+ - The task list this step consumes: Step A's `references/spec-handoff.md` (`../sdlc-spec/...`).
143
+ - Contract surface + hash recipe: `../sdlc-author-architecture/references/contract-format.md`.
@@ -0,0 +1,103 @@
1
+ # Implement conventions — branch, commit, file boundary, contract change
2
+
3
+ Step B (`sdlc-implement`) turns ONE atomic task into ONE branch and ONE commit in the code repo. These
4
+ conventions are what the later steps (check gates §C, PR template §D, review §E) rely on to trace a
5
+ diff back to its task, story, and contract.
6
+
7
+ ## Branch naming
8
+
9
+ ```
10
+ feat/<story-id>-<task-id>-<short-slug>
11
+ ```
12
+
13
+ - `<story-id>` — the permanent story ID, e.g. `EP-istifta-inquiries-S01`.
14
+ - `<task-id>` — the atomic task ID from `tasks.md`, e.g. `T01`.
15
+ - `<short-slug>` — 2–4 hyphenated words naming the change, e.g. `create-inquiry`.
16
+
17
+ Example: `feat/EP-istifta-inquiries-S01-T01-create-inquiry`. Branched off the code repo's default
18
+ branch. One task = one branch; never reuse a branch for a different task, never fork a second branch
19
+ for the same task.
20
+
21
+ ## Commit message
22
+
23
+ ```
24
+ <type>: <subject ending with what changed>
25
+
26
+ <body — what and why, 1–3 lines>
27
+
28
+ Task: <story-id>-<task-id>
29
+ [Contract-Change: yes]
30
+ [Co-Authored-By: <AI name> <email>]
31
+ ```
32
+
33
+ - The **`<type>` is lowercase** (`feat`, `fix`, `docs`, `refactor`, `test`, `perf`, `build`, `ci`,
34
+ `chore`, `revert`) and the **`<subject>` starts lowercase**, is **imperative**, and has **no trailing
35
+ period** — Conventional Commits (see `CONTRIBUTING.md` and `config.yaml` `build.commit_subject_style`).
36
+ Proper nouns/acronyms keep their case (`fix: refresh OAuth token`). e.g. `feat: add POST /inquiries
37
+ create path`, not `feat: Add POST /inquiries create path.`
38
+ - The **`Task:` trailer is required** (`Task: EP-istifta-inquiries-S01-T01`) — the anchor the spec-link
39
+ check (§C) and the PR (§D) read to connect the diff to its spec and story. It need not be the *last*
40
+ line: the spec-link gate finds it with git's native trailer parser
41
+ (`%(trailers:key=Task)`), which is order-independent. All trailers must sit in **one contiguous block**
42
+ in the last paragraph (no blank lines between them) so git parses them as trailers.
43
+ - **Trailer order:** `Task:` → `Contract-Change:` (if any) → `Co-Authored-By:` (if any), last.
44
+ - `Contract-Change: yes` appears **only** when the diff alters the locked contract surface (see below).
45
+ Omit it for normal implementation.
46
+
47
+ ## Commit ownership & AI co-authors
48
+
49
+ The **human git author owns the commit** (`config.yaml` `build.commit_owner: git_author`). When an AI
50
+ tool assisted, record it **per commit** as a `Co-Authored-By` trailer — the AI is a co-author, **never**
51
+ the author. The owner picks the tool from `config.yaml` `build.ai_coauthor.allowed`:
52
+
53
+ ```
54
+ Co-Authored-By: Claude <noreply@anthropic.com>
55
+ Co-Authored-By: GitHub Copilot <copilot@users.noreply.github.com>
56
+ Co-Authored-By: Cursor <noreply@cursor.com>
57
+ Co-Authored-By: CodeRabbit <noreply@coderabbit.ai>
58
+ ```
59
+
60
+ - Choose the entry whose `id` matches the tool that actually helped author the diff; add more than one
61
+ line if several did. CodeRabbit is a co-author only when it **contributed code**, not when it merely
62
+ reviewed (that is `ai_review` in `sdlc-ship`).
63
+ - For a fully human-authored commit, pick `id: none` — i.e. **omit** the trailer. `ai_coauthor.required`
64
+ is `false`, so a missing trailer is valid and no gate fails on it.
65
+ - `sdlc-implement` installs the `.gitmessage` template (`templates/.gitmessage`) and sets
66
+ `git config commit.template .gitmessage` in the repo, so these lines are pre-scaffolded (commented) for
67
+ the owner to uncomment.
68
+
69
+ ## File-boundary rule (hard stop)
70
+
71
+ Each task in `tasks.md` declares a `Files:` list (≤3 where possible). The implementation diff must stay
72
+ **inside that list**. If the task genuinely needs a file not listed:
73
+
74
+ 1. **Stop.** Do not widen the diff silently.
75
+ 2. Report the extra file(s) needed.
76
+ 3. Treat it as a **spec bug**: the task's declared files were wrong. Correct the task (re-run
77
+ `sdlc-spec` / re-scope) so the boundary is right, then implement.
78
+
79
+ A diff that quietly spreads beyond the declared files is the single easiest way to smuggle unreviewed
80
+ scope past the gates — hence the hard stop.
81
+
82
+ ## Contract-change rule
83
+
84
+ The **locked contract surface** is the cross-repo agreement in `epics/<epic>/contract.md` (the
85
+ `CONTRACT-SURFACE` block, hash-locked at `.sdlc/contract-lock.json`). Distinguish:
86
+
87
+ - **Consuming the contract** (normal) — implementing an endpoint/event/entity to the shape the contract
88
+ already agreed (e.g. building `POST /inquiries` to its agreed request/response). **Not** a contract
89
+ change; no trailer.
90
+ - **Changing the contract** (exceptional) — altering the agreed shape itself (new field crossing repos,
91
+ changed status enum, new shared endpoint). This is **not** an implementation decision. Stop, go back
92
+ to the **architecture gate**, amend and re-lock `contract.md` (which re-escalates that review per the
93
+ contract `risk_tags`), and only then implement — recording `Contract-Change: yes` so the §C
94
+ contract-check finds the matching, already-updated contract.
95
+
96
+ This keeps the contract singular and owned upstream: a code repo can never widen the shared surface
97
+ from inside an implementation branch.
98
+
99
+ ## Why one task at a time
100
+
101
+ Small diffs scoped to declared files are reviewable, revertable, and traceable. The heavy spec ceremony
102
+ (specify→tasks) ran once for the whole story in Step A; Step B is the **light loop** — repeat per task,
103
+ each its own branch and PR, each passing the gates independently.
@@ -0,0 +1,17 @@
1
+ # <type>: <subject — lowercase, imperative, no trailing period>
2
+ # types: feat fix docs refactor test perf build ci chore revert
3
+ #
4
+ # <body — what changed and why, 1–3 lines. Wrap at ~72 cols.>
5
+ #
6
+ # --- Trailers (keep them in ONE contiguous block below, no blank lines between) ---
7
+ # Task: is REQUIRED — it anchors the spec-link gate. Order: Task -> Contract-Change -> Co-Authored-By.
8
+ Task: <EP-slug-S0N-T0N>
9
+ # Contract-Change: yes # ONLY if this diff alters the locked contract surface (routes to architecture gate)
10
+ #
11
+ # Per-commit AI co-author (the HUMAN git author owns the commit; AI is only a co-author).
12
+ # Uncomment the line for the tool that actually helped author this diff (add more than one if needed).
13
+ # Leave all commented for a fully human-authored commit (id: none — the trailer is optional).
14
+ # Co-Authored-By: Claude <noreply@anthropic.com>
15
+ # Co-Authored-By: GitHub Copilot <copilot@users.noreply.github.com>
16
+ # Co-Authored-By: Cursor <noreply@cursor.com>
17
+ # Co-Authored-By: CodeRabbit <noreply@coderabbit.ai>
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: sdlc-pr-template
3
+ description: 'Build-half Step D of the gated SDLC. Detect a code repo''s platform and commit the matching PR/MR template — .github/pull_request_template.md (GitHub) or .gitlab/merge_request_templates/Default.md (GitLab). The template carries an Impact & Risk block; a high risk level (or a touched contract/auth/payments surface) routes the review to domain owners, reusing sdlc-review-gate''s escalation. Includes risk-route.sh to print the required reviewers from a PR body. Never auto-advances. Use when the user says "add the PR template" or "set up the MR template" for a repo.'
4
+ ---
5
+
6
+ # SDLC — PR/MR Template (build-half Step D)
7
+
8
+ **Goal:** Commit the platform-correct PR/MR template into a code repo so every PR/MR carries an
9
+ **Impact & Risk** block and a checklist tied to the check gates. A **high** risk level (or a touched
10
+ contract/auth/payments surface) **routes the review to domain owners** — the same escalation
11
+ `sdlc-review-gate` applies on the front-half gates (owner + 1 reviewer, plus one domain-owner per
12
+ touched domain). This step **never auto-advances**; it sets up the template and the routing helper.
13
+
14
+ ## Conventions
15
+
16
+ - `{project-root}` resolves from the project working directory — the **product** repo (holds the
17
+ canonical templates under this skill).
18
+ - Code repos are separate git repos under `{project-root}/demo-repos/<repo>/`.
19
+ - Canonical sources live in this skill's `templates/`:
20
+ - `templates/github/pull_request_template.md` → installs to `<repo>/.github/pull_request_template.md`
21
+ - `templates/gitlab/merge_request_templates/Default.md` → installs to
22
+ `<repo>/.gitlab/merge_request_templates/Default.md`
23
+ - `templates/checks/risk-route.sh` → installs to `<repo>/checks/risk-route.sh` (advisory routing helper)
24
+ - **Hub variants** (`repo: hub`) — front-half artifact-review PR/MR bodies:
25
+ `templates/hub/github/pull_request_template.md` → `{project-root}/.github/pull_request_template.md`;
26
+ `templates/hub/gitlab/merge_request_templates/Default.md` →
27
+ `{project-root}/.gitlab/merge_request_templates/Default.md`. The hub body carries no `Task:` trailer
28
+ (hub PRs change artifacts, not code); its routing helper is `sdlc-hub-bridge`'s `hub-route.sh`.
29
+ - The Impact & Risk block reuses the conventions of earlier steps: the `Task: <story>-<task>` trailer
30
+ (`sdlc-implement`), the contract surface (`sdlc-author-architecture` / contract-check), and the
31
+ domain-owner escalation (`sdlc-review-gate`).
32
+ - **PR/MR title.** One atomic task = one branch = one PR/MR, so the title **defaults to that task's
33
+ commit subject** and follows the same Conventional Commits style — `<type>: <lowercase imperative
34
+ description, no trailing period>`, proper nouns/acronyms keep their case (`config.yaml`
35
+ `build.pr_title_style`; see `CONTRIBUTING.md`). Because PRs are squash-merged, the title becomes the
36
+ merge commit subject — so it must be the clean, lowercase-after-the-type form, not `Fix: ...` or a
37
+ trailing period.
38
+
39
+ ## Inputs
40
+
41
+ - `repo` — the code repo to add the template to (one of an epic's repos), or `hub` for the product hub.
42
+ - `action` — `wire` (commit the matching template + helper) | `route` (print required reviewers from a
43
+ PR body). Default `wire`.
44
+ - `body` — for `route`: a file holding the PR/MR description to evaluate.
45
+
46
+ ## On Activation
47
+
48
+ ### Step 1 — Resolve the repo and detect the platform
49
+ Map `repo` → `{project-root}/demo-repos/<repo>/` (or the registry `path`). Detect the platform: a GitHub
50
+ remote or `.github/` → GitHub; a GitLab remote or `.gitlab/` → GitLab. If ambiguous, ask. For
51
+ `repo: hub`, the target is `{project-root}` itself and the platform comes from `.sdlc/hub.json`.
52
+
53
+ ### Step 2 — `wire` (drop only the matching template)
54
+ Copy from this skill's `templates/`:
55
+ - GitHub → `templates/github/pull_request_template.md` to `<repo>/.github/pull_request_template.md`.
56
+ - GitLab → `templates/gitlab/merge_request_templates/Default.md` to
57
+ `<repo>/.gitlab/merge_request_templates/Default.md`.
58
+ - **`repo: hub`** → use the `templates/hub/<platform>/…` variants, installed into `{project-root}`'s own
59
+ `.github/`/`.gitlab/`. The hub's routing helper (`hub-route.sh`) is installed by `sdlc-hub-bridge`.
60
+ Drop **only the matching** template (drop both only if the repo genuinely uses both). For code repos also
61
+ install `templates/checks/risk-route.sh` to `<repo>/checks/` (`chmod +x`). If the target already has a
62
+ non-SDLC PR/MR template, do not clobber it — back it up / ask. Commit the template on the repo's default
63
+ branch (shared infrastructure, not a task diff).
64
+
65
+ ### Step 3 — `route` (show who must review)
66
+ Run `bash checks/risk-route.sh <body>` to parse the PR description's Impact & Risk block and print the
67
+ required reviewers:
68
+ - **low | medium** risk → base rule: owner + 1 reviewer.
69
+ - **high** risk (or a contract/auth/payments surface touched) → base rule **plus** one domain-owner
70
+ approval per touched domain — identical to `sdlc-review-gate`'s escalation. The actual approvals are
71
+ recorded by the engineer review (Step E), via `sdlc-review-gate`.
72
+
73
+ ### Step 4 — Stop (no auto-advance)
74
+ Report what was committed (or the routing result). The template and routing are advisory inputs to the
75
+ human review (Step E); they do not approve or merge. Do not touch the epic's `.sdlc/` state.
76
+
77
+ ## Hard rules (build plan §D, Cross-cutting)
78
+
79
+ - **Drop only the matching template** for the detected platform.
80
+ - **High risk routes to domain owners** — the same escalation as the gate; never a separate rule.
81
+ - **Nothing auto-advances.** The template sets up review; the human owns the merge.
82
+
83
+ ## Reference
84
+ - The Impact & Risk block, the risk levels, and the routing rule: `references/risk-routing.md`.
85
+ - The escalation this reuses: `../sdlc-review-gate/SKILL.md` and its `references/gating.md`.
86
+ - The check gates the checklist references: `../sdlc-checks/references/check-gates.md`.
@@ -0,0 +1,54 @@
1
+ # Impact & Risk block and review routing
2
+
3
+ The PR/MR template (Phase 3 build plan §D) carries an **Impact & Risk** block so every change states
4
+ its blast radius before review, and so high-risk changes pull in the right reviewers automatically —
5
+ reusing the escalation `sdlc-review-gate` already applies on the front-half gates.
6
+
7
+ ## The Impact & Risk block
8
+
9
+ ```
10
+ ## Impact & Risk
11
+ - **Domains / repos touched:** <backend | mobile | …>
12
+ - **Contract surface touched:** no
13
+ - **Risk level:** low
14
+ - **Rollback plan:** <how to revert>
15
+ ```
16
+
17
+ - **Domains / repos touched** — the domains this change affects; these are the candidate domain owners
18
+ when the change escalates.
19
+ - **Contract surface touched** — `yes` means the diff changes the shared contract surface. That path is
20
+ governed by `contract-check` (needs `Contract-Change: yes` + a re-locked contract) AND it escalates
21
+ the review (contract is a `sdlc-review-gate` risk tag).
22
+ - **Risk level** — `low | medium | high`. The author's assessment of blast radius.
23
+ - **Rollback plan** — how to revert safely.
24
+
25
+ ## Routing rule (reuses the gate's escalation)
26
+
27
+ | Condition | Required reviewers |
28
+ |-----------|--------------------|
29
+ | `low` / `medium`, no sensitive surface | **base rule:** owner + 1 reviewer |
30
+ | `high`, OR a touched contract/auth/payments surface | base rule **plus one domain-owner approval per touched domain** |
31
+
32
+ This is exactly `sdlc-review-gate`'s rule (`references/gating.md`): the base rule is owner + 1
33
+ reviewer; escalation adds a domain-owner per touched domain. The PR template applies the same logic at
34
+ the code-review boundary so a risky PR cannot be approved by just any two people. The **approvals are
35
+ recorded by the engineer review (Step E) through `sdlc-review-gate`** — the template and `risk-route.sh`
36
+ only *route* (advisory); they never approve or merge.
37
+
38
+ ## risk-route.sh
39
+
40
+ `bash checks/risk-route.sh <pr-description-file>` parses the Impact & Risk block and prints the required
41
+ reviewers. Example (high risk, two domains):
42
+
43
+ ```
44
+ Risk level: high
45
+ Contract surface touched: no
46
+ Domains touched: backend, mobile
47
+ ROUTE: ESCALATED (risk: high) -> owner + 1 reviewer PLUS one domain-owner approval per touched domain
48
+ (same escalation as sdlc-review-gate). Required domain owners:
49
+ - domain-owner: backend
50
+ - domain-owner: mobile
51
+ ```
52
+
53
+ It is **advisory** — not a blocking gate. CI may run it to comment the required reviewers; the human
54
+ review (Step E) still owns the merge.