From c3b997e3fdf451f21a7773a40917cb7d0a6af1dc Mon Sep 17 00:00:00 2001 From: Gianluca Carucci Date: Thu, 23 Jul 2026 17:11:41 +0200 Subject: [PATCH 1/5] [#314] feat: wire template-override resolution into skills - add shared skill-conventions/template-resolution.md: file-existence check on .pair/adoption/tech/templates/-template.md (override wins whole), KB path fallback; way-of-working ## Templates = audit trail, not trigger - reference it from all 16 template-linking SKILL.md (dataset) at their template-use sites; regenerate .claude mirror via pair update - record-decision included (#310 merged, mutex cleared) - fix templates.mdx: 19 shipped templates (was 14), accurate file-existence mechanism (drop "AI reads adoption first" hand-wave), branch-template orphan resolved (consumed by publish-pr) - conformance test: every template-linking skill (dataset + mirror) carries the pointer; override-wins/idempotent semantics - Tasks: T1, T2, T3 Refs: #314 Co-Authored-By: Claude Opus 4.8 (1M context) --- .../pair-capability-checkpoint/SKILL.md | 2 +- .../SKILL.md | 2 +- .../SKILL.md | 2 +- .claude/skills/pair-capability-grill/SKILL.md | 2 +- .../pair-capability-map-contexts/SKILL.md | 2 +- .../pair-capability-map-subdomains/SKILL.md | 2 +- .../pair-capability-publish-pr/SKILL.md | 2 +- .../pair-capability-record-decision/SKILL.md | 2 +- .../pair-capability-write-issue/SKILL.md | 2 +- .../skills/pair-process-implement/SKILL.md | 2 +- .../skills/pair-process-plan-epics/SKILL.md | 2 +- .../pair-process-plan-initiatives/SKILL.md | 2 +- .../skills/pair-process-plan-stories/SKILL.md | 2 +- .../skills/pair-process-plan-tasks/SKILL.md | 2 +- .../skills/pair-process-refine-story/SKILL.md | 2 +- .claude/skills/pair-process-review/SKILL.md | 2 +- .pair/knowledge/skill-conventions/README.md | 1 + .../skill-conventions/template-resolution.md | 34 +++++ .../content/docs/customization/templates.mdx | 61 ++++++--- .../knowledge/skill-conventions/README.md | 1 + .../skill-conventions/template-resolution.md | 34 +++++ .../.skills/capability/checkpoint/SKILL.md | 2 +- .../capability/design-manual-tests/SKILL.md | 2 +- .../capability/execute-manual-tests/SKILL.md | 2 +- .../dataset/.skills/capability/grill/SKILL.md | 2 +- .../.skills/capability/map-contexts/SKILL.md | 2 +- .../capability/map-subdomains/SKILL.md | 2 +- .../.skills/capability/publish-pr/SKILL.md | 2 +- .../capability/record-decision/SKILL.md | 2 +- .../.skills/capability/write-issue/SKILL.md | 2 +- .../.skills/process/implement/SKILL.md | 2 +- .../.skills/process/plan-epics/SKILL.md | 2 +- .../.skills/process/plan-initiatives/SKILL.md | 2 +- .../.skills/process/plan-stories/SKILL.md | 2 +- .../.skills/process/plan-tasks/SKILL.md | 2 +- .../.skills/process/refine-story/SKILL.md | 2 +- .../dataset/.skills/process/review/SKILL.md | 2 +- .../conformance/template-resolution.test.ts | 123 ++++++++++++++++++ 38 files changed, 265 insertions(+), 53 deletions(-) create mode 100644 .pair/knowledge/skill-conventions/template-resolution.md create mode 100644 packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/template-resolution.md create mode 100644 packages/knowledge-hub/src/conformance/template-resolution.test.ts diff --git a/.claude/skills/pair-capability-checkpoint/SKILL.md b/.claude/skills/pair-capability-checkpoint/SKILL.md index 6f85f045..39a1ce94 100644 --- a/.claude/skills/pair-capability-checkpoint/SKILL.md +++ b/.claude/skills/pair-capability-checkpoint/SKILL.md @@ -7,7 +7,7 @@ author: Foomakers # /pair-capability-checkpoint — Resumable Progress State -Write and resume a self-contained progress checkpoint so a fresh session (or a subagent) can continue a story exactly where the previous one stopped. Follows the [checkpoint template](../../../.pair/knowledge/guidelines/collaboration/templates/checkpoint-template.md) — five sections: story, branch, tasks done, key decisions, remaining todos. +Write and resume a self-contained progress checkpoint so a fresh session (or a subagent) can continue a story exactly where the previous one stopped. Follows the [checkpoint template](../../../.pair/knowledge/guidelines/collaboration/templates/checkpoint-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)) — five sections: story, branch, tasks done, key decisions, remaining todos. ## Arguments diff --git a/.claude/skills/pair-capability-design-manual-tests/SKILL.md b/.claude/skills/pair-capability-design-manual-tests/SKILL.md index a3b306e4..601ec543 100644 --- a/.claude/skills/pair-capability-design-manual-tests/SKILL.md +++ b/.claude/skills/pair-capability-design-manual-tests/SKILL.md @@ -9,7 +9,7 @@ author: Foomakers Analyze a project's released artifacts, deployment targets, and user-facing surfaces to generate a complete manual test suite. Produces critical path files (`CP*.md`) and a suite `README.md` in the test suite directory. -Each test case follows the [manual-test-case-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-case-template.md). Design principles: [manual-testing.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-testing.md); organizational context: [manual-verification.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-verification.md). +Each test case follows the [manual-test-case-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-case-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)). Design principles: [manual-testing.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-testing.md); organizational context: [manual-verification.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-verification.md). ## Arguments diff --git a/.claude/skills/pair-capability-execute-manual-tests/SKILL.md b/.claude/skills/pair-capability-execute-manual-tests/SKILL.md index a5917a16..7aa84531 100644 --- a/.claude/skills/pair-capability-execute-manual-tests/SKILL.md +++ b/.claude/skills/pair-capability-execute-manual-tests/SKILL.md @@ -7,7 +7,7 @@ author: Foomakers # /pair-capability-execute-manual-tests — Manual Test Suite Executor -Execute a manual test suite against released or deployed artifacts (website, CLI packages, registries). Produces a structured report following the [manual-test-report-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-report-template.md). +Execute a manual test suite against released or deployed artifacts (website, CLI packages, registries). Produces a structured report following the [manual-test-report-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-report-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)). Reads test case files from the project's manual test suite directory; each follows the [manual-test-case-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-case-template.md). Organizational context (who, when, which areas): [manual-verification.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-verification.md); test case design principles: [manual-testing.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-testing.md). diff --git a/.claude/skills/pair-capability-grill/SKILL.md b/.claude/skills/pair-capability-grill/SKILL.md index 96955e86..c3d2e9c6 100644 --- a/.claude/skills/pair-capability-grill/SKILL.md +++ b/.claude/skills/pair-capability-grill/SKILL.md @@ -92,7 +92,7 @@ Systematic AI↔human alignment on a specific story, covering all six aspects of 1. **Act**: Compile recorded answers, explored facts (with source), and flagged assumptions: - `interview` → raw requirements blob (topic, findings per sub-question, open items, assumptions). - - `sync` → alignment synthesis mapped to the six [Sync Coverage Checklist](#sync-coverage-checklist) aspects, ready to drop into the matching [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) sections. + - `sync` → alignment synthesis mapped to the six [Sync Coverage Checklist](#sync-coverage-checklist) aspects, ready to drop into the matching [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)) sections. 2. **Verify**: Synthesis accounts for every answered/explored/assumed item — nothing silently dropped. ### Step 6: Return — Never Write diff --git a/.claude/skills/pair-capability-map-contexts/SKILL.md b/.claude/skills/pair-capability-map-contexts/SKILL.md index 2bde7a38..dee59713 100644 --- a/.claude/skills/pair-capability-map-contexts/SKILL.md +++ b/.claude/skills/pair-capability-map-contexts/SKILL.md @@ -115,7 +115,7 @@ For each approved context in scope: 1. **Check**: Does this context already exist as a file? 2. **Act**: If exists and no conflict was raised → leave untouched; pre-existing relationships without the 3-dimension assessment remain valid as-is (no forced migration — assessed the next time that relationship falls inside a `$scope`). -3. **Act**: If new, or an approved delta applies → create/update the context file following [bounded-context-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/bounded-context-template.md): +3. **Act**: If new, or an approved delta applies → create/update the context file following [bounded-context-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/bounded-context-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill all template sections: Type, Subdomains Covered, Business Scope, Relationships (with the 3-dimension assessment per relationship), Integration Patterns, Data Ownership, Team Alignment, Ubiquitous Language, Quality Attributes, Encapsulated Knowledge, Change Vectors. - File path: `adoption/tech/boundedcontext/[kebab-case-name].md` 4. **Verify**: File created/updated and parseable. Entries outside `$scope` are untouched. diff --git a/.claude/skills/pair-capability-map-subdomains/SKILL.md b/.claude/skills/pair-capability-map-subdomains/SKILL.md index 02c605a2..7b71f1aa 100644 --- a/.claude/skills/pair-capability-map-subdomains/SKILL.md +++ b/.claude/skills/pair-capability-map-subdomains/SKILL.md @@ -104,7 +104,7 @@ For each approved subdomain in scope: 1. **Check**: Does this subdomain already exist as a file? 2. **Act**: If exists and no conflict was raised → leave untouched; pre-existing entries without a `Volatility` field remain valid as-is (no forced migration — the field is added the next time this entry falls inside a `$scope`). -3. **Act**: If new, or an approved delta applies → create/update the subdomain file following [subdomain-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/subdomain-template.md): +3. **Act**: If new, or an approved delta applies → create/update the subdomain file following [subdomain-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/subdomain-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill all template sections: Classification, Volatility, Business Purpose, Key Capabilities, Strategic Importance, Complexity Assessment, Data Ownership, Dependencies, Team Recommendations, Implementation Priority. - File path: `adoption/product/subdomain/[kebab-case-name].md` 4. **Verify**: File created/updated and parseable. Entries outside `$scope` are untouched. diff --git a/.claude/skills/pair-capability-publish-pr/SKILL.md b/.claude/skills/pair-capability-publish-pr/SKILL.md index b1897637..93079fd5 100644 --- a/.claude/skills/pair-capability-publish-pr/SKILL.md +++ b/.claude/skills/pair-capability-publish-pr/SKILL.md @@ -67,7 +67,7 @@ Each phase follows the **check → skip → act → verify** pattern. Phases run ### Phase 3: Compose the PR Body (AC3) -1. **Act**: Read the [pr-template](../../../.pair/knowledge/guidelines/collaboration/templates/pr-template.md). Fill the always-applicable sections from the handoff/story: +1. **Act**: Read the [pr-template](../../../.pair/knowledge/guidelines/collaboration/templates/pr-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md); the same resolution applies to the commit and branch templates referenced above). Fill the always-applicable sections from the handoff/story: - **Title**: `[#] : ` (`` from commit-format / story type). - **Summary** (What Changed + Why) from the story statement and the handoff's decisions. - **Story Context**: link the story issue and list AC coverage. diff --git a/.claude/skills/pair-capability-record-decision/SKILL.md b/.claude/skills/pair-capability-record-decision/SKILL.md index 778e8b55..f69597e0 100644 --- a/.claude/skills/pair-capability-record-decision/SKILL.md +++ b/.claude/skills/pair-capability-record-decision/SKILL.md @@ -88,7 +88,7 @@ A domain decision only becomes a DDR if it meets **all three** criteria. This is 1. **Check**: Does [adoption/tech/adr/](../../../.pair/adoption/tech/adr) directory exist? 2. **Act**: If not, create it. 3. **Check**: Determine the next sequence number — scan existing `adr-NNN-*.md` files, take the highest `NNN`, increment by 1 (zero-padded to 3 digits). If none exist, start at `001`. -4. **Act**: Create (or update) the ADR file at [adoption/tech/adr/](../../../.pair/adoption/tech/adr)`adr-NNN-.md` following the standalone [ADR template](../../../.pair/knowledge/guidelines/collaboration/templates/adr-template.md). Fill in all sections: Status, Date, Context, Options Considered, Decision, Consequences, and Adoption Impact. +4. **Act**: Create (or update) the ADR file at [adoption/tech/adr/](../../../.pair/adoption/tech/adr)`adr-NNN-.md` following the standalone [ADR template](../../../.pair/knowledge/guidelines/collaboration/templates/adr-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md); the same applies to the ADL, DDR, and analysis-log templates below). Fill in all sections: Status, Date, Context, Options Considered, Decision, Consequences, and Adoption Impact. 5. **Verify**: ADR file exists with complete content following the template structure, and sequential numbering is unbroken. #### If `non-architectural` → ADL: diff --git a/.claude/skills/pair-capability-write-issue/SKILL.md b/.claude/skills/pair-capability-write-issue/SKILL.md index 47b5e2af..4e097eac 100644 --- a/.claude/skills/pair-capability-write-issue/SKILL.md +++ b/.claude/skills/pair-capability-write-issue/SKILL.md @@ -44,7 +44,7 @@ Create or update issues in the adopted PM tool. Template-driven: reads the type- ### Step 3: Load Template -1. **Check**: Resolve template path based on `$type`: +1. **Check**: Resolve the template path for `$type` **override-first** — see [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md) (adoption `.pair/adoption/tech/templates/-template.md` wins over the KB default below): - `story` → [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) - `task` → [task-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/task-template.md) - `epic` → [epic-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/epic-template.md) diff --git a/.claude/skills/pair-process-implement/SKILL.md b/.claude/skills/pair-process-implement/SKILL.md index d1d253b8..da7c2751 100644 --- a/.claude/skills/pair-process-implement/SKILL.md +++ b/.claude/skills/pair-process-implement/SKILL.md @@ -233,7 +233,7 @@ Follow the TDD discipline rules strictly, and the [Design Rules](../../../.pair/ **This confirmation is required for EVERY task** — commit-per-task exists precisely to give the developer a checkpoint between tasks. 4. **Verify**: Developer confirms. If changes needed → apply changes, re-run quality (Step 2.7), ask again. -5. **Act**: Stage and commit following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md): +5. **Act**: Stage and commit following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): ```text [#] : diff --git a/.claude/skills/pair-process-plan-epics/SKILL.md b/.claude/skills/pair-process-plan-epics/SKILL.md index 2637c35b..70fb12c9 100644 --- a/.claude/skills/pair-process-plan-epics/SKILL.md +++ b/.claude/skills/pair-process-plan-epics/SKILL.md @@ -103,7 +103,7 @@ Process epics sequentially (Epic 0 first if needed), per its Step 3 proposal: > Epic `[Title]` already exists (#ID). Skipping. -3. **Act**: Draft the epic following [epic-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/epic-template.md): +3. **Act**: Draft the epic following [epic-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/epic-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill template sections: Epic Statement, Business Value, Solution Overview, Epic Breakdown, Technical Considerations. - Present to developer for validation. 4. **Act**: Compose `/pair-capability-write-issue` per the confirmed proposal: diff --git a/.claude/skills/pair-process-plan-initiatives/SKILL.md b/.claude/skills/pair-process-plan-initiatives/SKILL.md index 92938316..4ef4d24d 100644 --- a/.claude/skills/pair-process-plan-initiatives/SKILL.md +++ b/.claude/skills/pair-process-plan-initiatives/SKILL.md @@ -90,7 +90,7 @@ Process initiatives by priority (P0 → P1 → P2). For each initiative: > Initiative `[Title]` already exists (#ID). Skipping. -3. **Act**: Draft the initiative following the [initiative-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/initiative-template.md): +3. **Act**: Draft the initiative following the [initiative-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/initiative-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill template sections with PRD-derived content. - Include business rationale, scope, success metrics, risk assessment, timeline. - Present to developer for validation. diff --git a/.claude/skills/pair-process-plan-stories/SKILL.md b/.claude/skills/pair-process-plan-stories/SKILL.md index 000241db..ed29c475 100644 --- a/.claude/skills/pair-process-plan-stories/SKILL.md +++ b/.claude/skills/pair-process-plan-stories/SKILL.md @@ -94,7 +94,7 @@ For each approved story, per its Step 3 proposal: > Story `[Title]` already exists (#ID). Skipping. -3. **Act**: Define story scope using the Initial Breakdown section of [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md): +3. **Act**: Define story scope using the Initial Breakdown section of [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Story statement (As a / I want / So that). - Rough scope boundaries with expected uncertainty. - Initial sizing: XS(1), S(2), M(3), L(5), XL(8). diff --git a/.claude/skills/pair-process-plan-tasks/SKILL.md b/.claude/skills/pair-process-plan-tasks/SKILL.md index a3412865..2d7f78b6 100644 --- a/.claude/skills/pair-process-plan-tasks/SKILL.md +++ b/.claude/skills/pair-process-plan-tasks/SKILL.md @@ -90,7 +90,7 @@ Transform a refined user story into specific, actionable implementation tasks. T For each task (skipping tasks that already exist in the story body): -1. **Act**: Define the task following the [task-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/task-template.md): +1. **Act**: Define the task following the [task-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/task-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Task Information (ID, priority, hours, bounded context) - Summary and type - Description with implementation context diff --git a/.claude/skills/pair-process-refine-story/SKILL.md b/.claude/skills/pair-process-refine-story/SKILL.md index 10a270ff..975c6828 100644 --- a/.claude/skills/pair-process-refine-story/SKILL.md +++ b/.claude/skills/pair-process-refine-story/SKILL.md @@ -138,7 +138,7 @@ Transform a user story from rough breakdown (Draft) into a development-ready spe ### Step 5: Documentation and PM Tool Update -1. **Act**: Assemble the complete refined story body using the [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) Refined template: +1. **Act**: Assemble the complete refined story body using the [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)) Refined template: - **Functional sections first**: Story Statement → Epic Context → Classification (the Step 3b matrix) → Acceptance Criteria → Definition of Done → Story Sizing → Dependencies → Validation → Notes. - **Technical sections last**: Technical Analysis → (Task Breakdown added later by `/pair-process-plan-tasks`). 2. **Act**: Compose `/pair-capability-write-issue` with: diff --git a/.claude/skills/pair-process-review/SKILL.md b/.claude/skills/pair-process-review/SKILL.md index 8d26f150..9f24edc3 100644 --- a/.claude/skills/pair-process-review/SKILL.md +++ b/.claude/skills/pair-process-review/SKILL.md @@ -191,7 +191,7 @@ Run the procedure for the level determined in Step 3.1 — see [degradation-leve ### Step 5.1: Compile Review Report -1. **Act**: Compile all findings into a review report following the [code-review-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/code-review-template.md): +1. **Act**: Compile all findings into a review report following the [code-review-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/code-review-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - **Review Information**: PR number, author, reviewer, date, story, review type - **Review Summary**: overall assessment, key changes, business value - **Code Review Checklist**: functionality, code quality, technical standards (from Phase 2) diff --git a/.pair/knowledge/skill-conventions/README.md b/.pair/knowledge/skill-conventions/README.md index 36e24116..a500535e 100644 --- a/.pair/knowledge/skill-conventions/README.md +++ b/.pair/knowledge/skill-conventions/README.md @@ -8,6 +8,7 @@ Single-source explanations for patterns that recur across many `SKILL.md` files. | Idempotency | [idempotency.md](idempotency.md) | 28 skills | | Graceful degradation | [graceful-degradation.md](graceful-degradation.md) | 34 skills | | Way-of-working / PM-tool resolution | [way-of-working-pm-resolution.md](way-of-working-pm-resolution.md) | 18 skills | +| Template resolution (adoption override > KB default, file-existence check) | [template-resolution.md](template-resolution.md) | 16 skills | | `/pair-capability-record-decision` invocation contract | [record-decision-contract.md](record-decision-contract.md) | 19 skills | | Output Format shapes (Decision / Report) | [output-shapes.md](output-shapes.md) | 10 skills (documentation-only — no logic change) | | To-issues triage (extend vs create) | [to-issues-triage.md](to-issues-triage.md) | 2 composing skills (new convention — no prior duplication to extract) | diff --git a/.pair/knowledge/skill-conventions/template-resolution.md b/.pair/knowledge/skill-conventions/template-resolution.md new file mode 100644 index 00000000..d99e4bbb --- /dev/null +++ b/.pair/knowledge/skill-conventions/template-resolution.md @@ -0,0 +1,34 @@ +# Template Resolution + +How any skill that uses a collaboration template (PR, commit, story, ADR, checkpoint, …) decides **which file to read**: the project's adoption override wins over the KB default, resolved by a **file-existence check** — never by parsing prose. + +Every template link a skill carries points at the KB default path. Before using that template, resolve it override-first via the check below. When no override exists the resolution is a no-op and the skill uses the exact KB default it links today — **zero behavior change for the common case** (idempotent). + +## Resolution — file-existence check (authoritative) + +For a template whose KB filename is `-template.md`: + +1. **Check**: Does `.pair/adoption/tech/templates/-template.md` exist? +2. **Act — override present**: Use the adoption file. The adoption override **always wins** over the KB default — no partial/hybrid merge, the override file is used whole, exactly as the skill would use the KB default. +3. **Act — no override**: Use the KB default `.pair/knowledge/guidelines/collaboration/templates/-template.md` — the path already linked in the skill. Nothing changes. +4. **Verify**: Exactly one template file resolved (adoption if present, else KB). + +`` is the KB template's own filename (e.g. `pr-template.md`, `commit-template.md`, `adr-template.md`) — an override matches only when its filename equals a shipped KB template filename. + +## The `## Templates` section is an audit trail, not the trigger + +A project may list its overrides under a `## Templates` heading in `.pair/adoption/tech/way-of-working.md`. That section is a **human-readable audit trail / record of intent** — it is **not the resolution trigger**. Resolution depends only on the file existing on disk (step 1 above), never on that prose being present or accurate. This avoids a second source of truth drifting out of sync with the actual override files. + +## Edge cases + +- **Override file exists but is malformed/empty**: used as-is — same trust level as any adoption file. Validating template content is out of scope (not this resolution's job). +- **Override filename doesn't match any shipped KB template** (typo): silently unused — the skill falls back to the KB default it links, matching how unused adoption content behaves elsewhere. +- **`pair-cli install`/`update`**: `.pair/adoption/` is preserved/untouched, so overrides survive updates — no install/update change is needed for this resolution to hold. + +## What stays in the skill (the delta) + +Nothing skill-specific. Each skill keeps its existing template link(s) and a single pointer to this file, e.g.: + +> **Template resolution:** resolve every collaboration template this skill links override-first — see [template-resolution.md](template-resolution.md). + +This is a context pointer, not a citation: the executor follows it when it reaches a template it must read, the same way it follows any other guideline pointer. diff --git a/apps/website/content/docs/customization/templates.mdx b/apps/website/content/docs/customization/templates.mdx index 615e5e88..40de745f 100644 --- a/apps/website/content/docs/customization/templates.mdx +++ b/apps/website/content/docs/customization/templates.mdx @@ -3,7 +3,7 @@ title: Customize Templates description: Override pair's built-in templates for commits, PRs, stories, epics, and other workflow artifacts. --- -pair ships with 14 templates that control the structure of workflow artifacts — from commit messages to user stories. This guide shows how to customize them for your team. +pair ships with 19 templates that control the structure of workflow artifacts — from commit messages to user stories. This guide shows how to customize them for your team. ## Where Templates Live @@ -11,23 +11,28 @@ Templates are part of the Knowledge Base: ```text .pair/knowledge/guidelines/collaboration/templates/ -├── adr-template.md # Architecture Decision Records -├── adl-template.md # Architecture Decision Log entries -├── ddr-template.md # Domain Decision Records -├── analysis-log-template.md # Analysis-Log entries (category `Analysis`) -├── bounded-context-template.md # DDD bounded context definitions -├── branch-template.md # Git branch naming conventions -├── code-review-template.md # Code review checklists -├── commit-template.md # Commit message format -├── epic-template.md # Epic issue structure -├── initiative-template.md # Initiative issue structure -├── pr-template.md # Pull request description -├── subdomain-template.md # DDD subdomain definitions -├── task-template.md # Task breakdown structure -└── user-story-template.md # User story issue structure +├── adr-template.md # Architecture Decision Records +├── adl-template.md # Architecture Decision Log entries +├── analysis-log-template.md # Analysis-Log entries (category `Analysis`) +├── bounded-context-template.md # DDD bounded context definitions +├── branch-template.md # Git branch naming conventions +├── checkpoint-template.md # Resumable progress state (context-reset handoff) +├── code-review-template.md # Code review checklists +├── commit-template.md # Commit message format +├── context-map-template.md # Context map: glossary, entities, common rules +├── ddr-template.md # Domain Decision Records +├── epic-template.md # Epic issue structure +├── initiative-template.md # Initiative issue structure +├── manual-test-case-template.md # Manual test case design +├── manual-test-report-template.md # Manual test execution report +├── pr-template.md # Pull request description +├── subdomain-context-template.md # Lazy per-subdomain context split +├── subdomain-template.md # DDD subdomain definitions +├── task-template.md # Task breakdown structure +└── user-story-template.md # User story issue structure ``` -The AI reads these templates when creating the corresponding artifacts. When you run `/pair-process-implement`, it uses the commit and PR templates. When you run `/pair-process-plan-stories`, it uses the user story template. And so on. +The AI reads these templates when creating the corresponding artifacts — but it always resolves an [adoption override](#how-to-customize-a-template) first, falling back to the KB copy above only when no override exists. When you run `/pair-process-implement`, it uses the commit and PR templates. When you run `/pair-process-plan-stories`, it uses the user story template. And so on. ## Template Categories @@ -37,7 +42,7 @@ The AI reads these templates when creating the corresponding artifacts. When you | --- | --- | --- | | **commit-template.md** | `/pair-process-implement` | Commit message format: `[#story-id] type: description` | | **pr-template.md** | `/pair-process-implement` | PR description structure: summary, changes, testing, risks | -| **branch-template.md** | `/pair-process-implement` | Branch naming: `feature/#story-id-description` | +| **branch-template.md** | `/pair-process-implement` (via `/pair-capability-publish-pr`, which parses `branch-format`) | Branch naming: `feature/#story-id-description` | | **code-review-template.md** | `/pair-process-review` | Review checklist: technical, adoption, completeness, decision | ### Planning Artifacts @@ -62,13 +67,25 @@ The AI reads these templates when creating the corresponding artifacts. When you | Template | Used by | Controls | | --- | --- | --- | -| **subdomain-template.md** | `/pair-capability-map-subdomains` | Subdomain definition: classification, Volatility, boundaries | +| **subdomain-template.md** | `/pair-capability-map-subdomains`, `/pair-capability-map-contexts` (reads Volatility) | Subdomain definition: classification, Volatility, boundaries | | **bounded-context-template.md** | `/pair-capability-map-contexts` | Context definition: responsibilities, per-relationship strength/distance/volatility assessment, Encapsulated Knowledge, Change Vectors | +| **context-map-template.md** | `/pair-capability-record-decision` (DDR flow), `/pair-capability-map-contexts` (via [context-map maintenance](/docs/reference/guidelines-catalog)) | Context map: ubiquitous language, entities, common rules | +| **subdomain-context-template.md** | `/pair-capability-map-subdomains` / `/pair-capability-map-contexts` (lazy per-subdomain split) | Per-subdomain slice of the context map, co-located with the subdomain catalog entry | + +### Quality & Operations + +| Template | Used by | Controls | +| --- | --- | --- | +| **manual-test-case-template.md** | `/pair-capability-design-manual-tests`, `/pair-capability-execute-manual-tests` | Manual test case: version-agnostic variables, priority classification | +| **manual-test-report-template.md** | `/pair-capability-execute-manual-tests` | Manual test execution report with sign-off criteria | +| **checkpoint-template.md** | `/pair-capability-checkpoint` (written by `/pair-process-implement`, consumed by `/pair-capability-publish-pr`) | Resumable progress state: story, branch, tasks done, decisions, remaining todos | ## How to Customize a Template Templates live in `.pair/knowledge/` — the upstream layer that gets overwritten on `pair-cli update`. To customize safely, create an override in the adoption layer. +**How resolution works:** every skill that uses a template resolves it **override-first** by a plain file-existence check — if `.pair/adoption/tech/templates/-template.md` exists (where `-template.md` matches a shipped KB filename above), the skill uses your copy whole; otherwise it uses the KB default. There is no partial merge and no prose to keep in sync — dropping the file in place is the entire mechanism. (Skill-side contract: `.pair/knowledge/skill-conventions/template-resolution.md`.) + ### Step 1: Copy the Template ```bash @@ -96,9 +113,11 @@ body explaining the change Refs: JIRA-XXX ``` -### Step 3: Tell the AI +That's it — the file is now live. The next time a skill needs that template it finds your override and uses it; no restart, no config, no announcement needed. + +### Step 3 (optional): Record the Override for Your Team -Record the override in your way-of-working so the AI knows to use your template: +The override already works from Step 2. This step is purely a **human-readable audit trail** — a record of intent so teammates can see, at a glance, which templates you've customized. It is **not** what triggers resolution (the file existing on disk is), so it can never fall out of sync with the actual behavior: ```markdown @@ -109,7 +128,7 @@ Record the override in your way-of-working so the AI knows to use your template: - Custom PR template: `.pair/adoption/tech/templates/pr-template.md` ``` -The AI reads adoption files first, so it will pick up your custom templates automatically. +Leaving this section out changes nothing about which template is used; adding a stale line to it does not activate an override that isn't on disk. ## Common Customizations diff --git a/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md b/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md index 41641e69..dae0183c 100644 --- a/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md +++ b/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md @@ -8,6 +8,7 @@ Single-source explanations for patterns that recur across many `SKILL.md` files. | Idempotency | [idempotency.md](idempotency.md) | 28 skills | | Graceful degradation | [graceful-degradation.md](graceful-degradation.md) | 34 skills | | Way-of-working / PM-tool resolution | [way-of-working-pm-resolution.md](way-of-working-pm-resolution.md) | 18 skills | +| Template resolution (adoption override > KB default, file-existence check) | [template-resolution.md](template-resolution.md) | 16 skills | | `/record-decision` invocation contract | [record-decision-contract.md](record-decision-contract.md) | 19 skills | | Output Format shapes (Decision / Report) | [output-shapes.md](output-shapes.md) | 10 skills (documentation-only — no logic change) | | To-issues triage (extend vs create) | [to-issues-triage.md](to-issues-triage.md) | 2 composing skills (new convention — no prior duplication to extract) | diff --git a/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/template-resolution.md b/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/template-resolution.md new file mode 100644 index 00000000..d99e4bbb --- /dev/null +++ b/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/template-resolution.md @@ -0,0 +1,34 @@ +# Template Resolution + +How any skill that uses a collaboration template (PR, commit, story, ADR, checkpoint, …) decides **which file to read**: the project's adoption override wins over the KB default, resolved by a **file-existence check** — never by parsing prose. + +Every template link a skill carries points at the KB default path. Before using that template, resolve it override-first via the check below. When no override exists the resolution is a no-op and the skill uses the exact KB default it links today — **zero behavior change for the common case** (idempotent). + +## Resolution — file-existence check (authoritative) + +For a template whose KB filename is `-template.md`: + +1. **Check**: Does `.pair/adoption/tech/templates/-template.md` exist? +2. **Act — override present**: Use the adoption file. The adoption override **always wins** over the KB default — no partial/hybrid merge, the override file is used whole, exactly as the skill would use the KB default. +3. **Act — no override**: Use the KB default `.pair/knowledge/guidelines/collaboration/templates/-template.md` — the path already linked in the skill. Nothing changes. +4. **Verify**: Exactly one template file resolved (adoption if present, else KB). + +`` is the KB template's own filename (e.g. `pr-template.md`, `commit-template.md`, `adr-template.md`) — an override matches only when its filename equals a shipped KB template filename. + +## The `## Templates` section is an audit trail, not the trigger + +A project may list its overrides under a `## Templates` heading in `.pair/adoption/tech/way-of-working.md`. That section is a **human-readable audit trail / record of intent** — it is **not the resolution trigger**. Resolution depends only on the file existing on disk (step 1 above), never on that prose being present or accurate. This avoids a second source of truth drifting out of sync with the actual override files. + +## Edge cases + +- **Override file exists but is malformed/empty**: used as-is — same trust level as any adoption file. Validating template content is out of scope (not this resolution's job). +- **Override filename doesn't match any shipped KB template** (typo): silently unused — the skill falls back to the KB default it links, matching how unused adoption content behaves elsewhere. +- **`pair-cli install`/`update`**: `.pair/adoption/` is preserved/untouched, so overrides survive updates — no install/update change is needed for this resolution to hold. + +## What stays in the skill (the delta) + +Nothing skill-specific. Each skill keeps its existing template link(s) and a single pointer to this file, e.g.: + +> **Template resolution:** resolve every collaboration template this skill links override-first — see [template-resolution.md](template-resolution.md). + +This is a context pointer, not a citation: the executor follows it when it reaches a template it must read, the same way it follows any other guideline pointer. diff --git a/packages/knowledge-hub/dataset/.skills/capability/checkpoint/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/checkpoint/SKILL.md index 274b335e..4decd2d1 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/checkpoint/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/checkpoint/SKILL.md @@ -7,7 +7,7 @@ author: Foomakers # /checkpoint — Resumable Progress State -Write and resume a self-contained progress checkpoint so a fresh session (or a subagent) can continue a story exactly where the previous one stopped. Follows the [checkpoint template](../../../.pair/knowledge/guidelines/collaboration/templates/checkpoint-template.md) — five sections: story, branch, tasks done, key decisions, remaining todos. +Write and resume a self-contained progress checkpoint so a fresh session (or a subagent) can continue a story exactly where the previous one stopped. Follows the [checkpoint template](../../../.pair/knowledge/guidelines/collaboration/templates/checkpoint-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)) — five sections: story, branch, tasks done, key decisions, remaining todos. ## Arguments diff --git a/packages/knowledge-hub/dataset/.skills/capability/design-manual-tests/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/design-manual-tests/SKILL.md index 2e651854..561e885e 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/design-manual-tests/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/design-manual-tests/SKILL.md @@ -9,7 +9,7 @@ author: Foomakers Analyze a project's released artifacts, deployment targets, and user-facing surfaces to generate a complete manual test suite. Produces critical path files (`CP*.md`) and a suite `README.md` in the test suite directory. -Each test case follows the [manual-test-case-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-case-template.md). Design principles: [manual-testing.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-testing.md); organizational context: [manual-verification.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-verification.md). +Each test case follows the [manual-test-case-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-case-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)). Design principles: [manual-testing.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-testing.md); organizational context: [manual-verification.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-verification.md). ## Arguments diff --git a/packages/knowledge-hub/dataset/.skills/capability/execute-manual-tests/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/execute-manual-tests/SKILL.md index b744deb2..29951d1b 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/execute-manual-tests/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/execute-manual-tests/SKILL.md @@ -7,7 +7,7 @@ author: Foomakers # /execute-manual-tests — Manual Test Suite Executor -Execute a manual test suite against released or deployed artifacts (website, CLI packages, registries). Produces a structured report following the [manual-test-report-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-report-template.md). +Execute a manual test suite against released or deployed artifacts (website, CLI packages, registries). Produces a structured report following the [manual-test-report-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-report-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)). Reads test case files from the project's manual test suite directory; each follows the [manual-test-case-template](../../../.pair/knowledge/guidelines/collaboration/templates/manual-test-case-template.md). Organizational context (who, when, which areas): [manual-verification.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-verification.md); test case design principles: [manual-testing.md](../../../.pair/knowledge/guidelines/quality-assurance/manual-testing.md). diff --git a/packages/knowledge-hub/dataset/.skills/capability/grill/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/grill/SKILL.md index 4ce606bc..633094e3 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/grill/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/grill/SKILL.md @@ -92,7 +92,7 @@ Systematic AI↔human alignment on a specific story, covering all six aspects of 1. **Act**: Compile recorded answers, explored facts (with source), and flagged assumptions: - `interview` → raw requirements blob (topic, findings per sub-question, open items, assumptions). - - `sync` → alignment synthesis mapped to the six [Sync Coverage Checklist](#sync-coverage-checklist) aspects, ready to drop into the matching [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) sections. + - `sync` → alignment synthesis mapped to the six [Sync Coverage Checklist](#sync-coverage-checklist) aspects, ready to drop into the matching [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)) sections. 2. **Verify**: Synthesis accounts for every answered/explored/assumed item — nothing silently dropped. ### Step 6: Return — Never Write diff --git a/packages/knowledge-hub/dataset/.skills/capability/map-contexts/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/map-contexts/SKILL.md index 0137b135..c110318c 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/map-contexts/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/map-contexts/SKILL.md @@ -115,7 +115,7 @@ For each approved context in scope: 1. **Check**: Does this context already exist as a file? 2. **Act**: If exists and no conflict was raised → leave untouched; pre-existing relationships without the 3-dimension assessment remain valid as-is (no forced migration — assessed the next time that relationship falls inside a `$scope`). -3. **Act**: If new, or an approved delta applies → create/update the context file following [bounded-context-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/bounded-context-template.md): +3. **Act**: If new, or an approved delta applies → create/update the context file following [bounded-context-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/bounded-context-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill all template sections: Type, Subdomains Covered, Business Scope, Relationships (with the 3-dimension assessment per relationship), Integration Patterns, Data Ownership, Team Alignment, Ubiquitous Language, Quality Attributes, Encapsulated Knowledge, Change Vectors. - File path: `adoption/tech/boundedcontext/[kebab-case-name].md` 4. **Verify**: File created/updated and parseable. Entries outside `$scope` are untouched. diff --git a/packages/knowledge-hub/dataset/.skills/capability/map-subdomains/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/map-subdomains/SKILL.md index 9bcb39ee..1aafb820 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/map-subdomains/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/map-subdomains/SKILL.md @@ -104,7 +104,7 @@ For each approved subdomain in scope: 1. **Check**: Does this subdomain already exist as a file? 2. **Act**: If exists and no conflict was raised → leave untouched; pre-existing entries without a `Volatility` field remain valid as-is (no forced migration — the field is added the next time this entry falls inside a `$scope`). -3. **Act**: If new, or an approved delta applies → create/update the subdomain file following [subdomain-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/subdomain-template.md): +3. **Act**: If new, or an approved delta applies → create/update the subdomain file following [subdomain-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/subdomain-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill all template sections: Classification, Volatility, Business Purpose, Key Capabilities, Strategic Importance, Complexity Assessment, Data Ownership, Dependencies, Team Recommendations, Implementation Priority. - File path: `adoption/product/subdomain/[kebab-case-name].md` 4. **Verify**: File created/updated and parseable. Entries outside `$scope` are untouched. diff --git a/packages/knowledge-hub/dataset/.skills/capability/publish-pr/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/publish-pr/SKILL.md index c760511c..04643945 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/publish-pr/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/publish-pr/SKILL.md @@ -67,7 +67,7 @@ Each phase follows the **check → skip → act → verify** pattern. Phases run ### Phase 3: Compose the PR Body (AC3) -1. **Act**: Read the [pr-template](../../../.pair/knowledge/guidelines/collaboration/templates/pr-template.md). Fill the always-applicable sections from the handoff/story: +1. **Act**: Read the [pr-template](../../../.pair/knowledge/guidelines/collaboration/templates/pr-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md); the same resolution applies to the commit and branch templates referenced above). Fill the always-applicable sections from the handoff/story: - **Title**: `[#] : ` (`` from commit-format / story type). - **Summary** (What Changed + Why) from the story statement and the handoff's decisions. - **Story Context**: link the story issue and list AC coverage. diff --git a/packages/knowledge-hub/dataset/.skills/capability/record-decision/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/record-decision/SKILL.md index d58c4187..0778b12d 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/record-decision/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/record-decision/SKILL.md @@ -88,7 +88,7 @@ A domain decision only becomes a DDR if it meets **all three** criteria. This is 1. **Check**: Does [adoption/tech/adr/](../../../.pair/adoption/tech/adr/) directory exist? 2. **Act**: If not, create it. 3. **Check**: Determine the next sequence number — scan existing `adr-NNN-*.md` files, take the highest `NNN`, increment by 1 (zero-padded to 3 digits). If none exist, start at `001`. -4. **Act**: Create (or update) the ADR file at [adoption/tech/adr/](../../../.pair/adoption/tech/adr/)`adr-NNN-.md` following the standalone [ADR template](../../../.pair/knowledge/guidelines/collaboration/templates/adr-template.md). Fill in all sections: Status, Date, Context, Options Considered, Decision, Consequences, and Adoption Impact. +4. **Act**: Create (or update) the ADR file at [adoption/tech/adr/](../../../.pair/adoption/tech/adr/)`adr-NNN-.md` following the standalone [ADR template](../../../.pair/knowledge/guidelines/collaboration/templates/adr-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md); the same applies to the ADL, DDR, and analysis-log templates below). Fill in all sections: Status, Date, Context, Options Considered, Decision, Consequences, and Adoption Impact. 5. **Verify**: ADR file exists with complete content following the template structure, and sequential numbering is unbroken. #### If `non-architectural` → ADL: diff --git a/packages/knowledge-hub/dataset/.skills/capability/write-issue/SKILL.md b/packages/knowledge-hub/dataset/.skills/capability/write-issue/SKILL.md index 7022789d..43614069 100644 --- a/packages/knowledge-hub/dataset/.skills/capability/write-issue/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/capability/write-issue/SKILL.md @@ -44,7 +44,7 @@ Create or update issues in the adopted PM tool. Template-driven: reads the type- ### Step 3: Load Template -1. **Check**: Resolve template path based on `$type`: +1. **Check**: Resolve the template path for `$type` **override-first** — see [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md) (adoption `.pair/adoption/tech/templates/-template.md` wins over the KB default below): - `story` → [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) - `task` → [task-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/task-template.md) - `epic` → [epic-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/epic-template.md) diff --git a/packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md b/packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md index 6b1cbe3c..3feabf43 100644 --- a/packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/process/implement/SKILL.md @@ -233,7 +233,7 @@ Follow the TDD discipline rules strictly, and the [Design Rules](../../../.pair/ **This confirmation is required for EVERY task** — commit-per-task exists precisely to give the developer a checkpoint between tasks. 4. **Verify**: Developer confirms. If changes needed → apply changes, re-run quality (Step 2.7), ask again. -5. **Act**: Stage and commit following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md): +5. **Act**: Stage and commit following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): ```text [#] : diff --git a/packages/knowledge-hub/dataset/.skills/process/plan-epics/SKILL.md b/packages/knowledge-hub/dataset/.skills/process/plan-epics/SKILL.md index fb31bea6..f3389da3 100644 --- a/packages/knowledge-hub/dataset/.skills/process/plan-epics/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/process/plan-epics/SKILL.md @@ -103,7 +103,7 @@ Process epics sequentially (Epic 0 first if needed), per its Step 3 proposal: > Epic `[Title]` already exists (#ID). Skipping. -3. **Act**: Draft the epic following [epic-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/epic-template.md): +3. **Act**: Draft the epic following [epic-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/epic-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill template sections: Epic Statement, Business Value, Solution Overview, Epic Breakdown, Technical Considerations. - Present to developer for validation. 4. **Act**: Compose `/write-issue` per the confirmed proposal: diff --git a/packages/knowledge-hub/dataset/.skills/process/plan-initiatives/SKILL.md b/packages/knowledge-hub/dataset/.skills/process/plan-initiatives/SKILL.md index ea348784..2ea63fe3 100644 --- a/packages/knowledge-hub/dataset/.skills/process/plan-initiatives/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/process/plan-initiatives/SKILL.md @@ -90,7 +90,7 @@ Process initiatives by priority (P0 → P1 → P2). For each initiative: > Initiative `[Title]` already exists (#ID). Skipping. -3. **Act**: Draft the initiative following the [initiative-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/initiative-template.md): +3. **Act**: Draft the initiative following the [initiative-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/initiative-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Fill template sections with PRD-derived content. - Include business rationale, scope, success metrics, risk assessment, timeline. - Present to developer for validation. diff --git a/packages/knowledge-hub/dataset/.skills/process/plan-stories/SKILL.md b/packages/knowledge-hub/dataset/.skills/process/plan-stories/SKILL.md index 7f976e82..c8b8f81a 100644 --- a/packages/knowledge-hub/dataset/.skills/process/plan-stories/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/process/plan-stories/SKILL.md @@ -94,7 +94,7 @@ For each approved story, per its Step 3 proposal: > Story `[Title]` already exists (#ID). Skipping. -3. **Act**: Define story scope using the Initial Breakdown section of [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md): +3. **Act**: Define story scope using the Initial Breakdown section of [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Story statement (As a / I want / So that). - Rough scope boundaries with expected uncertainty. - Initial sizing: XS(1), S(2), M(3), L(5), XL(8). diff --git a/packages/knowledge-hub/dataset/.skills/process/plan-tasks/SKILL.md b/packages/knowledge-hub/dataset/.skills/process/plan-tasks/SKILL.md index c1e6c870..77350147 100644 --- a/packages/knowledge-hub/dataset/.skills/process/plan-tasks/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/process/plan-tasks/SKILL.md @@ -90,7 +90,7 @@ Transform a refined user story into specific, actionable implementation tasks. T For each task (skipping tasks that already exist in the story body): -1. **Act**: Define the task following the [task-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/task-template.md): +1. **Act**: Define the task following the [task-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/task-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - Task Information (ID, priority, hours, bounded context) - Summary and type - Description with implementation context diff --git a/packages/knowledge-hub/dataset/.skills/process/refine-story/SKILL.md b/packages/knowledge-hub/dataset/.skills/process/refine-story/SKILL.md index da5b256e..951761d6 100644 --- a/packages/knowledge-hub/dataset/.skills/process/refine-story/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/process/refine-story/SKILL.md @@ -138,7 +138,7 @@ Transform a user story from rough breakdown (Draft) into a development-ready spe ### Step 5: Documentation and PM Tool Update -1. **Act**: Assemble the complete refined story body using the [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) Refined template: +1. **Act**: Assemble the complete refined story body using the [user-story-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/user-story-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)) Refined template: - **Functional sections first**: Story Statement → Epic Context → Classification (the Step 3b matrix) → Acceptance Criteria → Definition of Done → Story Sizing → Dependencies → Validation → Notes. - **Technical sections last**: Technical Analysis → (Task Breakdown added later by `/plan-tasks`). 2. **Act**: Compose `/write-issue` with: diff --git a/packages/knowledge-hub/dataset/.skills/process/review/SKILL.md b/packages/knowledge-hub/dataset/.skills/process/review/SKILL.md index 073645a1..8c6fc813 100644 --- a/packages/knowledge-hub/dataset/.skills/process/review/SKILL.md +++ b/packages/knowledge-hub/dataset/.skills/process/review/SKILL.md @@ -191,7 +191,7 @@ Run the procedure for the level determined in Step 3.1 — see [degradation-leve ### Step 5.1: Compile Review Report -1. **Act**: Compile all findings into a review report following the [code-review-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/code-review-template.md): +1. **Act**: Compile all findings into a review report following the [code-review-template.md](../../../.pair/knowledge/guidelines/collaboration/templates/code-review-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): - **Review Information**: PR number, author, reviewer, date, story, review type - **Review Summary**: overall assessment, key changes, business value - **Code Review Checklist**: functionality, code quality, technical standards (from Phase 2) diff --git a/packages/knowledge-hub/src/conformance/template-resolution.test.ts b/packages/knowledge-hub/src/conformance/template-resolution.test.ts new file mode 100644 index 00000000..2a32fc37 --- /dev/null +++ b/packages/knowledge-hub/src/conformance/template-resolution.test.ts @@ -0,0 +1,123 @@ +import { describe, it, expect } from 'vitest' +import { existsSync, readFileSync, readdirSync } from 'fs' +import { join, relative } from 'path' +import { collectSkillFiles } from '../tools/skills-conformance-check' + +// Conformance guard for story #314: the template-override mechanism the docs +// describe (`.pair/adoption/tech/templates/-template.md` shadows the KB +// default) must actually be wired into the skills. Every SKILL.md that links a +// collaboration template must resolve it override-first, via the single shared +// `template-resolution.md` convention — not by hardcoding the KB path alone. + +const DATASET_SKILLS = join(__dirname, '../../dataset/.skills') +const CONVENTIONS = join(__dirname, '../../dataset/.pair/knowledge/skill-conventions') +const MIRROR = join(__dirname, '../../../../.claude/skills') + +const CONVENTION_FILE = join(CONVENTIONS, 'template-resolution.md') +const CONVENTION = readFileSync(CONVENTION_FILE, 'utf-8') +const CONVENTION_README = readFileSync(join(CONVENTIONS, 'README.md'), 'utf-8') + +const POINTER = /template-resolution\.md/ +// A collaboration-template link: .../collaboration/templates/-template.md +const TEMPLATE_LINK = /collaboration\/templates\/[a-z0-9-]*template\.md/ + +/** Every SKILL.md under a skills root that links at least one collaboration template. */ +function templateLinkingSkills(skillsRoot: string): { rel: string; content: string }[] { + return collectSkillFiles(skillsRoot) + .map(f => ({ rel: relative(skillsRoot, f), content: readFileSync(f, 'utf-8') })) + .filter(({ content }) => TEMPLATE_LINK.test(content)) +} + +describe('template-resolution.md — shared convention (AC1, AC3, AC4)', () => { + it('is registered in the skill-conventions README', () => { + expect(CONVENTION_README).toMatch(POINTER) + }) + + it('resolves via a file-existence check on the adoption override path (AC3)', () => { + expect(CONVENTION).toMatch(/\.pair\/adoption\/tech\/templates\/-template\.md/) + expect(CONVENTION.toLowerCase()).toMatch(/file-existence check/) + expect(CONVENTION.toLowerCase()).toMatch(/exist/) + }) + + it('falls back to the KB default path when no override is present (AC1 — zero behavior change)', () => { + expect(CONVENTION).toMatch( + /\.pair\/knowledge\/guidelines\/collaboration\/templates\/-template\.md/, + ) + expect(CONVENTION.toLowerCase()).toMatch(/zero behavior change/) + expect(CONVENTION.toLowerCase()).toMatch(/idempotent/) + }) + + it('states the adoption override always wins, whole, over the KB default (AC2 semantics)', () => { + expect(CONVENTION.toLowerCase()).toMatch(/always wins/) + expect(CONVENTION.toLowerCase()).toMatch(/no partial\/hybrid/) + }) + + it('documents way-of-working `## Templates` as an audit trail, NOT the resolution trigger (AC4)', () => { + expect(CONVENTION).toMatch(/## Templates/) + expect(CONVENTION.toLowerCase()).toMatch(/audit trail/) + expect(CONVENTION.toLowerCase()).toMatch(/not the resolution trigger/) + }) + + it('handles the edge cases (malformed/empty override used as-is; unknown filename silently unused)', () => { + expect(CONVENTION.toLowerCase()).toMatch(/malformed\/empty/) + expect(CONVENTION.toLowerCase()).toMatch(/silently unused/) + }) +}) + +describe('every template-linking SKILL.md carries the resolution pointer (AC3)', () => { + it('dataset: no skill links a collaboration template without the resolution pointer', () => { + const offenders = templateLinkingSkills(DATASET_SKILLS) + .filter(({ content }) => !POINTER.test(content)) + .map(({ rel }) => rel) + expect(offenders).toEqual([]) + }) + + it('dataset: at least 15 skills link collaboration templates (the wired set)', () => { + expect(templateLinkingSkills(DATASET_SKILLS).length).toBeGreaterThanOrEqual(15) + }) + + it('installed mirror: no skill links a collaboration template without the resolution pointer', () => { + // The mirror is flat: .claude/skills/pair--/SKILL.md + const offenders = readdirSync(MIRROR, { withFileTypes: true }) + .filter(d => d.isDirectory()) + .map(d => join(MIRROR, d.name, 'SKILL.md')) + .filter(f => existsSync(f)) + .map(f => ({ rel: relative(MIRROR, f), content: readFileSync(f, 'utf-8') })) + .filter(({ content }) => TEMPLATE_LINK.test(content) && !POINTER.test(content)) + .map(({ rel }) => rel) + expect(offenders).toEqual([]) + }) +}) + +// AC2: verified for at least one process-composed template and one capability-composed template. +describe('AC2 — key template consumers resolve override-first (dataset + mirror)', () => { + const cases: { dataset: string; mirror: string; label: string }[] = [ + { + dataset: 'process/implement', + mirror: 'pair-process-implement', + label: 'commit-template via /implement', + }, + { + dataset: 'capability/publish-pr', + mirror: 'pair-capability-publish-pr', + label: 'pr-template via /publish-pr', + }, + { + dataset: 'capability/record-decision', + mirror: 'pair-capability-record-decision', + label: 'adr/adl/ddr-template via /record-decision', + }, + ] + + it.each(cases)('$label carries the pointer in the dataset skill', ({ dataset }) => { + const content = readFileSync(join(DATASET_SKILLS, dataset, 'SKILL.md'), 'utf-8') + expect(content).toMatch(TEMPLATE_LINK) + expect(content).toMatch(POINTER) + }) + + it.each(cases)('$label carries the pointer in the installed mirror', ({ mirror }) => { + const content = readFileSync(join(MIRROR, mirror, 'SKILL.md'), 'utf-8') + expect(content).toMatch(TEMPLATE_LINK) + expect(content).toMatch(POINTER) + }) +}) From 21c90d4f1f64242261baae19912e61c5bebd1f4e Mon Sep 17 00:00:00 2001 From: Gianluca Carucci Date: Thu, 23 Jul 2026 17:30:56 +0200 Subject: [PATCH 2/5] [#314] fix: wire template resolution into aux merge sites + guideline; broaden conformance scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review fixes on PR #367: - commit-template use-sites in merge-and-cascade.md (review Phase 6) and post-review-merge.md (implement Phase 4) now resolve override-first (dataset + .claude mirror) - context-map-maintenance.md: context-map-template & subdomain-context-template links resolve override-first so those 2 overrides are honored (matches templates.mdx claim, AC5) - conformance: new collectSkillMarkdownFiles walks all .md (not just SKILL.md); pointer invariant now scans disclosed aux files — root cause that let the aux-site gap pass green Refs: #314 --- .../post-review-merge.md | 2 +- .../pair-process-review/merge-and-cascade.md | 2 +- .../context-map-maintenance.md | 4 +- .../context-map-maintenance.md | 4 +- .../process/implement/post-review-merge.md | 2 +- .../process/review/merge-and-cascade.md | 2 +- .../conformance/template-resolution.test.ts | 37 +++++++++++-------- .../src/tools/skills-conformance-check.ts | 19 ++++++++++ 8 files changed, 49 insertions(+), 23 deletions(-) diff --git a/.claude/skills/pair-process-implement/post-review-merge.md b/.claude/skills/pair-process-implement/post-review-merge.md index ebaaa07e..d8e2bbce 100644 --- a/.claude/skills/pair-process-implement/post-review-merge.md +++ b/.claude/skills/pair-process-implement/post-review-merge.md @@ -12,7 +12,7 @@ Disclosed from [SKILL.md](./SKILL.md) Phase 4 — only reached when `/pair-proce 1. **Check**: Read [way-of-working.md](../../../.pair/adoption/tech/way-of-working.md) for merge strategy (squash, merge, rebase). 2. **Act**: Draft the final commit message: - - **If squash**: combine all commits into a single message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md). + - **If squash**: combine all commits into a single message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)). - **If merge or rebase**: use the default merge/rebase message. 3. **Act** (BLOCKING): Present the commit message to the developer for confirmation: diff --git a/.claude/skills/pair-process-review/merge-and-cascade.md b/.claude/skills/pair-process-review/merge-and-cascade.md index 77edfc7a..dbeedc09 100644 --- a/.claude/skills/pair-process-review/merge-and-cascade.md +++ b/.claude/skills/pair-process-review/merge-and-cascade.md @@ -11,7 +11,7 @@ Disclosed from [SKILL.md](./SKILL.md) Phase 6 — only reached when the reviewer ### Step 6.2: Prepare Merge Commit -1. **Act**: Draft the merge commit message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md): +1. **Act**: Draft the merge commit message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): ```text [#] feat: diff --git a/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md b/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md index 2a649f2e..814e40ec 100644 --- a/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md +++ b/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md @@ -20,7 +20,7 @@ Apply these four checks whenever the session's domain step is reached. They are ### 1. Challenge the Glossary -Before adding a term, check whether `context-map.md` (or the relevant `subdomain/.context.md`) already has it, or something close enough to conflict. Read the [Subdomain Index](../../collaboration/templates/context-map-template.md) first to know which subdomains keep their context inline versus split out. +Before adding a term, check whether `context-map.md` (or the relevant `subdomain/.context.md`) already has it, or something close enough to conflict. Read the [Subdomain Index](../../collaboration/templates/context-map-template.md) (resolve override-first — [template resolution](../../../skill-conventions/template-resolution.md)) first to know which subdomains keep their context inline versus split out. ### 2. Sharpen Vague Terms @@ -44,7 +44,7 @@ The map starts as a single file. Splitting is **lazy and human-approved — neve - **Signal to propose a split**: a subdomain's inline Glossary/Entities rows in `context-map.md` have grown large enough that scanning the shared file for unrelated subdomains becomes noisy, or the subdomain's context changes far more often than the rest of the map. - **Propose, don't act**: the maintaining session states the reason and asks for approval — e.g. "`billing`'s glossary is now 12 terms; move it to its own context file?" Proceed only once the human agrees. -- **What moves**: every Glossary and Entities row scoped to that subdomain moves to the co-located sibling `subdomain/.context.md` (see `subdomain-context-template.md`), sitting next to the strategic `subdomain/.md` catalog file. +- **What moves**: every Glossary and Entities row scoped to that subdomain moves to the co-located sibling `subdomain/.context.md` (see [subdomain-context-template.md](../../collaboration/templates/subdomain-context-template.md) — resolve override-first per [template resolution](../../../skill-conventions/template-resolution.md)), sitting next to the strategic `subdomain/.md` catalog file. - **What stays**: the Subdomain Index row (flipped to "Yes" under Own Context) and the entire Common Rules and Invariants section — domain-wide rules are never split, since they aren't owned by one subdomain. - **Never re-map automatically**: a later session may propose merging a context file back inline if it shrinks, but this again requires human approval — the guideline never rewrites the split on its own. diff --git a/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md b/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md index 2a649f2e..814e40ec 100644 --- a/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md +++ b/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md @@ -20,7 +20,7 @@ Apply these four checks whenever the session's domain step is reached. They are ### 1. Challenge the Glossary -Before adding a term, check whether `context-map.md` (or the relevant `subdomain/.context.md`) already has it, or something close enough to conflict. Read the [Subdomain Index](../../collaboration/templates/context-map-template.md) first to know which subdomains keep their context inline versus split out. +Before adding a term, check whether `context-map.md` (or the relevant `subdomain/.context.md`) already has it, or something close enough to conflict. Read the [Subdomain Index](../../collaboration/templates/context-map-template.md) (resolve override-first — [template resolution](../../../skill-conventions/template-resolution.md)) first to know which subdomains keep their context inline versus split out. ### 2. Sharpen Vague Terms @@ -44,7 +44,7 @@ The map starts as a single file. Splitting is **lazy and human-approved — neve - **Signal to propose a split**: a subdomain's inline Glossary/Entities rows in `context-map.md` have grown large enough that scanning the shared file for unrelated subdomains becomes noisy, or the subdomain's context changes far more often than the rest of the map. - **Propose, don't act**: the maintaining session states the reason and asks for approval — e.g. "`billing`'s glossary is now 12 terms; move it to its own context file?" Proceed only once the human agrees. -- **What moves**: every Glossary and Entities row scoped to that subdomain moves to the co-located sibling `subdomain/.context.md` (see `subdomain-context-template.md`), sitting next to the strategic `subdomain/.md` catalog file. +- **What moves**: every Glossary and Entities row scoped to that subdomain moves to the co-located sibling `subdomain/.context.md` (see [subdomain-context-template.md](../../collaboration/templates/subdomain-context-template.md) — resolve override-first per [template resolution](../../../skill-conventions/template-resolution.md)), sitting next to the strategic `subdomain/.md` catalog file. - **What stays**: the Subdomain Index row (flipped to "Yes" under Own Context) and the entire Common Rules and Invariants section — domain-wide rules are never split, since they aren't owned by one subdomain. - **Never re-map automatically**: a later session may propose merging a context file back inline if it shrinks, but this again requires human approval — the guideline never rewrites the split on its own. diff --git a/packages/knowledge-hub/dataset/.skills/process/implement/post-review-merge.md b/packages/knowledge-hub/dataset/.skills/process/implement/post-review-merge.md index 92b7e987..19f3c465 100644 --- a/packages/knowledge-hub/dataset/.skills/process/implement/post-review-merge.md +++ b/packages/knowledge-hub/dataset/.skills/process/implement/post-review-merge.md @@ -12,7 +12,7 @@ Disclosed from [SKILL.md](SKILL.md) Phase 4 — only reached when `/implement` i 1. **Check**: Read [way-of-working.md](../../../.pair/adoption/tech/way-of-working.md) for merge strategy (squash, merge, rebase). 2. **Act**: Draft the final commit message: - - **If squash**: combine all commits into a single message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md). + - **If squash**: combine all commits into a single message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)). - **If merge or rebase**: use the default merge/rebase message. 3. **Act** (BLOCKING): Present the commit message to the developer for confirmation: diff --git a/packages/knowledge-hub/dataset/.skills/process/review/merge-and-cascade.md b/packages/knowledge-hub/dataset/.skills/process/review/merge-and-cascade.md index 4909df9b..827670d5 100644 --- a/packages/knowledge-hub/dataset/.skills/process/review/merge-and-cascade.md +++ b/packages/knowledge-hub/dataset/.skills/process/review/merge-and-cascade.md @@ -11,7 +11,7 @@ Disclosed from [SKILL.md](SKILL.md) Phase 6 — only reached when the reviewer p ### Step 6.2: Prepare Merge Commit -1. **Act**: Draft the merge commit message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md): +1. **Act**: Draft the merge commit message following the [commit template](../../../.pair/knowledge/guidelines/collaboration/templates/commit-template.md) (resolve override-first — [template resolution](../../../.pair/knowledge/skill-conventions/template-resolution.md)): ```text [#] feat: diff --git a/packages/knowledge-hub/src/conformance/template-resolution.test.ts b/packages/knowledge-hub/src/conformance/template-resolution.test.ts index 2a32fc37..56a0c358 100644 --- a/packages/knowledge-hub/src/conformance/template-resolution.test.ts +++ b/packages/knowledge-hub/src/conformance/template-resolution.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest' -import { existsSync, readFileSync, readdirSync } from 'fs' +import { readFileSync } from 'fs' import { join, relative } from 'path' -import { collectSkillFiles } from '../tools/skills-conformance-check' +import { collectSkillFiles, collectSkillMarkdownFiles } from '../tools/skills-conformance-check' // Conformance guard for story #314: the template-override mechanism the docs // describe (`.pair/adoption/tech/templates/-template.md` shadows the KB @@ -21,7 +21,19 @@ const POINTER = /template-resolution\.md/ // A collaboration-template link: .../collaboration/templates/-template.md const TEMPLATE_LINK = /collaboration\/templates\/[a-z0-9-]*template\.md/ -/** Every SKILL.md under a skills root that links at least one collaboration template. */ +/** + * Every skill Markdown file (SKILL.md AND auxiliary composed files) under a + * skills root that links at least one collaboration template. Walks all `.md`, + * not just SKILL.md, so template links in disclosed files (merge-and-cascade.md, + * post-review-merge.md, …) cannot escape the pointer invariant (story #314). + */ +function templateLinkingFiles(skillsRoot: string): { rel: string; content: string }[] { + return collectSkillMarkdownFiles(skillsRoot) + .map(f => ({ rel: relative(skillsRoot, f), content: readFileSync(f, 'utf-8') })) + .filter(({ content }) => TEMPLATE_LINK.test(content)) +} + +/** Same, but SKILL.md files only — used for the "wired skill count" invariant. */ function templateLinkingSkills(skillsRoot: string): { rel: string; content: string }[] { return collectSkillFiles(skillsRoot) .map(f => ({ rel: relative(skillsRoot, f), content: readFileSync(f, 'utf-8') })) @@ -64,26 +76,21 @@ describe('template-resolution.md — shared convention (AC1, AC3, AC4)', () => { }) }) -describe('every template-linking SKILL.md carries the resolution pointer (AC3)', () => { - it('dataset: no skill links a collaboration template without the resolution pointer', () => { - const offenders = templateLinkingSkills(DATASET_SKILLS) +describe('every template-linking skill file carries the resolution pointer (AC3)', () => { + it('dataset: no skill file (SKILL.md or disclosed .md) links a collaboration template without the resolution pointer', () => { + const offenders = templateLinkingFiles(DATASET_SKILLS) .filter(({ content }) => !POINTER.test(content)) .map(({ rel }) => rel) expect(offenders).toEqual([]) }) - it('dataset: at least 15 skills link collaboration templates (the wired set)', () => { + it('dataset: at least 15 SKILL.md link collaboration templates (the wired set)', () => { expect(templateLinkingSkills(DATASET_SKILLS).length).toBeGreaterThanOrEqual(15) }) - it('installed mirror: no skill links a collaboration template without the resolution pointer', () => { - // The mirror is flat: .claude/skills/pair--/SKILL.md - const offenders = readdirSync(MIRROR, { withFileTypes: true }) - .filter(d => d.isDirectory()) - .map(d => join(MIRROR, d.name, 'SKILL.md')) - .filter(f => existsSync(f)) - .map(f => ({ rel: relative(MIRROR, f), content: readFileSync(f, 'utf-8') })) - .filter(({ content }) => TEMPLATE_LINK.test(content) && !POINTER.test(content)) + it('installed mirror: no skill file links a collaboration template without the resolution pointer', () => { + const offenders = templateLinkingFiles(MIRROR) + .filter(({ content }) => !POINTER.test(content)) .map(({ rel }) => rel) expect(offenders).toEqual([]) }) diff --git a/packages/knowledge-hub/src/tools/skills-conformance-check.ts b/packages/knowledge-hub/src/tools/skills-conformance-check.ts index 2ad90e1b..b3889177 100644 --- a/packages/knowledge-hub/src/tools/skills-conformance-check.ts +++ b/packages/knowledge-hub/src/tools/skills-conformance-check.ts @@ -313,6 +313,25 @@ export function checkCategoryLabelCounts( // --- Corpus walk --- +/** + * Every Markdown file under the skills corpus — SKILL.md AND auxiliary composed + * files (e.g. `merge-and-cascade.md`, `post-review-merge.md`) that a SKILL.md + * discloses to. Instruction lives in these files too, so template-link/pointer + * invariants must scan them, not just SKILL.md (story #314). + */ +export function collectSkillMarkdownFiles(skillsDir: string): string[] { + const files: string[] = [] + const walk = (dir: string): void => { + for (const e of readdirSync(dir, { withFileTypes: true })) { + const p = join(dir, e.name) + if (e.isDirectory()) walk(p) + else if (e.name.endsWith('.md')) files.push(p) + } + } + walk(skillsDir) + return files +} + export function collectSkillFiles(skillsDir: string): string[] { const files: string[] = [] const categories = readdirSync(skillsDir, { withFileTypes: true }).filter(d => d.isDirectory()) From 922fba35cb4e6f96d8bf15d653be333a73be12a0 Mon Sep 17 00:00:00 2001 From: Gianluca Carucci Date: Thu, 23 Jul 2026 17:49:07 +0200 Subject: [PATCH 3/5] [#314] fix: correct template attribution + close guideline scan blind-spot (review minors) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - templates.mdx: attribute context-map/subdomain-context templates to the context-map-maintenance guideline (record-decision writes context-map.md inline, never reads the template; map-* don't read it directly) - context-map-maintenance.md footer: add override-first resolution pointer to both template links (intra-file consistency with the use-sites) - skill-conventions README: drop hardcoded "16 skills" → "all template-linking skills" - template-resolution.test.ts: extend pointer scan to the guidelines corpus (dataset + generated root) so removing the pointer from context-map-maintenance.md fails the test (closes Major #2 blind-spot) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../context-map-maintenance.md | 4 +- .pair/knowledge/skill-conventions/README.md | 2 +- .../content/docs/customization/templates.mdx | 4 +- .../context-map-maintenance.md | 4 +- .../knowledge/skill-conventions/README.md | 2 +- .../conformance/template-resolution.test.ts | 55 ++++++++++++++++--- 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md b/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md index 814e40ec..ac62290d 100644 --- a/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md +++ b/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md @@ -66,8 +66,8 @@ Any skill that touches domain scope (brainstorm's domain step, refine-story's re ## Related Documents -- **[Context Map Template](../../collaboration/templates/context-map-template.md)** — the dispatcher + index + core artifact this guideline maintains -- **[Subdomain Context Template](../../collaboration/templates/subdomain-context-template.md)** — the lazy-split sibling file +- **[Context Map Template](../../collaboration/templates/context-map-template.md)** — the dispatcher + index + core artifact this guideline maintains (resolve override-first per [template resolution](../../../skill-conventions/template-resolution.md)) +- **[Subdomain Context Template](../../collaboration/templates/subdomain-context-template.md)** — the lazy-split sibling file (resolve override-first per [template resolution](../../../skill-conventions/template-resolution.md)) - **[Strategic Subdomain Definition](strategic-subdomain-definition.md)** — subdomain catalog and classification, source of the mirrored Volatility column - **[Domain-Driven Design](domain-driven-design.md)** — overall DDD principles this guideline operationalizes as a maintenance process - **[Decision Records](../decision-frameworks/README.md)** — ADR/DDR process for domain decisions cited during conflict-flagging diff --git a/.pair/knowledge/skill-conventions/README.md b/.pair/knowledge/skill-conventions/README.md index a500535e..167e3dd1 100644 --- a/.pair/knowledge/skill-conventions/README.md +++ b/.pair/knowledge/skill-conventions/README.md @@ -8,7 +8,7 @@ Single-source explanations for patterns that recur across many `SKILL.md` files. | Idempotency | [idempotency.md](idempotency.md) | 28 skills | | Graceful degradation | [graceful-degradation.md](graceful-degradation.md) | 34 skills | | Way-of-working / PM-tool resolution | [way-of-working-pm-resolution.md](way-of-working-pm-resolution.md) | 18 skills | -| Template resolution (adoption override > KB default, file-existence check) | [template-resolution.md](template-resolution.md) | 16 skills | +| Template resolution (adoption override > KB default, file-existence check) | [template-resolution.md](template-resolution.md) | all template-linking skills | | `/pair-capability-record-decision` invocation contract | [record-decision-contract.md](record-decision-contract.md) | 19 skills | | Output Format shapes (Decision / Report) | [output-shapes.md](output-shapes.md) | 10 skills (documentation-only — no logic change) | | To-issues triage (extend vs create) | [to-issues-triage.md](to-issues-triage.md) | 2 composing skills (new convention — no prior duplication to extract) | diff --git a/apps/website/content/docs/customization/templates.mdx b/apps/website/content/docs/customization/templates.mdx index 40de745f..bba2e741 100644 --- a/apps/website/content/docs/customization/templates.mdx +++ b/apps/website/content/docs/customization/templates.mdx @@ -69,8 +69,8 @@ The AI reads these templates when creating the corresponding artifacts — but i | --- | --- | --- | | **subdomain-template.md** | `/pair-capability-map-subdomains`, `/pair-capability-map-contexts` (reads Volatility) | Subdomain definition: classification, Volatility, boundaries | | **bounded-context-template.md** | `/pair-capability-map-contexts` | Context definition: responsibilities, per-relationship strength/distance/volatility assessment, Encapsulated Knowledge, Change Vectors | -| **context-map-template.md** | `/pair-capability-record-decision` (DDR flow), `/pair-capability-map-contexts` (via [context-map maintenance](/docs/reference/guidelines-catalog)) | Context map: ubiquitous language, entities, common rules | -| **subdomain-context-template.md** | `/pair-capability-map-subdomains` / `/pair-capability-map-contexts` (lazy per-subdomain split) | Per-subdomain slice of the context map, co-located with the subdomain catalog entry | +| **context-map-template.md** | [context-map maintenance](/docs/reference/guidelines-catalog) guideline (read while maintaining the context map) | Context map: ubiquitous language, entities, common rules | +| **subdomain-context-template.md** | [context-map maintenance](/docs/reference/guidelines-catalog) guideline (lazy per-subdomain split) | Per-subdomain slice of the context map, co-located with the subdomain catalog entry | ### Quality & Operations diff --git a/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md b/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md index 814e40ec..ac62290d 100644 --- a/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md +++ b/packages/knowledge-hub/dataset/.pair/knowledge/guidelines/architecture/design-patterns/context-map-maintenance.md @@ -66,8 +66,8 @@ Any skill that touches domain scope (brainstorm's domain step, refine-story's re ## Related Documents -- **[Context Map Template](../../collaboration/templates/context-map-template.md)** — the dispatcher + index + core artifact this guideline maintains -- **[Subdomain Context Template](../../collaboration/templates/subdomain-context-template.md)** — the lazy-split sibling file +- **[Context Map Template](../../collaboration/templates/context-map-template.md)** — the dispatcher + index + core artifact this guideline maintains (resolve override-first per [template resolution](../../../skill-conventions/template-resolution.md)) +- **[Subdomain Context Template](../../collaboration/templates/subdomain-context-template.md)** — the lazy-split sibling file (resolve override-first per [template resolution](../../../skill-conventions/template-resolution.md)) - **[Strategic Subdomain Definition](strategic-subdomain-definition.md)** — subdomain catalog and classification, source of the mirrored Volatility column - **[Domain-Driven Design](domain-driven-design.md)** — overall DDD principles this guideline operationalizes as a maintenance process - **[Decision Records](../decision-frameworks/README.md)** — ADR/DDR process for domain decisions cited during conflict-flagging diff --git a/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md b/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md index dae0183c..881434f5 100644 --- a/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md +++ b/packages/knowledge-hub/dataset/.pair/knowledge/skill-conventions/README.md @@ -8,7 +8,7 @@ Single-source explanations for patterns that recur across many `SKILL.md` files. | Idempotency | [idempotency.md](idempotency.md) | 28 skills | | Graceful degradation | [graceful-degradation.md](graceful-degradation.md) | 34 skills | | Way-of-working / PM-tool resolution | [way-of-working-pm-resolution.md](way-of-working-pm-resolution.md) | 18 skills | -| Template resolution (adoption override > KB default, file-existence check) | [template-resolution.md](template-resolution.md) | 16 skills | +| Template resolution (adoption override > KB default, file-existence check) | [template-resolution.md](template-resolution.md) | all template-linking skills | | `/record-decision` invocation contract | [record-decision-contract.md](record-decision-contract.md) | 19 skills | | Output Format shapes (Decision / Report) | [output-shapes.md](output-shapes.md) | 10 skills (documentation-only — no logic change) | | To-issues triage (extend vs create) | [to-issues-triage.md](to-issues-triage.md) | 2 composing skills (new convention — no prior duplication to extract) | diff --git a/packages/knowledge-hub/src/conformance/template-resolution.test.ts b/packages/knowledge-hub/src/conformance/template-resolution.test.ts index 56a0c358..e5143e78 100644 --- a/packages/knowledge-hub/src/conformance/template-resolution.test.ts +++ b/packages/knowledge-hub/src/conformance/template-resolution.test.ts @@ -12,6 +12,11 @@ import { collectSkillFiles, collectSkillMarkdownFiles } from '../tools/skills-co const DATASET_SKILLS = join(__dirname, '../../dataset/.skills') const CONVENTIONS = join(__dirname, '../../dataset/.pair/knowledge/skill-conventions') const MIRROR = join(__dirname, '../../../../.claude/skills') +// The guidelines corpus (dataset source + generated repo root) — the skills scan +// above never sees it, yet a template use-site lives here (context-map-maintenance.md, +// Major #2). Both roots are byte-consistent mirrors; scan both. +const DATASET_GUIDELINES = join(__dirname, '../../dataset/.pair/knowledge/guidelines') +const ROOT_GUIDELINES = join(__dirname, '../../../../.pair/knowledge/guidelines') const CONVENTION_FILE = join(CONVENTIONS, 'template-resolution.md') const CONVENTION = readFileSync(CONVENTION_FILE, 'utf-8') @@ -22,14 +27,16 @@ const POINTER = /template-resolution\.md/ const TEMPLATE_LINK = /collaboration\/templates\/[a-z0-9-]*template\.md/ /** - * Every skill Markdown file (SKILL.md AND auxiliary composed files) under a - * skills root that links at least one collaboration template. Walks all `.md`, - * not just SKILL.md, so template links in disclosed files (merge-and-cascade.md, - * post-review-merge.md, …) cannot escape the pointer invariant (story #314). + * Every Markdown file under a corpus root that links at least one collaboration + * template. Walks all `.md`, not just SKILL.md, so template links in disclosed + * skill files (merge-and-cascade.md, post-review-merge.md, …) AND in the + * guidelines corpus (context-map-maintenance.md) cannot escape the pointer + * invariant (story #314). `collectSkillMarkdownFiles` is a generic recursive + * `.md` walker — reused here for both the skills and guidelines trees. */ -function templateLinkingFiles(skillsRoot: string): { rel: string; content: string }[] { - return collectSkillMarkdownFiles(skillsRoot) - .map(f => ({ rel: relative(skillsRoot, f), content: readFileSync(f, 'utf-8') })) +function templateLinkingFiles(root: string): { rel: string; content: string }[] { + return collectSkillMarkdownFiles(root) + .map(f => ({ rel: relative(root, f), content: readFileSync(f, 'utf-8') })) .filter(({ content }) => TEMPLATE_LINK.test(content)) } @@ -128,3 +135,37 @@ describe('AC2 — key template consumers resolve override-first (dataset + mirro expect(content).toMatch(POINTER) }) }) + +// Major #2 blind-spot: the resolution fix also lives in the guidelines corpus +// (context-map-maintenance.md), which the skills scan never reaches. Same +// invariant applied to the guidelines tree (dataset + generated root) so a +// regression that drops the pointer from a guideline's template use-site fails. +describe('every template-linking guideline carries the resolution pointer (AC5, Major #2)', () => { + const guidelineRoots: { root: string; label: string }[] = [ + { root: DATASET_GUIDELINES, label: 'dataset' }, + { root: ROOT_GUIDELINES, label: 'generated root' }, + ] + const CONTEXT_MAP_GUIDELINE = join( + 'architecture', + 'design-patterns', + 'context-map-maintenance.md', + ) + + it.each(guidelineRoots)( + '$label: no guideline links a collaboration template without the resolution pointer', + ({ root }) => { + const offenders = templateLinkingFiles(root) + .filter(({ content }) => !POINTER.test(content)) + .map(({ rel }) => rel) + expect(offenders).toEqual([]) + }, + ) + + it.each(guidelineRoots)( + '$label: context-map-maintenance.md is actually reached by the scan (guards the Major #2 fix)', + ({ root }) => { + const scanned = templateLinkingFiles(root).map(({ rel }) => rel) + expect(scanned).toContain(CONTEXT_MAP_GUIDELINE) + }, + ) +}) From a1f52971f53052bd6f43c48a096812e064635b68 Mon Sep 17 00:00:00 2001 From: Gianluca Carucci Date: Thu, 23 Jul 2026 17:51:33 +0200 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20implement-batch=20=E2=80=94=20revi?= =?UTF-8?q?ew-cycle=20noise=20reduction=20(bundled)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Orchestration change (distinct concern from #314), bundled here per maintainer request to merge together. The review<->fix loop is one logical cycle, so it no longer spams the PR per round: - first review IS posted on the PR (the independent review artifact); - fix<->re-review rounds accumulate in .pair/working/reviews/.md (re-reviewer stays blind to it — prior findings arrive via prompt); - at convergence ONE synthesized remediation comment is posted, written contextually to the first review, then the log is deleted; - on escalation the log is flushed to the PR. +6 workflow tests (first-review-posts / re-review-silent / fix-logs / synthesis / clean-first-review-no-synthesis). Applies from the next run (this run used the prior script). Note: resume path posts a session-entry review by design. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/workflows/implement-batch.js | 46 +++++++++++++++++++-- .claude/workflows/implement-batch.test.mjs | 48 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/.claude/workflows/implement-batch.js b/.claude/workflows/implement-batch.js index edbf01e9..a0c00142 100644 --- a/.claude/workflows/implement-batch.js +++ b/.claude/workflows/implement-batch.js @@ -263,12 +263,27 @@ async function driveStory(story) { // nonActionable is NOT a scope filter — "not this story's original scope" alone // never qualifies; only "fixing it would be genuinely wrong" does. See ADL // decision-log/2026-07-11-agent-execution-layer.md (amended 2026-07-18). + // + // PR-COMMENT POLICY (noise reduction — the loop is ONE logical cycle): + // - The FIRST review IS posted on the PR (the independent review artifact). + // - The fix<->re-review rounds are NOT commented per round; each round is appended + // to a working log `.pair/working/reviews/.md` (orchestrator-side audit; the + // re-reviewer stays BLIND to it — it receives prior findings via the prompt). + // - At convergence ONE synthesized remediation comment is posted, written + // CONTEXTUALLY to the first review (maps each finding -> resolution + accepted + // dispositions + final verdict); the log is then deleted. + // - On escalation the log is flushed to the PR so the human sees the state (kept). + // The workflow runs in a sandbox (no FS/gh), so file + comment work is delegated to + // agents running in the worktree. + const reviewLog = `.pair/working/reviews/${story.id}.md` let round = 0 let prevFindings = [] let accepted = [] + let remediated = false while (true) { + const first = round === 0 const review = await agent( - `Independently review PR #${pr.prNumber} for story ${tag}, following /pair-process-review. ${revWtClause(story)} Review ONLY from the story's acceptance criteria, the PR diff+description, and the code. Do NOT read .pair/working/. Report EVERY finding regardless of severity (including minor/nit), using the code-review-template vocabulary: each finding = \`location\` (File:Line), \`severity\` ∈ {${SEVERITIES}}, \`description\` (issue + impact), \`recommendation\`; verdict ∈ {${VERDICTS}}. Set \`nonActionable: true\` ONLY if fixing it would be genuinely WRONG (byte-consistent with a source of truth, matches an existing convention/already-tracked deferred plan, resolves only after merge, etc.) — being outside this story's originally stated scope is NOT by itself a reason to mark something nonActionable: a real, fixable gap found during review gets fixed in this same PR unless it is large enough to warrant its own story (state that explicitly in the description if so). Whenever you set \`nonActionable: true\`, ALSO set \`disposition\` — a specific reason that replaces the bare label: write exactly \`Deferred to #\` when the finding belongs to a separate tracked story (file one via /pair-capability-write-issue if none exists yet), otherwise a concrete by-design reason (\`By convention …\` / \`Historical record\` / \`Forward-ref to unbuilt #\` / \`Resolves after merge\`); never leave "non-actionable" as the only explanation. ${round > 0 ? `Verify these prior findings were genuinely resolved: ${JSON.stringify(prevFindings)}.` : ''} Return findings and a verdict.`, + `Independently review PR #${pr.prNumber} for story ${tag}, following /pair-process-review. ${revWtClause(story)} Review ONLY from the story's acceptance criteria, the PR diff+description, and the code. Do NOT read .pair/working/. Report EVERY finding regardless of severity (including minor/nit), using the code-review-template vocabulary: each finding = \`location\` (File:Line), \`severity\` ∈ {${SEVERITIES}}, \`description\` (issue + impact), \`recommendation\`; verdict ∈ {${VERDICTS}}. Set \`nonActionable: true\` ONLY if fixing it would be genuinely WRONG (byte-consistent with a source of truth, matches an existing convention/already-tracked deferred plan, resolves only after merge, etc.) — being outside this story's originally stated scope is NOT by itself a reason to mark something nonActionable: a real, fixable gap found during review gets fixed in this same PR unless it is large enough to warrant its own story (state that explicitly in the description if so). Whenever you set \`nonActionable: true\`, ALSO set \`disposition\` — a specific reason that replaces the bare label: write exactly \`Deferred to #\` when the finding belongs to a separate tracked story (file one via /pair-capability-write-issue if none exists yet), otherwise a concrete by-design reason (\`By convention …\` / \`Historical record\` / \`Forward-ref to unbuilt #\` / \`Resolves after merge\`); never leave "non-actionable" as the only explanation. ${first ? `This is the FIRST review: POST your full review report as a PR comment on #${pr.prNumber} (code-review-template structure) AND return findings + verdict.` : `This is a RE-REVIEW: do NOT post any PR comment (the orchestrator synthesizes the cycle at the end). Return findings + verdict only. Verify these prior findings were genuinely resolved: ${JSON.stringify(prevFindings)}.`} Return findings and a verdict.`, { agentType: 'reviewer', phase: 'Review', label: `rev:${tag} r${round}`, effort: 'xhigh', schema: REVIEW_SCHEMA }, ) const findings = review?.findings ?? [] @@ -276,21 +291,44 @@ async function driveStory(story) { accepted = findings.filter((f) => f.nonActionable) // Converge once nothing actionable remains (by-design findings don't block). if (actionable.length === 0) break - if (round >= MAX_FIX_ROUNDS || review?.needsHumanDecision) + if (round >= MAX_FIX_ROUNDS || review?.needsHumanDecision) { + if (remediated) + await agent( + `Story ${tag}: the review<->fix loop is escalating to a human (non-convergence or a design disagreement). ${wtClause(story)} Read the review log \`${reviewLog}\` and post ONE comment on PR #${pr.prNumber} — written as a response to the first code-review comment — summarizing the rounds so far (per finding: what was attempted + current state) and the still-open actionable findings: ${JSON.stringify(actionable)}. Do NOT delete the log (the human acts on it). Do NOT merge.`, + { agentType: 'implementer', phase: 'Review', label: `flush:${tag}`, model: 'sonnet', effort: 'medium' }, + ) return { story, prNumber: pr.prNumber, status: 'escalate', findings: actionable, acceptedFindings: accepted } + } round++ prevFindings = actionable + remediated = true // FIX — implementer resumes checkpoint (if present) + resolves actionable findings. + // Logs the round to the working review log INSTEAD of posting a per-round PR comment. const fix = await agent( - `Resume story ${tag}. ${wtClause(story)} Read the checkpoint if present (/pair-capability-checkpoint $mode=resume); otherwise work from the PR diff + code. Resolve EVERY one of these actionable review findings on PR #${pr.prNumber} — including minor/nit, do not defer any: ${JSON.stringify(prevFindings)}. Commit, push, and post a remediation comment mapping each finding to what changed. Only for a genuine design disagreement set needsHumanDecision instead of forcing a fix. Do NOT merge.`, + `Resume story ${tag}. ${wtClause(story)} Read the checkpoint if present (/pair-capability-checkpoint $mode=resume); otherwise work from the PR diff + code. Resolve EVERY one of these actionable review findings on PR #${pr.prNumber} — including minor/nit, do not defer any: ${JSON.stringify(prevFindings)}. Commit and push. Do NOT post a remediation PR comment; INSTEAD append this round to the working log \`${reviewLog}\` (create it if absent): list each finding and exactly what changed to resolve it, with commit refs. Only for a genuine design disagreement set needsHumanDecision instead of forcing a fix. Do NOT merge.`, { agentType: 'implementer', phase: 'Review', label: `fix:${tag} r${round}`, effort: 'high', schema: FIX_SCHEMA }, ) if (!fix) return { story, prNumber: pr.prNumber, status: 'failed-fix' } - if (fix.needsHumanDecision) + if (fix.needsHumanDecision) { + if (remediated) + await agent( + `Story ${tag}: escalating a design disagreement to a human. ${wtClause(story)} Read \`${reviewLog}\` and post ONE comment on PR #${pr.prNumber} (response to the first review) summarizing the remediation rounds so far and the open decision. Do NOT delete the log. Do NOT merge.`, + { agentType: 'implementer', phase: 'Review', label: `flush:${tag}`, model: 'sonnet', effort: 'medium' }, + ) return { story, prNumber: pr.prNumber, status: 'escalate', findings: prevFindings, acceptedFindings: accepted } + } } + // Converged. If any remediation happened, post ONE synthesized remediation comment + // (contextual to the first review) and delete the working log. If the first review was + // already clean (no remediation), the first-review comment stands alone — nothing to do. + if (remediated) + await agent( + `Story ${tag} converged: the latest independent re-review found zero actionable findings. ${wtClause(story)} Read the review log \`${reviewLog}\`. Post ONE remediation comment on PR #${pr.prNumber}, written as a direct RESPONSE to the first code-review comment: map each finding from the first review (and any surfaced during remediation) to how it was resolved (with commit refs), list any accepted/non-actionable findings with their dispositions (${JSON.stringify(accepted)}), and state the final verdict (review clean). This single comment IS the durable audit of the review<->fix cycle. Then DELETE \`${reviewLog}\`. Do NOT merge.`, + { agentType: 'implementer', phase: 'Review', label: `synth:${tag}`, model: 'sonnet', effort: 'medium' }, + ) + // STOP at the merge boundary — human decides the merge. return { story, prNumber: pr.prNumber, status: 'ready-for-merge', acceptedFindings: accepted } } diff --git a/.claude/workflows/implement-batch.test.mjs b/.claude/workflows/implement-batch.test.mjs index 4bfa134c..911b811f 100644 --- a/.claude/workflows/implement-batch.test.mjs +++ b/.claude/workflows/implement-batch.test.mjs @@ -223,3 +223,51 @@ test('empty batch: no agent calls at all (contracts skipped too)', async () => { assert.equal(calls.length, 0) assert.deepEqual(result.batch, []) }) + +test('review noise policy: first review posts, re-review is silent, fix logs to working, convergence synthesizes ONE remediation', async () => { + let revCall = 0 + const dispatch = (prompt, opts) => { + if (opts.agentType === 'contract-generator') return { status: 'cache-hit', contract: validContract() } + if (opts.agentType === 'reviewer') { + revCall++ + // round 0: one actionable finding; round 1 (re-review): clean → converge + return revCall === 1 + ? { verdict: 'Rework', findings: [{ location: 'a.ts:1', severity: 'Minor', description: 'd', recommendation: 'r' }] } + : { verdict: 'Approved', findings: [] } + } + if (opts.phase === 'Implement') return { gatesPassed: true, branch: 'b' } + if (opts.phase === 'PR') return { prNumber: 7 } + if (opts.label?.startsWith('synth:')) return 'posted' + return { fixed: true } // fix step + } + const { result, calls } = await runWorkflow({ args: { stories: [STORY] }, dispatch }) + + const reviews = calls.filter(c => c.opts.agentType === 'reviewer') + assert.equal(reviews.length, 2, 'first review + one re-review') + assert.ok(reviews[0].prompt.includes('This is the FIRST review: POST'), 'first review is posted on the PR') + assert.ok(reviews[1].prompt.includes('do NOT post any PR comment'), 're-review posts no comment') + + const fix = calls.find(c => c.opts.label?.startsWith('fix:')) + assert.ok(fix.prompt.includes('append this round to the working log'), 'fix logs the round, no per-round PR comment') + assert.ok(fix.prompt.includes('.pair/working/reviews/292.md'), 'working log is per-story') + + const synth = calls.find(c => c.opts.label?.startsWith('synth:')) + assert.ok(synth, 'a synthesis step runs at convergence') + assert.ok( + synth.prompt.includes('Post ONE remediation comment') && synth.prompt.includes('DELETE'), + 'convergence posts ONE remediation comment then deletes the log', + ) + assert.equal(result.batch[0].status, 'ready-for-merge') +}) + +test('clean first review: no remediation comment, no synthesis step (first-review comment stands alone)', async () => { + const { calls } = await runWorkflow({ + args: { stories: [STORY] }, + dispatch: stdDispatch({ contractResult: { status: 'cache-hit', contract: validContract() } }), + }) + assert.ok(!calls.some(c => c.opts.label?.startsWith('synth:')), 'no synthesis when first review is already clean') + assert.ok(!calls.some(c => c.opts.label?.startsWith('fix:')), 'no fix round when nothing actionable') + const reviews = calls.filter(c => c.opts.agentType === 'reviewer') + assert.equal(reviews.length, 1, 'exactly one (first) review') + assert.ok(reviews[0].prompt.includes('This is the FIRST review: POST')) +}) From 96873dc29a5ebaa4725b30e6a8ec8657c0eca809 Mon Sep 17 00:00:00 2001 From: Gianluca Carucci Date: Thu, 23 Jul 2026 19:50:38 +0200 Subject: [PATCH 5/5] chore: close re-review minors on implement-batch escalation paths - flush-on-escalate branch now covered by a test (non-convergence -> flush with open findings, no synthesis) [Minor #1] - design-disagreement escalation passes the open findings to the flush agent, symmetric with the MAX_FIX_ROUNDS branch [Minor #2] - failed-fix return surfaces reviewLog so an orphaned working log is discoverable by the human / next resume [Minor #3] 13/13 workflow tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/workflows/implement-batch.js | 6 ++++-- .claude/workflows/implement-batch.test.mjs | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.claude/workflows/implement-batch.js b/.claude/workflows/implement-batch.js index a0c00142..72a1784e 100644 --- a/.claude/workflows/implement-batch.js +++ b/.claude/workflows/implement-batch.js @@ -309,11 +309,13 @@ async function driveStory(story) { `Resume story ${tag}. ${wtClause(story)} Read the checkpoint if present (/pair-capability-checkpoint $mode=resume); otherwise work from the PR diff + code. Resolve EVERY one of these actionable review findings on PR #${pr.prNumber} — including minor/nit, do not defer any: ${JSON.stringify(prevFindings)}. Commit and push. Do NOT post a remediation PR comment; INSTEAD append this round to the working log \`${reviewLog}\` (create it if absent): list each finding and exactly what changed to resolve it, with commit refs. Only for a genuine design disagreement set needsHumanDecision instead of forcing a fix. Do NOT merge.`, { agentType: 'implementer', phase: 'Review', label: `fix:${tag} r${round}`, effort: 'high', schema: FIX_SCHEMA }, ) - if (!fix) return { story, prNumber: pr.prNumber, status: 'failed-fix' } + // failed-fix: the fixer died mid-round; a partial working log may exist. Surface + // its path in the return so the human / next resume can find (and clean) it. + if (!fix) return { story, prNumber: pr.prNumber, status: 'failed-fix', reviewLog: remediated ? reviewLog : undefined } if (fix.needsHumanDecision) { if (remediated) await agent( - `Story ${tag}: escalating a design disagreement to a human. ${wtClause(story)} Read \`${reviewLog}\` and post ONE comment on PR #${pr.prNumber} (response to the first review) summarizing the remediation rounds so far and the open decision. Do NOT delete the log. Do NOT merge.`, + `Story ${tag}: escalating a design disagreement to a human. ${wtClause(story)} Read \`${reviewLog}\` and post ONE comment on PR #${pr.prNumber} (response to the first review) summarizing the remediation rounds so far, the still-open findings (${JSON.stringify(prevFindings)}) and the open decision. Do NOT delete the log. Do NOT merge.`, { agentType: 'implementer', phase: 'Review', label: `flush:${tag}`, model: 'sonnet', effort: 'medium' }, ) return { story, prNumber: pr.prNumber, status: 'escalate', findings: prevFindings, acceptedFindings: accepted } diff --git a/.claude/workflows/implement-batch.test.mjs b/.claude/workflows/implement-batch.test.mjs index 911b811f..27fefada 100644 --- a/.claude/workflows/implement-batch.test.mjs +++ b/.claude/workflows/implement-batch.test.mjs @@ -271,3 +271,23 @@ test('clean first review: no remediation comment, no synthesis step (first-revie assert.equal(reviews.length, 1, 'exactly one (first) review') assert.ok(reviews[0].prompt.includes('This is the FIRST review: POST')) }) + +test('non-convergence: MAX_FIX_ROUNDS escalation flushes the working log to the PR with the open findings, no synthesis', async () => { + const finding = { location: 'x.ts:1', severity: 'Minor', description: 'never fixed', recommendation: 'r' } + const dispatch = (prompt, opts) => { + if (opts.agentType === 'contract-generator') return { status: 'cache-hit', contract: validContract() } + if (opts.agentType === 'reviewer') return { verdict: 'Rework', findings: [finding] } // never converges + if (opts.phase === 'Implement') return { gatesPassed: true, branch: 'b' } + if (opts.phase === 'PR') return { prNumber: 7 } + if (opts.label?.startsWith('flush:')) return 'flushed' + return { fixed: true } // fix step + } + const { result, calls } = await runWorkflow({ args: { stories: [STORY] }, dispatch }) + + assert.equal(result.batch[0].status, 'escalate') + const flush = calls.find(c => c.opts.label?.startsWith('flush:')) + assert.ok(flush, 'escalation posts a flush comment') + assert.ok(flush.prompt.includes('x.ts:1'), 'flush carries the still-open findings') + assert.ok(flush.prompt.includes('.pair/working/reviews/292.md') && flush.prompt.includes('Do NOT delete the log'), 'flush reads the log and keeps it for the human') + assert.ok(!calls.some(c => c.opts.label?.startsWith('synth:')), 'no synthesis on escalation') +})