yadflow 3.0.0 → 3.2.0

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.
@@ -19,6 +19,7 @@ login to an SDLC name + role. It is a single object for the hub itself — the s
19
19
  "git_url": "https://github.com/abdelrahmannasr/yadflow.git",
20
20
  "default_branch": "main",
21
21
  "bridge_enabled": true, // open review PRs/MRs on the hub for front-half reviews
22
+ "review": { "requireEngagement": false }, // Review Companion: false (soft) counts bare approves but nudges; true counts only verified-engagement approvals
22
23
  "detectedAt": "2026-06-08", // last detect-hub run (YYYY-MM-DD)
23
24
  "roster": [
24
25
  { "login": "abdelrahmannasr", "name": "alice", "email": "alice@example.com",
@@ -76,6 +77,15 @@ as the registry). `detect-hub` upserts `hub.json` in place — it is idempotent
76
77
  existing **file-only** flow with no error. The file ledger is the source of truth in both modes.
77
78
  - The master switch `config.yaml` `hub.bridge: false` disables the bridge globally regardless of `hub.json`.
78
79
 
80
+ ## Review Companion engagement (`review.requireEngagement`)
81
+
82
+ `review.requireEngagement` (default `false`) controls the [Review Companion](../../yad-review-companion/SKILL.md)
83
+ engagement gate. Each approval records `engagement: verified | none`. **Soft (`false`):** both count — a
84
+ bare approve still passes but draws a friendly public nudge, so review *quality* is visible without
85
+ blocking. **Strict (`true`):** the predicate counts only `verified` approvals. The signal is gameable by
86
+ design ("visible, not impossible") — it raises the cost of a rubber-stamp, it does not prove a human
87
+ read the artifact. Applies to both the front gate and the back-half engineer review.
88
+
79
89
  ## Git tracking
80
90
 
81
91
  Commit `hub.json` — it is small, reviewable, and carries no secrets (logins and names only, never tokens).
@@ -56,6 +56,7 @@ The gated authoring chain + the reusable review gate (10 steps, or 12 with the o
56
56
  | `yad-stories` | → `stories-review` (per-repo routing) | `stories/EP-<slug>-S0N.md` |
57
57
  | `yad-test-cases` | → `test-cases-review` (parallel, non-blocking) | `test-cases.md`, `test-links.json` |
58
58
  | `yad-review-gate` | the shared gate | `reviews/*.md`, `approvals.json`, `comments.json` |
59
+ | `yad-review-companion` | the fun/visible review layer (trailer/cards/chat + engagement) | `approvals.json` `engagement`, platform trailer/cards |
59
60
  | `yad-hub-bridge` | the platform PR/MR bridge | `hub-prs.json` |
60
61
 
61
62
  ### Path: Build half (`phase: 3-build`)
@@ -39,11 +39,29 @@ a **second set of eyes, never the authority**: it cannot approve or merge. Where
39
39
  (no remote), run an equivalent AI first-pass by hand and capture its notes. Record that the AI review
40
40
  ran; surface its findings to the engineer. Do **not** treat AI approval as a gate.
41
41
 
42
+ Also run the **Review Companion** so the human review is easy and fun: post the 60-sec trailer
43
+ (`yad review trailer --repo <r> --pr <n> --body "<text>"`) and deal the swipe cards / open the grounded
44
+ chat from the bundle (`yad review context --repo <r> --pr <n>` → [yad-review-companion](../yad-review-companion/SKILL.md)).
45
+ Companion comments carry `<!-- yad:noblock -->` (history-only, never block); genuine concerns are posted
46
+ unflagged and block normally.
47
+
48
+ For a **deep, teaching** review instead of a skim, offer the **Pair Review** walkthrough
49
+ ([yad-pair-review](../yad-pair-review/SKILL.md)): `yad review walkthrough --repo <r> --pr <n>` deals an
50
+ ordered, risk-tagged stop-list, and the AI walks the engineer through the change one stop at a time —
51
+ asking questions, answering theirs, until both are satisfied — then records the engineer's review-skill
52
+ growth in the local-only learning log. Still soft: it rides the same `engagement: verified` signal and
53
+ never gates. When a pair session backs the approve, you may set `companion.pair: true` on the ship record.
54
+
42
55
  ### Step 2 — `approve` (the engineer review — the human gate)
43
56
  A human engineer reads the diff **against the spec** (`specs/<story>/`) and the acceptance criteria,
44
57
  and records an approval. Determine the rule from the PR's Impact & Risk block (run
45
58
  `../yad-pr-template/templates/checks/risk-route.sh` on the PR body): base, or escalated to a
46
59
  domain-owner per touched domain. Record each approval; re-evaluate whether the rule is satisfied.
60
+ Record `engagement: verified` when the engineer reviewed through the companion (else `none` for a bare
61
+ approve); `yad review reconcile --epic <id> --repo <r> --pr <n>` stamps it onto the ship record from the
62
+ platform. Soft by default (both count; a bare approve draws `yad review nudge`); only gates when
63
+ `hub.review.requireEngagement: true`. The signal is gameable by design and sits **beside** the CI gates,
64
+ never above them.
47
65
  Recording an approval does **not** ship — shipping is a separate, explicit step. Front-half discipline:
48
66
  the gate talks only through files; refuse to treat AI review as a human approval.
49
67
 
@@ -55,8 +73,8 @@ engineer-review rule is satisfied (Step 2). Then:
55
73
  ```json
56
74
  { "story": "<story>", "task": "<task>", "repo": "<repo>", "branch": "feat/<story>-<task>-…",
57
75
  "pr": "<url|#>", "mergeCommit": "<sha>", "gates": ["spec-link","contract-check","build-test-lint"],
58
- "ai_review": "coderabbit (advisory)", "engineer_review": [{"approver":"<name>","role":"<role>","domain":"<opt>"}],
59
- "risk": "<low|medium|high>", "shippedAt": "<YYYY-MM-DD>" }
76
+ "ai_review": "coderabbit (advisory)", "engineer_review": [{"approver":"<name>","role":"<role>","domain":"<opt>","engagement":"<verified|none>"}],
77
+ "companion": {"trailer":true,"cards":true,"chat":false}, "risk": "<low|medium|high>", "shippedAt": "<YYYY-MM-DD>" }
60
78
  ```
61
79
  - **Update the story state** — when **every** task in `specs/<story>/tasks.md` has a ship record, set
62
80
  the story frontmatter `status: shipped`; otherwise `status: in-build`. The chain
@@ -33,9 +33,10 @@ Append-only. One record per shipped task:
33
33
  "gates": ["spec-link", "contract-check", "build-test-lint"],
34
34
  "ai_review": "coderabbit (advisory)",
35
35
  "engineer_review": [
36
- { "approver": "amelia", "role": "owner" },
37
- { "approver": "carol", "role": "reviewer" }
36
+ { "approver": "amelia", "role": "owner", "engagement": "verified" },
37
+ { "approver": "carol", "role": "reviewer", "engagement": "none" }
38
38
  ],
39
+ "companion": { "trailer": true, "cards": true, "chat": false },
39
40
  "risk": "low",
40
41
  "shippedAt": "2026-06-06"
41
42
  }
@@ -46,6 +47,18 @@ Append-only. One record per shipped task:
46
47
  This is the back-half analogue of the front half's `approvals.json` — files only, no hidden state, so a
47
48
  future service can drive ship by writing the same records.
48
49
 
50
+ **Engagement (the Review Companion).** Each `engineer_review` entry carries `engagement: verified | none`
51
+ — `verified` when the engineer reviewed through the [companion](../../yad-review-companion/SKILL.md)
52
+ (`yad review trailer/context/nudge`, a real trailer/cards/chat session over the diff), `none` for a bare
53
+ approve. The optional `companion` block records which faces ran. It is **soft by default** (both count;
54
+ a bare approve draws a friendly `yad review nudge`); it only gates ship when
55
+ `hub.review.requireEngagement: true`. `yad review reconcile --epic <id> --repo <r> --pr <n>` reads the
56
+ code PR's approvals (with the engagement signal) and stamps them onto the matching ship record — the
57
+ back-half **bridge**, the analogue of `yad gate sync`. The signal is gameable by design ("visible, not
58
+ impossible"): it makes engineer-review quality visible, it does not prove a human read the diff. It sits
59
+ **beside** the CI gates (build/test/lint/contract/verified-commits) — never above them; CI still
60
+ decides machine safety, the merge is still the human act.
61
+
49
62
  ## Story state
50
63
 
51
64
  The story frontmatter `status` reflects build progress:
@@ -35,6 +35,40 @@ glab api projects/:id/merge_requests/:iid/notes # discussion notes (com
35
35
  All commands run as the **local user**; the bridge stores no tokens. If the CLI is missing/unauthenticated
36
36
  or the remote is unreachable, the bridge stops and the gate falls back to file-only (no error).
37
37
 
38
+ > **GitLab read parity (GAP-6).** `readPrGitLab` reads approvals (`approved_by[]`) and discussions but
39
+ > does **not** map a "Request changes" reviewer state to `CHANGES_REQUESTED` — on GitLab the blocking
40
+ > signal is an **unresolved discussion**. So on GitLab the gate is held by unresolved threads, not by a
41
+ > reviewer state. (GitHub maps both.) If you need GitLab "Request changes" honored, read
42
+ > `reviewers[].state` from the MR and map it to `CHANGES_REQUESTED`.
43
+
44
+ ## Open recipes (request the reviewers — used by `yad gate open` / `yad open-pr`)
45
+
46
+ Opening the review PR/MR must **request the required reviewers**, or an escalated gate is opened with
47
+ nobody asked. The CLI (`createPr` in `cli/platform.mjs`) does this; an agent opening a PR by hand uses:
48
+
49
+ **GitHub** — create, then add each reviewer (a bad/non-collaborator login WARNS instead of aborting the
50
+ whole create):
51
+ ```bash
52
+ gh pr create --title "review: <artifact> (<epic>)" --body <body> --base <default> --head <branch> \
53
+ --assignee @me --label domain:<repo>
54
+ gh pr edit <n> --add-reviewer <login> # once per required reviewer
55
+ ```
56
+
57
+ **GitLab** — a Free/Core MR carries a **single** reviewer field (multiple reviewers is Premium), so
58
+ assign the first required reviewer and **@-mention the rest in a note** so they are still notified/routed:
59
+ ```bash
60
+ glab mr create --title "review: <artifact> (<epic>)" --description <body> \
61
+ --target-branch <default> --source-branch <branch> --reviewer <first-login> --label domain:<repo> --yes
62
+ glab mr note <iid> -m "Review requested (owner + reviewer rule): @<l2> @<l3> — please review and approve/comment on this MR (this drives the gate)."
63
+ ```
64
+ The read side counts a mentioned reviewer normally: their eventual **approval** still appears in
65
+ `…/approvals → approved_by[]`, and their **note** in `…/discussions` — so the single-reviewer-field cap
66
+ loses only the native "Reviewers" UI chip, not the gate routing.
67
+
68
+ Required reviewers = the hub's `reviewer`/`domain-owner` roster logins for the touched scopes, PLUS any
69
+ repo whose ownership lives only in `repos.json` `domain_owner`/`domain_owners` (those are resolved to a
70
+ login and requested too — otherwise an escalated step is structurally unsatisfiable through routing).
71
+
38
72
  ## Login → role resolution (order)
39
73
 
40
74
  1. Roster (`.sdlc/hub.json`) maps `login` → `name` + base `role` (owner/reviewer).
@@ -62,6 +62,16 @@ PR/MR with the auto-assigned assignee + reviewers.
62
62
  On `high` risk or a contract touch, run `bash checks/risk-route.sh <pr-body>` to print the required
63
63
  domain-owner reviewers — the same escalation `yad-engineer-review` enforces.
64
64
 
65
+ ### Step 3b — Post the review trailer (optional, recommended)
66
+ Make the reviewer's job easy: generate the 60-sec briefing and post it to the new PR/MR so it greets
67
+ every reviewer in the UI (idempotent; safe to re-run after a push):
68
+ ```bash
69
+ yad review trailer --repo <name> --pr <n> --body "<companion-generated briefing>"
70
+ ```
71
+ The full fun-review flow (cards + grounded chat + engagement) is driven by the
72
+ [Review Companion](../yad-review-companion/SKILL.md) during `yad-engineer-review`. Non-blocking by
73
+ design — companion comments carry `<!-- yad:noblock -->`.
74
+
65
75
  ### Step 4 — Stop (no merge)
66
76
  Report the PR/MR URL and the requested reviewers. The PR now runs the check gates (Step C); the human
67
77
  engineer review and merge happen in `yad-engineer-review` (Step E).
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: yad-pair-review
3
+ description: 'The guided, two-way, teaching pair-review walkthrough for the SDLC review gates — the AI-driven companion face. The human opens a PR/MR with an AI session and the AI walks them through the change ONE STOP AT A TIME (highest-risk first), giving comprehensive context per change, then asking the human a Socratic question about it; the human answers and asks back, and both keep going until BOTH declare satisfied. The session doubles as a learning session: it demonstrates a transferable review method, scores the engineer against it, and records their review-skill growth in the local-only yad-learn ledger (rolled up by yad status). Works on the back-half code PR/MR (yad review) and the front-half artifact-review PR/MR (yad gate). Soft and additive — it NEVER blocks a merge or gate; it rides the existing engagement signal and surfaces genuine concerns as normal blocking comments. Use when the user says "pair review this", "walk me through the PR/MR", "review with me", "co-review", or "teach me to review".'
4
+ ---
5
+
6
+ # SDLC — Pair Review (the guided, two-way, teaching walkthrough)
7
+
8
+ **Goal:** turn a review from a solo skim into a **paired session** where the AI is the senior reviewer
9
+ sitting beside a junior engineer. The AI **drives** the review change-by-change, explains each change in
10
+ depth, then **asks the human about it** (flipping the companion's chat direction); the human answers and
11
+ asks back; both keep answering **until both are satisfied**. The PR is the textbook, the walkthrough is
12
+ the lesson — and the engineer walks away having *learned how to review a PR efficiently*, with that
13
+ growth recorded in their personal learning log.
14
+
15
+ This is the **fifth face** of the Review Companion — the AI-driven, bidirectional, teaching layer on top
16
+ of [`yad-review-companion`](../yad-review-companion/SKILL.md) (Trailer / Cards / Chat / Social). The
17
+ **gate still owns the predicate and advancement**; this skill only enriches the *input* and records the
18
+ *engagement* + *learning* signals. Like the companion, **the CLI never calls an LLM — you (this skill)
19
+ generate every briefing, question, and answer, grounded only in real material, and post via the
20
+ platform.**
21
+
22
+ > **Philosophy — "the review is the lesson, and laziness stays visible, not blocked."** Every signal here
23
+ > is soft and gameable by design (same as the companion + learning layers). A pair session never proves a
24
+ > human understood anything and never holds the gate; it makes a real review the *easiest, most useful*
25
+ > path and turns it into mentorship. Say this openly — do not oversell it.
26
+
27
+ ## Conventions
28
+
29
+ - `{project-root}` resolves from the project working directory — the **product hub**.
30
+ - Back half (code PR/MR): grounded by `yad review walkthrough --repo <r> --pr <n>`.
31
+ - Front half (artifact-review PR/MR): grounded by `yad gate walkthrough <epic> [artifact]`.
32
+ - The transferable review method + scorecard live in `references/review-rubric.md`.
33
+ - The session-record comment shape, the dual sign-off, and the learning record this writes live in
34
+ `references/session-state.md` (it reuses [`yad-learn`](../yad-learn/SKILL.md)'s ledger + gitignore
35
+ discipline **verbatim** — the learning output is **local-only, never committed or pushed**).
36
+ - Speak in the configured `communication_language`; write any rendered tutorial in
37
+ `document_output_language`.
38
+
39
+ ## Inputs
40
+
41
+ - Back half: `repo` + `pr`. Front half: `epic` + `artifact`.
42
+ - `member` — the learner being paired with (default: the invoking user). Used for the learning record.
43
+ - `action` — `walkthrough` (the full session, default) | `record` (just write the session comment +
44
+ learning record from an already-finished session) | `rubric` (print the review method and stop).
45
+
46
+ ## On Activation (`action: walkthrough`)
47
+
48
+ ### Step 1 — Get the ordered stops (the grounding)
49
+ Run the walkthrough grounding for the half you're on:
50
+ - Back half: `yad review walkthrough --repo <r> --pr <n>` → prints the grounding bundle **plus an ordered
51
+ `stops[]`** (the code diff parsed into hunk-anchored, risk-tagged review stops, highest-risk first).
52
+ - Front half: `yad gate walkthrough <epic> [artifact]` → the same, over the artifact's review diff.
53
+
54
+ **Read the real material yourself** — run the bundle's `diffCmd`, and read the named `codeMap` / `pack` /
55
+ `contract` / `artifactPath` / `specs/<story>/` files. Never invent content. If a stop's material isn't
56
+ available, say so at that stop (a gap is a finding — see Hard rules).
57
+
58
+ ### Step 2 — Set the frame (teach the method first)
59
+ Briefly state the **review method** you'll both follow (from `references/review-rubric.md`): spec-first →
60
+ contract-surface → high-risk hunks first → per-change correctness · tests · edge cases · security/auth/
61
+ payments → tests-cover-the-change → decide. Tell the human you'll walk the change in that order and ask
62
+ them to apply each step with you. This framing is the lesson scaffold.
63
+
64
+ ### Step 3 — Walk the stops, one at a time (the two-way loop)
65
+ For **each stop in `stops[]` order** (highest-risk first):
66
+ 1. **Comprehensive briefing.** Explain *what* changed in this hunk, *why* (tie it to the spec / epic /
67
+ contract), *how it fits* the surrounding code (use the code-map), and *where the risk is* (call out the
68
+ stop's `riskTags` — `contract`/`auth`/`payments`/`tests`). Cite real file + line ranges.
69
+ 2. **Socratic question.** Ask the human ONE focused question that applies a rubric step to *this* change —
70
+ e.g. "this touches the `auth` surface — what could a malicious caller do here, and does the change
71
+ guard it?" or "which test covers this branch, and what edge case is still uncovered?"
72
+ 3. **Two-way until satisfied with the stop.** The human answers and may ask their own questions; you
73
+ answer **only from real material**, citing lines. Coach — if they miss a rubric angle, surface it and
74
+ explain *how* an efficient reviewer would have caught it. The stop closes when **both** of you are
75
+ satisfied with it (no open concern, the human has engaged the change).
76
+ 4. **Capture the moment** for the scorecard: which rubric step this stop exercised, and whether the human
77
+ nailed it, needed a nudge, or missed it (feeds the learning `comprehension` signal).
78
+
79
+ A **genuine concern** found at any stop (a real bug, a missing test, an unguarded surface) is posted as a
80
+ **normal, unflagged** PR/MR comment so it **blocks** like any reviewer's note — exactly the companion
81
+ rule. Do not bury a real finding inside the session log.
82
+
83
+ ### Step 4 — Dual sign-off (both satisfied)
84
+ After the last stop:
85
+ - **AI sign-off (your verdict).** State plainly: did the human demonstrate understanding across the
86
+ rubric? Are there any unresolved blocking concerns? "Satisfied" from you means *no unresolved blocking
87
+ concern remains*.
88
+ - **Human sign-off.** The human decides: **approve** or **request changes**. When they approve through
89
+ this session, submit the approval carrying the engagement marker so the gate records
90
+ `engagement: verified` — back half: `gh pr review <n> --approve --body "<note>\n\n<!-- yad:engagement verified -->"`
91
+ (GitLab: `glab mr approve <n>` then a note with the marker); front half: the human approves via
92
+ [`yad-review-gate`](../yad-review-gate/SKILL.md) the normal way.
93
+
94
+ "**Both satisfied**" = the human approved **and** your verdict holds no unresolved blocking concern. If
95
+ either is not satisfied, the loop continues (more stops, or the human requests changes and the owner
96
+ addresses them) — nothing advances on a half-finished session.
97
+
98
+ ### Step 5 — Record the session (twice) — see `references/session-state.md`
99
+ 1. **Session comment (PR/MR history).** Post one comment built by the CLI helper `pairSessionBody`
100
+ (carries `<!-- yad:pair -->` so `yad status` can count paired reviews, and `<!-- yad:noblock -->` so it
101
+ never holds the gate): the transcript summary, the **review-skill scorecard**, your AI verdict, and
102
+ both sign-offs. Post it with the platform CLI (`gh pr comment` / `glab mr note`).
103
+ 2. **Learning record (local-only).** Append a `yad-learn` record for the `member`: `concept` =
104
+ `review <repo> PR #<n> — <title>` (front half: `review <artifact> (<epic>)`), `stage` =
105
+ `engineer-review` (back) / `<artifact>-review` (front), `mode` = `deep` (or `quiz` when you scored
106
+ comprehension), `comprehension` = the scorecard roll-up, `tutorial` = a rendered
107
+ `learning/<member>--review-<pr>.md` capturing the method as applied to this PR + the engineer's gaps.
108
+ **First ensure the hub `.gitignore` covers the learning paths** (reuse yad-learn's guard), then write —
109
+ these are personal, gitignored, **never committed or pushed**. The growth rolls up under `yad status`
110
+ "My skills".
111
+
112
+ ## Hard rules
113
+
114
+ - **Never a gate.** This skill never moves `currentStep`, never records an approval on the human's
115
+ behalf, and never merges. It enriches the input and rides the existing soft `engagement` signal only.
116
+ Strict mode (`hub.review.requireEngagement`) is the gate's switch, not this skill's.
117
+ - **The CLI never calls an LLM.** The sequencer (`stops[]`) and the markers are deterministic; *you*
118
+ generate every briefing, question, and answer. Same split as the companion.
119
+ - **Grounded only in real material.** Briefings/answers come from the diff + artifact + contract +
120
+ code-map/pack + specs. If the material can't answer something, **say so — that gap is itself a finding**
121
+ and is posted as a genuine, blocking comment, not fabricated over.
122
+ - **Real concerns block; the session log never does.** Genuine findings are posted **unflagged**; the
123
+ session comment carries `<!-- yad:pair -->` + `<!-- yad:noblock -->` and is permanent history only.
124
+ - **The learning output is local-only.** Reuse yad-learn's gitignore guard before writing; never commit
125
+ or push the records/tutorials, and never write them into a code repo.
126
+ - **You never approve for the human and never merge.** You pair and teach; the human acts.
127
+
128
+ ## File-only mode (no platform)
129
+
130
+ With no hub platform there is no PR to post to: write the session record to
131
+ `reviews/<base>--<date>--pair-session.md` alongside the existing `reviews/*.md`, and the human records
132
+ approval the manual way via [`yad-review-gate`](../yad-review-gate/SKILL.md). The learning record is
133
+ written exactly the same (it is local-only regardless of platform). The session logic is unchanged; only
134
+ the posting surface differs.
135
+
136
+ ## Reference
137
+
138
+ - The transferable review method + scorecard schema: `references/review-rubric.md`.
139
+ - The session comment shape, dual sign-off, and learning record: `references/session-state.md`.
140
+ - The four skim faces this complements: [`yad-review-companion`](../yad-review-companion/SKILL.md).
141
+ - The back-half merge gate it enriches: [`yad-engineer-review`](../yad-engineer-review/SKILL.md).
142
+ - The front-half gate it enriches: [`yad-review-gate`](../yad-review-gate/SKILL.md).
143
+ - The learning layer it records into: [`yad-learn`](../yad-learn/SKILL.md) and its
144
+ `references/learning-state.md`.
@@ -0,0 +1,56 @@
1
+ # The review method + scorecard (the transferable skill)
2
+
3
+ This is the repeatable, efficient PR/MR review method the pair walkthrough demonstrates on the real
4
+ change and scores the engineer against. It is **transferable** — the point is that after a few paired
5
+ sessions the engineer reviews this way on their own. `action: rubric` prints this method and stops.
6
+
7
+ ## The method (the order the walkthrough follows)
8
+
9
+ 1. **Spec first — know what it should do.** Read the acceptance criteria / story / `specs/<story>/`
10
+ before the diff. You can't judge a change you can't measure against its intent.
11
+ 2. **Contract surface — did it move the locked surface?** Map the diff against the locked contract
12
+ (`contract.md` / `contract-lock.json`). A change to the surface without a `Contract-Change` is a
13
+ routing problem, not just a code problem (it must go back to the architecture gate).
14
+ 3. **Risk first — walk the dangerous hunks first.** The grounding orders stops highest-risk first
15
+ (`contract` > `auth`/`payments` > everything; larger hunks before smaller). Spend your attention where
16
+ a mistake costs the most; don't read top-to-bottom.
17
+ 4. **Per change — the four lenses.** For each hunk ask: **correctness** (does it do what the spec says,
18
+ including the unhappy path?), **tests** (is the new behaviour covered?), **edge cases** (nulls, empty,
19
+ concurrency, large input, failure/rollback), **security** (auth/authz, injection, secrets, payments
20
+ integrity) — weight the last two hard on `auth`/`payments`/`contract` stops.
21
+ 5. **Tests cover the change.** A `tests`-tagged stop should map to the behaviour stops. Behaviour with no
22
+ test is a finding; a test that doesn't exercise the new branch is a finding.
23
+ 6. **Decide — approve or request changes.** A clear verdict with the *why*. "Looks good" is not a review;
24
+ name what you checked and what convinced you.
25
+
26
+ ## The scorecard (feeds the learning signal)
27
+
28
+ At each stop, capture which rubric step it exercised and how the engineer did. Roll the stops up into a
29
+ compact scorecard for the session comment and the `comprehension` field of the learning record.
30
+
31
+ Per-step grade (one of):
32
+
33
+ | grade | meaning |
34
+ |-------|---------|
35
+ | ✅ nailed | the engineer applied the step correctly unprompted |
36
+ | 💡 nudged | they got there after a Socratic hint — a learning moment |
37
+ | ⚠️ missed | they didn't catch it; the AI surfaced it and explained how to next time |
38
+ | — n/a | the step didn't apply to this change |
39
+
40
+ `comprehension` for the learning record is a short roll-up, e.g. `4/6 steps nailed, 2 nudged
41
+ (contract-surface, edge-cases)` — honest about where the engineer is still growing. It is a soft,
42
+ gameable signal (a learning aid), never a gate — say so.
43
+
44
+ ## Example scorecard block (rendered into the session comment)
45
+
46
+ ```
47
+ | Rubric step | Grade | Note |
48
+ |------------------------|---------|-----------------------------------------------------|
49
+ | 1 Spec first | ✅ nailed | read the AC before the diff |
50
+ | 2 Contract surface | 💡 nudged | spotted the surface change after a hint |
51
+ | 3 Risk first | ✅ nailed | started on the auth hunk |
52
+ | 4 Four lenses | ⚠️ missed | missed the missing authz check on the new endpoint |
53
+ | 5 Tests cover | ✅ nailed | flagged the uncovered error branch |
54
+ | 6 Decide | ✅ nailed | clear request-changes with the why |
55
+ Comprehension: 4/6 nailed, 1 nudged, 1 missed (four-lenses/security)
56
+ ```
@@ -0,0 +1,82 @@
1
+ # The session record, the dual sign-off, and the learning record
2
+
3
+ The pair walkthrough writes its outcome to **two** places, neither of which is a gate ledger and neither
4
+ of which ever blocks the gate. The gate's predicate (approvals + resolved threads + merge) is untouched.
5
+
6
+ ## 1. The session comment (PR/MR platform history)
7
+
8
+ Built by the CLI helper `pairSessionBody({ summary, scorecard, verdict, humanSignoff, aiSignoff })` in
9
+ `cli/companion.mjs`. It carries **both** markers:
10
+
11
+ - `<!-- yad:pair -->` — so this is countable as a *paired review* in the `yad status` 🏆 roll-up.
12
+ - `<!-- yad:noblock -->` — so the thread is excluded from the gate's blocking check and persists as
13
+ permanent history (a deliberate, unresolved trail), exactly like the companion's card/chat threads.
14
+
15
+ It **never** carries an engagement marker — the session comment is *history*, not the approval. The
16
+ approval is a separate act (Step 4) and carries `<!-- yad:engagement verified -->` on its own.
17
+
18
+ Sections (you generate the prose; the helper composes them):
19
+ - **summary** — what was walked, how many stops, where the risk was, what the human engaged with.
20
+ - **scorecard** — the rubric table + comprehension roll-up from `references/review-rubric.md`.
21
+ - **verdict** — the AI sign-off: understanding demonstrated? any unresolved blocking concern?
22
+ - **humanSignoff / aiSignoff** — the two satisfaction statements ("both satisfied").
23
+
24
+ Post it with the platform CLI (`gh pr comment <n> -b "<body>"` / `glab mr note <n> -m "<body>"`). In
25
+ file-only mode write it to `reviews/<base>--<date>--pair-session.md` instead.
26
+
27
+ ## 2. The learning record (local-only, reuses yad-learn)
28
+
29
+ This is the "review **is** the lesson" half. It reuses [`yad-learn`](../../yad-learn/SKILL.md)'s ledger
30
+ schema and gitignore discipline **verbatim** — see `yad-learn/references/learning-state.md`. Do not
31
+ invent a new store.
32
+
33
+ **Before writing anything**, ensure the **product hub's** `.gitignore` covers the learning paths
34
+ (idempotent — append only if absent), the same block yad-learn uses:
35
+
36
+ ```
37
+ # yadflow learning layer — personal, local-only (never commit or push)
38
+ .sdlc/learning-records.json
39
+ .sdlc/learning/
40
+ epics/*/.sdlc/learning-records.json
41
+ epics/*/learning/
42
+ ```
43
+
44
+ Then append one record to `epics/EP-<slug>/.sdlc/learning-records.json` (or `.sdlc/learning-records.json`
45
+ cross-project), using yad-learn's exact field shape:
46
+
47
+ ```json
48
+ {
49
+ "member": "alice",
50
+ "concept": "review backend PR #42 — add refund endpoint",
51
+ "context": "pair-review walkthrough; risk: payments, contract",
52
+ "stage": "engineer-review",
53
+ "mode": "quiz",
54
+ "tool": "harness-native",
55
+ "sessionId": null,
56
+ "tutorial": "learning/alice--review-42.md",
57
+ "comprehension": "4/6 nailed, 1 nudged, 1 missed (four-lenses/security)",
58
+ "status": "learned",
59
+ "requestedAt": "<YYYY-MM-DD>",
60
+ "completedAt": "<YYYY-MM-DD>"
61
+ }
62
+ ```
63
+
64
+ Field notes:
65
+ - `stage` = `engineer-review` (back half) or `<artifact>-review` (front half, e.g. `architecture-review`).
66
+ - `mode` = `deep` for a walkthrough that didn't score, `quiz` when you captured a comprehension roll-up.
67
+ - `comprehension` = the scorecard roll-up string (null when `mode: deep`).
68
+ - `tool` = `harness-native` (or `deeptutor` if a DeepTutor session backed the tutoring).
69
+ - `status` = `learned` once the session completed (set `completedAt`); `in-progress` if paused.
70
+
71
+ Also render the tutorial artifact `epics/EP-<slug>/learning/<member>--review-<pr>.md` (front-matter:
72
+ `member`, `concept`, `stage`, `tool`, `requestedAt`) — the review method as applied to *this* PR plus the
73
+ engineer's specific gaps and how to close them. Both files are **local-only, gitignored, never committed
74
+ or pushed, and never written into a code repo** — they are a private personal skills log. `yad status`
75
+ rolls them up by stage (e.g. "engineer-review: 3").
76
+
77
+ ## Optional: stamp the build-log (back half)
78
+
79
+ When the task later ships, [`yad-engineer-review`](../../yad-engineer-review/SKILL.md) may record on the
80
+ ship record's `companion` block that a pair session ran: `"companion": { "trailer": true, "cards": false,
81
+ "chat": false, "pair": true }`. This is informational only — it never changes whether the ship is
82
+ allowed.
@@ -45,6 +45,15 @@ if [ -z "$BODY" ] || [ ! -f "$BODY" ]; then
45
45
  exit 1
46
46
  fi
47
47
 
48
+ # The Review Companion injects a `<!-- yad:trailer --> … <!-- /yad:trailer -->` briefing block (and
49
+ # `<!-- yad:noblock -->` notes) into the description. Strip those before the template check so the
50
+ # AI-generated prose can never hide a required section heading or be mistaken for the `Risk level:`
51
+ # value (the trailer is prepended, so an unstripped "risk level" mention would win `head -1`).
52
+ STRIPPED="$(mktemp)"
53
+ trap 'rm -f "$STRIPPED"' EXIT
54
+ sed '/<!-- yad:trailer -->/,/<!-- \/yad:trailer -->/d; /<!-- yad:noblock -->/d' "$BODY" > "$STRIPPED"
55
+ BODY="$STRIPPED"
56
+
48
57
  rc=0
49
58
  require_heading() {
50
59
  if ! grep -qiE "^[[:space:]]*$1[[:space:]]*$" "$BODY"; then
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: yad-review-companion
3
+ description: 'The fun, easy, transparent review companion for the SDLC review gates. Generates a 60-second AI "trailer" of what changed and where the risk is, deals a swipe-through deck of small review "cards", and runs a grounded chat where a reviewer asks anything and their questions become the review record — then records an engagement signal on the approval (verified vs none) and posts a friendly public nudge on a bare rubber-stamp. Works on the front-half artifact-review PR/MR (yad gate) and the back-half code PR/MR (yad review). Use when the user says "review this", "run the companion", "give me the trailer/cards", or wants reviewing to be less of a chore.'
4
+ ---
5
+
6
+ # SDLC — Review Companion (fun & easy, transparent review)
7
+
8
+ **Goal:** make the honest review the *easiest, most enjoyable* path, and make laziness **visible**, not
9
+ blocked. This companion does **not** try to prove a human read something — no quiz, no un-fakeable
10
+ proof (a quiz is answerable by AI, swipes can be spammed). Instead it removes the friction that makes
11
+ reviewers skip, and it makes review *quality* visible to the whole team.
12
+
13
+ > **Philosophy — "visible, not impossible."** Every engagement signal here is gameable by design. It
14
+ > raises the cost of a bare rubber-stamp and shines a light on it; it never claims certainty. State that
15
+ > openly — do not oversell it.
16
+
17
+ This companion is the AI layer on top of [`yad-review-gate`](../yad-review-gate/SKILL.md) (front half)
18
+ and [`yad-engineer-review`](../yad-engineer-review/SKILL.md) (back half). The **gate** still owns the
19
+ predicate and advancement; the companion only enriches the *input* and records the *engagement* field.
20
+ The CLI never calls an LLM — **you** (this skill) generate the text and post it via the platform.
21
+
22
+ ## The four faces
23
+
24
+ 1. **🎬 Trailer (A)** — a 60-second briefing: what this change does, where the risk is (from the step's
25
+ `risk_tags`), and an honest read-time estimate. Posted to the **PR/MR description** as a delimited
26
+ `<!-- yad:trailer --> … <!-- /yad:trailer -->` block so everyone sees it in the UI. Regenerated when
27
+ the artifact changes (it must never lie about the current content).
28
+ 2. **🃏 Cards (B)** — split the change into small **atomic claims**, one per card, each anchored to real
29
+ line numbers / hunks and tagged with risk. Posted as PR/MR comment threads the reviewer skims 👍/🤔.
30
+ 3. **💬 Chat (C)** — a grounded Q&A: the reviewer asks anything; you answer **only from real material**
31
+ (the artifact + diff + contract + the repo `code-map.md`/`pack.md` + sister docs). The reviewer's
32
+ questions and flagged concerns become the review record. If you **cannot** answer from the material,
33
+ say so — that gap is itself a finding (post it as a genuine, blocking comment).
34
+ 4. **🏆 Social (E)** — a verified-engagement mark on a real companion review, a friendly public
35
+ @-mention nudge on a bare approve, and light read-only gamification surfaced by `yad status`.
36
+
37
+ ## Markers (the contract with the gate — all live in PLATFORM data, never a ledger file)
38
+
39
+ - `<!-- yad:trailer -->` / `<!-- /yad:trailer -->` — the trailer block in the PR/MR description.
40
+ - `<!-- yad:noblock -->` — on **every** companion-posted comment (card deck, chat log) and the nudge.
41
+ The gate **excludes** these threads from the blocking check, so they stay unresolved as a permanent
42
+ PR/MR history trail and never hold the gate. A reviewer's *genuine* concern is posted **without** this
43
+ marker, so it blocks normally.
44
+ - `<!-- yad:engagement verified -->` — embedded in the body of a companion-driven **Approve** review.
45
+ A bare UI click has no marker → `engagement: none`. (Definitions live in `cli/companion.mjs`.)
46
+
47
+ ## On activation
48
+
49
+ Inputs: `epic` + `artifact` (front half) **or** `repo` + `pr` (back half); and the `action`
50
+ (`trailer` | `cards` | `chat` | `approve` | `nudge`, default the full flow).
51
+
52
+ 1. **Get the grounding bundle.** Front half: `yad gate review <epic> [artifact]` prints JSON with the
53
+ artifact path, risk tags, PR number, contract path, touched domains, repo code-map paths, and
54
+ `requireEngagement`. Back half: `yad review chat --repo <r>` (see `yad-engineer-review`) provides the
55
+ diff + code-map grounding. Read the named files yourself — never invent content.
56
+ 2. **Trailer.** Generate ≤6 lines (what / risk / read-time), grounded only in the bundle. Post it:
57
+ `yad gate trailer <epic> <artifact> --body "<text>" [--pr <n>]` (idempotent; re-run after edits).
58
+ 3. **Cards.** Produce atomic claims, each citing real lines. Post the deck as a single comment carrying
59
+ `<!-- yad:noblock -->`. A reviewer's 🤔 with a real concern → re-post as a **normal, unflagged**
60
+ comment so it blocks.
61
+ 4. **Chat.** Answer from the material with cited lines. Append the Q&A log as a `<!-- yad:noblock -->`
62
+ comment so it persists in history without blocking. Turn genuine concerns into unflagged comments.
63
+ 5. **Approve.** When the reviewer is satisfied, submit an Approve whose body carries
64
+ `<!-- yad:engagement verified -->` (so the gate records `engagement: verified`). GitHub:
65
+ `gh pr review <n> --approve --body "<note>\n\n<!-- yad:engagement verified -->"`. GitLab: approve,
66
+ then post the marker as a note (`glab mr approve <n>` + `glab mr note <n> -m "…<!-- yad:engagement verified -->"`).
67
+ 6. **Nudge.** A bare approve (engagement `none`) still counts under the soft default; `yad gate sync`
68
+ posts the friendly @-mention automatically. You may also post it directly with the
69
+ `<!-- yad:noblock -->` marker.
70
+
71
+ ## Hard rules
72
+
73
+ - **Never write a ledger file.** Post comments / the trailer / the approval to the PLATFORM (the
74
+ reviewer's own `gh`/`glab`). CI writes `approvals.json`/`comments.json`; the `ledger-guard` check
75
+ rejects a human ledger edit on a review PR. The markers ride in platform data only.
76
+ - **Trailer/cards/chat are grounded ONLY in real material.** If the material does not answer a
77
+ question, say so and record the gap — do not fabricate.
78
+ - **Companion comments never block; genuine concerns always do.** Flag the former with `yad:noblock`;
79
+ leave the latter unflagged.
80
+ - **The engagement signal is gameable and you say so.** It makes review quality visible; it is not
81
+ proof. The strict-mode switch is `hub.review.requireEngagement` (off by default).
82
+ - **The companion never approves on a human's behalf and never merges.** It assists; the human acts.
83
+
84
+ ## File-only mode (no platform)
85
+
86
+ With no hub platform, there is no PR to post to: write the trailer to
87
+ `reviews/<artifact-base>--<date>--trailer.md` and the card/chat notes alongside the existing
88
+ `reviews/*.md`, and the reviewer records approval the manual way (`yad-review-gate` `approve`). The same
89
+ generation logic applies; only the surface changes.
@@ -92,8 +92,12 @@ Repeat comment→address rounds until reviewers are satisfied. **Commenting neve
92
92
 
93
93
  **`approve`** — Record an approval. Append to `.sdlc/approvals.json`:
94
94
  ```json
95
- { "artifact": "<artifact>", "step": "<step id>", "approver": "<name>", "role": "<owner|reviewer|domain-owner>", "domain": "<optional>", "status": "approved", "date": "<YYYY-MM-DD>" }
95
+ { "artifact": "<artifact>", "step": "<step id>", "approver": "<name>", "role": "<owner|reviewer|domain-owner>", "domain": "<optional>", "status": "approved", "date": "<YYYY-MM-DD>", "engagement": "<verified|none>" }
96
96
  ```
97
+ `engagement` records whether the approval came through the [Review Companion](../yad-review-companion/SKILL.md)
98
+ (a real trailer/cards/chat session = `verified`) or as a bare click (`none`). It is soft by default
99
+ (both count; a bare approve draws a friendly nudge) and only gates when `hub.review.requireEngagement`
100
+ is on — see `references/gating.md`. The signal is gameable by design ("visible, not impossible").
97
101
  Also write/refresh `reviews/<artifact-base>--<YYYY-MM-DD>--approved.md` as a **named roster** with three
98
102
  sections, so every participant is attributable in one place:
99
103
 
@@ -12,6 +12,15 @@ Let `A` = the set of `approved` records in `.sdlc/approvals.json` for this step.
12
12
  **Escalated pass** (step `risk_tags` ∩ `{contract, auth, payments}` ≠ ∅): base pass AND, for every
13
13
  touched `domain`, `|domainOwners[domain]| >= 1`.
14
14
 
15
+ **Engagement (the Review Companion).** Each approval carries `engagement: verified | none` —
16
+ `verified` when it was recorded through the companion (a real trailer/cards/chat session), `none` for a
17
+ bare UI click. By **default (soft)** both count: a bare approve still passes the gate but is recorded
18
+ `none` and draws a friendly public @-mention nudge, so review *quality* is visible without blocking
19
+ anyone. When `hub.review.requireEngagement: true`, only `verified` approvals are counted toward the
20
+ sets above (a determined faker can still run an empty session — the signal is **gameable by design**;
21
+ it raises the cost of a rubber-stamp and makes laziness visible, it does not prove a human read the
22
+ artifact). Philosophy: *visible, not impossible.*
23
+
15
24
  **Touched domains** are resolved from files, not hardcoded:
16
25
  - Architecture+contract review: the touched domains are the epic's `repos` (every repo shares the
17
26
  contract surface).
@@ -55,6 +64,15 @@ counterpart to the `reviews/*--comments.md` markdown). It does **not** feed the
55
64
  alone decide the gate — but it makes the `approved.md` roster's "Reviewed / commented by" section
56
65
  attributable, and it is the same shape a future service or the platform bridge can write.
57
66
 
67
+ ## Non-blocking companion comments (`<!-- yad:noblock -->`)
68
+ The Review Companion posts scaffolding comments (the card deck, the chat log) and the social nudge.
69
+ These are fun/interactive aids, **not** review objections, so they must never hold the PR/MR — yet they
70
+ are deliberately **left unresolved** so they remain in the PR/MR history forever (anyone can scroll back
71
+ to see the trailer, cards, chat, and nudges). Every such comment carries a `<!-- yad:noblock -->`
72
+ marker, and the gate **excludes marked threads** from the unresolved-thread blocking check (so it does
73
+ not "resolve to pass" — it ignores them). A reviewer's *genuine* concern is posted **without** the
74
+ marker and blocks normally, exactly as a `CHANGES_REQUESTED` or any unresolved human thread does.
75
+
58
76
  ## Platform-backed input (the bridge)
59
77
  When the hub has a platform (`.sdlc/hub.json`) and the bridge is enabled, reviewers can approve/comment
60
78
  on a real PR/MR instead of (or as well as) the skill recording it directly. `action: sync`