Skip to content

Dedicated mirror-realignment command (local, deterministic) — replaces 'pair update' as the gate remedy, run by publish-pr #419

Description

@rucka

Story Statement

As a contributor whose push was blocked by the mirror-equality guard
I want an explicit command that realigns the generated mirrors from the local dataset, named by the gate and run by /pair-capability-publish-pr
So that the remedy is deterministic and one step — instead of a command that updates the knowledge base to its latest published version, which is not what the situation calls for

Where: a root script, the gate's remedy message (PRE_PUSH_REMEDY + DEVELOPMENT.md + its docs-site twin), and /pair-capability-publish-pr.

Epic Context

Parent Epic: none — standalone tooling/process defect
Status: Refined
Priority: P1 (Should-Have) — the drift class this addresses bit seven times in one day, three of them because a mirror was hand-ported instead of regenerated

Classification

risk:yellow · cost: green (not projected — Active: risk only)

Matrix — per dimension
Dimension Tier Source Note
Service/domain criticality green Criticality Table tech/risk-matrix.md now lists every touched deployable (packages/dev-tools, apps/website, packages/knowledge-hub) as Low — no Medium default, no unlisted-conservative-High (D21)
Change/diff risk yellow story scope Multiple non-paired modules touched — root script, packages/dev-tools, DEVELOPMENT.md, the docs-site twin — plus a publish-pr step that commits; change-risk.dataset-mirror-pairs collapses only the skill's own dataset+mirror pair into one module, not the rest, so shared code is still touched
Business impact yellow subdomain class Integration & Process Standardization (Supporting) — the new Development Tooling Standards (Generic) subdomain covers lint/format/TS/markdownlint/brand only, not gate/CI/process tooling, so this story's scope stays Supporting
Security relevance green path heuristic No credentials; the command reads the local dataset and writes generated files inside the repo
Coupling balance green subdomain volatility + integrations Wraps an existing CLI path; no new integration

Tier = yellow (max rule). tier-resolution.default-artifact-downgrade does not apply: Change/diff risk is yellow and is not an eligible artifact dimension (only Service/domain-criticality and/or Business-impact yellows qualify), so the downgrade-to-green condition is not met. Gate checks for 🟡: lint + type + build + unit.

Re-classified 2026-08-14 against corrected adoption inputs (Criticality Table, tier-resolution.default-artifact-downgrade override, Development Tooling Standards Generic subdomain — none of which existed at prior classification): Service/domain criticality moves yellow → green; tier stays risk:yellow, confirmed not lowered, held by Change/diff risk and Business impact.

Problem

When a dataset file is edited, its generated mirrors (.claude/skills/**, .pair/**) go out of sync, and the mirror-equality guard blocks the push. The documented remedy — in PRE_PUSH_REMEDY, DEVELOPMENT.md and the docs-site twin — is pair update.

That is the wrong command for the job. DEVELOPMENT.md:154 documents pair update as "Update knowledge base to latest version": it resolves and installs the published KB. What the situation needs is regeneration from the local dataset, which is a different operation — pair update --source <local dataset>, the form already exercised by CP3 and by the source-resolution smoke scenario.

Two consequences, both observed:

  • Disproportionate remedy. To fix a reformatted table, a contributor is told to run a knowledge-base update. Three of the seven drift incidents on record were hand-ports — someone chose to edit the mirror by hand rather than run that command, which is exactly the behaviour the guard exists to prevent.
  • Non-determinism where determinism is required. A remedy that can pull a newer KB makes the fix depend on what has been published, not on what is in the working tree.

Decision that produced this card (maintainer, 2026-08-05)

Recorded against the Open Decision section of ADL 2026-07-31-pre-push-gate-is-check-only.md, which asked whether the gate should apply fixes (option A) and/or pnpm format should realign the mirrors (option B).

Neither, as written. pair update is an install command, not a formatting one: folding it into pnpm format would mean every format could touch the KB, and the gate applying fixes at pre-push cannot reach the commits being pushed. Instead: a dedicated, explicit command, named by the gate and executed at the point where a commit is still possible — PR creation. pnpm format stays formatting.

Acceptance Criteria

Functional Requirements

Given-When-Then Format:

  1. Given a repo whose generated mirrors are out of sync with the local dataset
    When the new command runs
    Then the mirrors are regenerated from the local dataset and no published KB version is fetched or installed

  2. Given the command
    When it is run twice in a row
    Then the second run produces no diff (idempotent)

  3. Given the mirror-equality guard fails
    When the contributor reads the remedy
    Then it names this command, and no longer pair update — in all three places that state the remedy (PRE_PUSH_REMEDY, DEVELOPMENT.md, the docs-site twin), whose load-bearing paragraph must stay byte-identical except for the ADL link form

  4. Given /pair-capability-publish-pr
    When it prepares a branch for a pull request
    Then it runs the command and, if it produced changes, commits them as their own commit with a message naming the regeneration — never mixed into a feature commit

  5. Given publish-pr running on a branch with no drift
    When the command runs
    Then nothing is committed and nothing is reported as done — a no-op stays silent

  6. Given pnpm format
    When this story lands
    Then it is unchanged: formatting does not regenerate mirrors, and does not reach outside format scope

  7. Given the command runs where git or the dataset is unavailable
    When it fails
    Then it exits non-zero with the reason — it never reports success over a no-op (the same fail-loud rule the formatter wrappers follow)

  8. Given a contributor who wants the check without the write
    When they need to know whether mirrors are in sync
    Then the existing guard already answers that; this command never runs in "check" mode, so there is exactly one writer and one checker

Business Rules

  • Regeneration is local and deterministic. The source is the working tree's dataset, never a published release.
  • pnpm format stays formatting. The scope boundary is the reason this is a separate command rather than a step inside it.
  • The write happens where a commit is possible. Pre-push is too late (the commits exist); PR creation is the point where regeneration can still become part of the branch.
  • Generated output is committed separately, so a reviewer can tell authored changes from regenerated ones at a glance.
  • The remedy is stated once and identically in the three documents that advertise it.

Edge Cases and Error Handling

  • Drift in a file the branch never touched (regeneration picks up someone else's earlier miss): commit it separately anyway, and say so — surprising, but better than pushing known-stale output.
  • Regeneration produces a huge diff (a dataset-wide change): the separate commit keeps the PR readable; the reviewer sees one regeneration commit rather than noise across the feature commits.
  • Command run on a dirty working tree: it writes generated files only; unstaged authored changes must be left untouched, and publish-pr must not commit them.
  • A mirror hand-edited earlier: regeneration overwrites it. That is correct — and the reason the guard exists — so the message should say the mirror was regenerated, not "fixed".
  • publish-pr invoked twice: idempotence (AC2) means the second run commits nothing.

Definition of Done Checklist

  • All acceptance criteria implemented and verified
  • Test written first: drifted mirror ⇒ regenerated; second run ⇒ no diff; missing dataset ⇒ non-zero exit
  • Root script added, wrapping the local-source regeneration (no new generation logic)
  • Remedy text updated in PRE_PUSH_REMEDY, DEVELOPMENT.md and the docs-site twin, with the byte-identical paragraph preserved
  • /pair-capability-publish-pr runs it and commits separately when there is a diff; silent no-op otherwise
  • pnpm format verified unchanged (gate:composition still green)
  • Dataset edited as source for the skill change, mirror regenerated, skills:conformance green
  • 🟡 gate checks green: lint + type + build + unit

Story Sizing and Sprint Readiness

Refined Story Points

Final Story Points: 3 (M)
Confidence Level: High
Sizing Justification: The script is a thin wrapper over a CLI path that already exists (--source with a local dataset). The work is the wiring: three documents whose paragraph must stay byte-identical, and a publish-pr step that commits — the only part that needs care, because a step that commits on the contributor's behalf must be predictable and silent when idle.

Sprint Capacity Validation

Sprint Fit Assessment: Yes
Development Time Estimate: ~0.5 day
Testing Time Estimate: ~0.25 day

Dependencies and Coordination

Story Dependencies

Prerequisite Stories: none
Dependent Stories: none — but it makes #393 cheaper: that card's regeneration step becomes this one command, and its "regenerate, don't hand-port" rule gets a tool to point at
Shared Components: pair-capability-publish-pr (mutex with any card touching that skill), root package.json scripts (coordinates with #414, which rewrites the format scripts), DEVELOPMENT.md

External Dependencies

Related decisions: ADL 2026-07-31-pre-push-gate-is-check-only.md — this story closes its Open Decision · the check-only rule (the gate reports, never fixes) · #414 (brings the mirrors into format-check scope, which is what makes a correct regeneration command necessary rather than nice)

Validation and Testing Strategy

Acceptance Testing Approach

Testing Methods: unit/integration tests over a fixture repo — drifted mirror regenerated, idempotence on a second run, non-zero exit with no dataset, unstaged authored changes untouched. Then an end-to-end pass: edit a dataset file, run publish-pr on a branch, and confirm one separate regeneration commit and a green gate.
Test Data Requirements: fixture repo with a dataset and its mirrors, one deliberately drifted.

Success Metrics

  • The gate's remedy is a single command that always produces the same result
  • No hand-ported mirror appears in a PR after this lands
  • pnpm format behaves exactly as before

Notes and Additional Context

Refinement Session Insights (2026-08-05):

  1. The remedy was the wrong command, not just a missing automation. pair update updates the KB to its latest published version; the situation needs regeneration from the local dataset. That mismatch is the most plausible explanation for three hand-ports on record.
  2. Both options in the ADL were declined as written: the gate applying fixes writes files the contributor never staged and cannot reach the commits being pushed; folding an install command into pnpm format makes formatting able to change the knowledge base.
  3. PR creation is the right moment — the latest point where the regenerated output can still enter the branch, and an explicit, tracked act rather than a hook side effect.
  4. This becomes load-bearing once [tech-debt] format:check does not cover files outside workspace packages (.pair/**, scripts/**, root-level JSON) #414 lands: with mirrors inside format-check scope, a contributor without this command would be pushed toward formatting a generated file by hand, which another guard forbids.

Team Concerns: publish-pr will commit on the contributor's behalf. That is acceptable only because the content is generated output committed separately and labelled — the same reasoning that made a write-mode formatter in the gate unacceptable applies here, and the difference is that the write is explicit, isolated in its own commit, and happens where the author is still in control.

Technical Analysis

Implementation Approach

Technical Strategy: expose the existing local-source regeneration as a named root script; point the three remedy texts at it; call it from publish-pr and commit its output separately when non-empty.

Key Components:

  • root package.json — the new script (wrapper only, no new generation logic)
  • packages/dev-tools/src/quality-gates/pre-push-gate-composition.tsPRE_PUSH_REMEDY text (unit-tested)
  • DEVELOPMENT.md + apps/website/content/docs/contributing/development-setup.mdx — the byte-identical paragraph
  • pair-capability-publish-pr — the step and its commit
  • tests: fixture-repo regeneration, idempotence, failure path

Data Flow: local dataset → regeneration (--source) → generated mirrors → (in publish-pr) separate commit → gate green.

Integration Points: the pre-push gate's message, publish-pr, the mirror-equality guards, #414's format scope.

Design: not required

Technical Risks and Mitigation

Risk Impact Mitigation
The script resolves a published KB instead of the local dataset The remedy becomes non-deterministic and can pull unrelated changes — the defect this card fixes AC1 + a test asserting no network/version resolution occurs
publish-pr commits authored changes along with generated ones The contributor loses control of their own commit boundaries AC4/edge case: generated files only, separate commit, unstaged authored work untouched
Remedy text updated in two of three places The three copies diverge, and the byte-identical paragraph stops being a drift signal DoD names all three; the paragraph equality is the existing check
A no-op run that reports work Contributors stop reading publish-pr output AC5: silent when there is nothing to do
Command silently succeeding with an empty file set A green remedy that fixed nothing — the same class as the formatter wrappers' empty-set trap AC7: non-zero exit with the reason

Task Breakdown

  • T-1: Root script for local-source mirror regeneration
  • T-2: Rename the remedy in PRE_PUSH_REMEDY (with unit tests)
  • T-3: Rename the remedy in DEVELOPMENT.md and its docs-site twin
  • T-4: Wire regeneration + separate commit into /pair-capability-publish-pr
  • T-5: End-to-end test — dataset edit → publish-pr → one regeneration commit
  • T-6: Verify pnpm format / gate:composition unchanged and close the ADL's Open Decision

Dependency Graph

T-1 ──┬── T-2 ──┐
      ├── T-3 ──┤
      └── T-4 ── T-5 ──┴── T-6

AC Coverage

AC Tasks
AC-1 (local regen, no published KB fetch) T-1, T-5
AC-2 (idempotent — second run no diff) T-1, T-5
AC-3 (remedy renamed in all 3 places, paragraph intact) T-2, T-3
AC-4 (publish-pr runs it, separate commit) T-4, T-5
AC-5 (no-op stays silent) T-4, T-5
AC-6 (pnpm format unchanged) T-6
AC-7 (fails loud, non-zero exit + reason) T-1, T-5
AC-8 (no check mode — one writer, one checker) T-1

T-1: Root script for local-source mirror regeneration

Priority: P0 | Estimated Hours: 3h | Bounded Context: Integration & Process Standardization

Summary: Expose the existing pair update --source <local dataset> path as a named root package.json script — a thin wrapper, no new generation logic — that regenerates .claude/skills/** and .pair/** from the working tree's dataset and never touches a published KB version.

Type: Feature Implementation

Description: This is the deterministic remedy the story introduces. It must call the CLI's existing local-source resolution (the same form CP3 and the source-resolution smoke scenario already exercise: pair update --source packages/knowledge-hub/dataset) — no new regeneration logic, no network/version resolution. It must be idempotent (a second run produces no diff) and must fail loud (non-zero exit with the reason) when git or the dataset is unavailable, mirroring the fail-loud rule the formatter wrappers already follow (packages/dev-tools/src/quality-gates/pre-push-gate-composition.ts header comment on sync-version/test:perf). It never runs in "check" mode — the existing mirror-equality guard is the checker; this is the sole writer.

Acceptance Criteria:

  • Primary deliverable: a root package.json script (e.g. mirrors:regenerate) that runs pair update --source <local dataset path> and nothing else.
  • Quality standard: no new mirror-generation logic — delegates entirely to the CLI's existing --source path.
  • Integration requirement: consumed by T-4 (publish-pr) and named by T-2/T-3 (remedy text).
  • Verification method: fixture-repo tests (T-1's own test-first pass, per DoD) — drifted mirror ⇒ regenerated; second run ⇒ no diff; missing dataset/no git ⇒ non-zero exit with reason.

Technical Requirements:

  • Functionality: reads the local dataset only; never resolves or installs a published KB version.
  • Performance: n/a beyond existing CLI performance.
  • Security: none — no credentials involved (reads local dataset, writes generated files inside the repo).
  • Compatibility: same environments the CLI already supports.

Implementation Approach:

  • Technical Design: root package.json script string invoking the CLI binary with --source pointed at packages/knowledge-hub/dataset (or the CLI's own default-source-dir convention if one already resolves this without the flag — confirm against apps/pair-cli/src/config/kb-resolver.ts before hardcoding a path).
  • Bounded Context & Modules: Integration & Process Standardization (packages/knowledge-hub, root scripts).
  • Files to Modify/Create:
    • package.json — add the new script.
  • Technical Standards: architecture.md § Knowledge Base Distribution, tech-stack.md § Runtime & CLI tooling.

Dependencies:

  • Technical: apps/pair-cli's --source resolution (already exists, per ADL 2026-07-31-pre-push-gate-is-check-only.md).
  • Tasks: none — this is the foundation task.

Implementation Steps:

  1. Write the failing tests first (fixture repo: drifted mirror, idempotent second run, missing-dataset failure) — bug-fix workflow applies (test-first).
  2. Add the root script wrapping pair update --source <local dataset>.
  3. Run the tests, confirm green.
  4. Manually verify: edit a dataset file, run the script, confirm the mirror regenerates and a second run is a no-op.

Testing Strategy:

  • Unit Tests: fixture repo with a dataset + drifted mirror — asserts regeneration; asserts idempotence on re-run; asserts non-zero exit with a clear reason when git/dataset is unavailable; asserts no network call / no published-version resolution occurs (mitigates the "resolves a published KB instead" risk from the Technical Risks table).
  • Integration Tests: none at this task level (covered in T-5 end-to-end).
  • Manual Testing: run twice locally against a real drifted mirror.

Notes: No new generation logic — this task is wiring only, per the story's Technical Strategy.


T-2: Rename the remedy in PRE_PUSH_REMEDY (with unit tests)

Priority: P0 | Estimated Hours: 1.5h | Bounded Context: Integration & Process Standardization

Summary: Update PRE_PUSH_REMEDY in packages/dev-tools/src/quality-gates/pre-push-gate-composition.ts to name T-1's new command instead of pair update, and update its unit tests.

Type: Bug Fix

Description: PRE_PUSH_REMEDY currently reads: "...re-sync the generated .claude/skills/** copies (pair update) in the same commit...". Replace pair update with the new command name from T-1. The load-bearing paragraph shared with DEVELOPMENT.md/the docs-site twin must stay byte-identical except for the ADL link form (per AC-3) — coordinate wording with T-3 before finalizing either file. pre-push-gate-composition.test.ts already asserts properties of PRE_PUSH_REMEDY (e.g. that REMEDY_SCRIPT exists in package.json) — extend or add an assertion that the remedy names the new script and that the script exists, mirroring the existing REMEDY_SCRIPT check (lines ~259-266 of the source file).

Acceptance Criteria:

  • Primary deliverable: PRE_PUSH_REMEDY string updated to name T-1's script instead of pair update.
  • Quality standard: existing test suite extended to assert the new command is named and exists as a root script (same pattern as the current REMEDY_SCRIPT existence check).
  • Integration requirement: wording of the shared paragraph matches T-3's edit byte-for-byte (except the ADL link form).
  • Verification method: pnpm --filter @pair/dev-tools test green; pnpm gate:composition green.

Technical Requirements:

  • Functionality: string content change only, no behavioral change to the gate logic.
  • Performance: n/a.
  • Security: n/a.
  • Compatibility: n/a.

Implementation Approach:

  • Technical Design: edit the PRE_PUSH_REMEDY constant string; add/extend a test asserting the new script name is referenced and present in package.json scripts.
  • Bounded Context & Modules: Integration & Process Standardization (packages/dev-tools/src/quality-gates).
  • Files to Modify/Create:
    • packages/dev-tools/src/quality-gates/pre-push-gate-composition.ts — update PRE_PUSH_REMEDY.
    • packages/dev-tools/src/quality-gates/pre-push-gate-composition.test.ts — update/add assertions.
  • Technical Standards: testing strategy README.

Dependencies:

  • Technical: T-1's script name must be finalized first.
  • Tasks: T-1.

Implementation Steps:

  1. Confirm T-1's final script name.
  2. Update PRE_PUSH_REMEDY text, keeping the shared paragraph byte-identical to T-3's version (except ADL link form).
  3. Update/extend the unit test asserting the remedy names an existing script.
  4. Run pnpm --filter @pair/dev-tools test and pnpm gate:composition.

Testing Strategy:

  • Unit Tests: assert PRE_PUSH_REMEDY contains the new script name and no longer contains pair update as the fix instruction; assert the named script exists in root package.json.
  • Integration Tests: gate:composition self-check stays green.
  • Manual Testing: trigger the guard locally and read the printed remedy.

Notes: Coordinate wording with T-3 before either file is finalized — the byte-identical-paragraph requirement is cross-checked by an existing test (per the story's "the paragraph equality is the existing check" mitigation).


T-3: Rename the remedy in DEVELOPMENT.md and its docs-site twin

Priority: P0 | Estimated Hours: 1h | Bounded Context: Integration & Process Standardization

Summary: Update the same remedy paragraph in DEVELOPMENT.md (root) and apps/website/content/docs/contributing/development-setup.mdx (the docs-site twin) to name T-1's new command, keeping the two byte-identical to each other and to T-2's PRE_PUSH_REMEDY text except for the ADL link form (relative markdown link in DEVELOPMENT.md vs. the absolute GitHub URL in the .mdx).

Type: Documentation

Description: Both files currently document pair update as the two-step remedy for a format:check-caused mirror drift (DEVELOPMENT.md:130-138, development-setup.mdx:83-91) and both list pair update in a CLI-commands table (DEVELOPMENT.md:155) with the description "Update knowledge base to latest version" — that table entry is correct and must NOT be changed (it documents pair update itself, not the remedy); only the remedy paragraph's parenthetical changes. docs:staleness (apps/website/lib/docs-staleness-check.ts) enforces derivation from the dataset for skill/capability counts — verify this specific prose paragraph is not one of its derived checks (it reads as hand-authored dev-process prose, not a dataset-derived fact) before editing freely.

Acceptance Criteria:

  • Primary deliverable: DEVELOPMENT.md and development-setup.mdx remedy paragraphs both name T-1's script instead of pair update.
  • Quality standard: the two paragraphs are byte-identical to each other and to T-2's PRE_PUSH_REMEDY wording, differing only in the ADL link form.
  • Integration requirement: the CLI-commands table row for pair update (DEVELOPMENT.md:155) is left untouched — it documents a different command.
  • Verification method: pnpm docs:staleness green; manual diff of the two paragraphs confirms byte-equality modulo the link.

Technical Requirements:

  • Functionality: documentation-only change.
  • Performance: n/a.
  • Security: n/a.
  • Compatibility: n/a.

Implementation Approach:

  • Technical Design: text edit in both files, mirroring T-2's finalized wording.
  • Bounded Context & Modules: Integration & Process Standardization (docs distribution) / Knowledge & Standards (authored prose) — per the context map's authored-by/disseminated-by split.
  • Files to Modify/Create:
    • DEVELOPMENT.md — remedy paragraph (lines ~130-138).
    • apps/website/content/docs/contributing/development-setup.mdx — remedy paragraph (lines ~83-91).
  • Technical Standards: integration-process-standardization.md § Data Ownership (docs-site as dissemination, not authorship).

Dependencies:

  • Technical: T-1's script name must be finalized first; wording coordinated with T-2.
  • Tasks: T-1, T-2 (wording).

Implementation Steps:

  1. Update DEVELOPMENT.md's remedy paragraph with T-1's script name.
  2. Update development-setup.mdx's remedy paragraph identically, swapping only the ADL link form (relative path → absolute GitHub URL, already the existing pattern).
  3. Diff both paragraphs against T-2's PRE_PUSH_REMEDY wording to confirm byte-equality (modulo the link).
  4. Run pnpm docs:staleness.

Testing Strategy:

  • Unit Tests: none (docs-only) — covered by the existing byte-equality check the story references ("the paragraph equality is the existing check").
  • Integration Tests: pnpm docs:staleness.
  • Manual Testing: side-by-side read of all three copies.

Notes: Do not touch the pair update row in the CLI commands table — only the remedy prose.


T-4: Wire regeneration + separate commit into /pair-capability-publish-pr

Priority: P0 | Estimated Hours: 2.5h | Bounded Context: Development Collaboration (skill authoring) / Integration & Process Standardization (the step's effect)

Summary: Add a step to /pair-capability-publish-pr (.claude/skills/pair-capability-publish-pr/SKILL.md) that runs T-1's regeneration command while preparing the branch, and — only if it produced a diff — commits the generated files as their own commit, named as a regeneration, never mixed into a feature commit. A no-op run commits nothing and reports nothing.

Type: Feature Implementation

Description: Per the story's edge cases: the command must write generated files only, leaving unstaged authored changes untouched, and publish-pr must never commit those authored changes. Drift in a file the branch never touched must still be committed (separately, and reported), since pushing known-stale output is worse. The natural insertion point is Phase 2 ("Resolve Merge Strategy & Prepare Base"), after the branch is confirmed pushed and before the PR body is composed — i.e., before anything reads the diff for the PR body (Phase 3 reads git diff --name-only <base-branch>...HEAD for Changes Made, which should already reflect the regeneration commit if any).

Acceptance Criteria:

  • Primary deliverable: a new step in the SKILL.md algorithm (with output-format row) that runs T-1's command and conditionally commits.
  • Quality standard: silent no-op (AC-5) — no commit, no output row noise, when the command produces no diff.
  • Integration requirement: the commit, when made, is separate from feature commits and named as a regeneration (e.g. commit message chore: regenerate mirrors from local dataset); it must not touch unstaged authored changes.
  • Verification method: T-5's end-to-end test.

Technical Requirements:

  • Functionality: run-then-check-diff-then-conditionally-commit, mirroring the pattern already used elsewhere in the skill for "read-back confirms the write" (Phase 4's re-read discipline).
  • Performance: negligible — one CLI invocation plus a git status/git diff check.
  • Security: none.
  • Compatibility: n/a.

Implementation Approach:

  • Technical Design: Act: run T-1's script; Check: does git status --porcelain show changes confined to the generated mirror paths; Skip: none → report nothing, move on; Act: stage only the generated paths and commit with a naming convention that says "regenerated", never "fixed"; Verify: git log shows exactly one new commit, and git status still shows the pre-existing unstaged authored changes (if any) untouched.
  • Bounded Context & Modules: .claude/skills/pair-capability-publish-pr/SKILL.md (Development Collaboration authorship — skills/workflows content, per the context map).
  • Files to Modify/Create:
    • .claude/skills/pair-capability-publish-pr/SKILL.md — new algorithm step + output-format row (composed skills table update if /pair-capability-verify-quality's scope is affected — likely not).
    • packages/knowledge-hub/dataset/.skills/... — the dataset source for this skill (mirror regenerated afterward, per the story's own DoD item: "Dataset edited as source for the skill change, mirror regenerated").
  • Technical Standards: idempotency convention.

Dependencies:

  • Technical: T-1's script must exist and be invocable.
  • Tasks: T-1.

Implementation Steps:

  1. Edit the SKILL.md dataset source (packages/knowledge-hub/dataset/.skills/...), never the generated .claude/skills/** copy directly.
  2. Add the new step to Phase 2 (or a clearly labeled sub-step) per the design above.
  3. Add the corresponding output-format row (e.g. ├── Mirrors: [regenerated — commit <sha> | no drift — nothing to commit]).
  4. Regenerate the mirror via T-1's own command and confirm skills:conformance is green.
  5. Update the HALT/Graceful Degradation sections only if this step introduces a new failure mode worth documenting (e.g. T-1's command failing — non-zero exit — should HALT publish-pr before the PR is touched, consistent with Phase 1's gate-red HALT).

Testing Strategy:

  • Unit Tests: n/a at the skill-authoring level (skills are prose, not code) — verified by T-5's behavioral end-to-end test instead.
  • Integration Tests: T-5.
  • Manual Testing: run publish-pr once on a branch with a deliberately drifted mirror and once on a clean branch; confirm the commit appears only in the first case.

Notes: This is the task that most needs care per the story's sizing rationale ("a step that commits on the contributor's behalf must be predictable and silent when idle").


T-5: End-to-end test — dataset edit → publish-pr → one regeneration commit

Priority: P1 | Estimated Hours: 2h | Bounded Context: Integration & Process Standardization

Summary: Fixture-repo integration test exercising the full path: edit a dataset file, run publish-pr on a branch, confirm exactly one separate regeneration commit is produced and the gate stays green — plus the no-op and dirty-tree edge cases.

Type: Testing

Description: Covers the story's "Acceptance Testing Approach" end-to-end pass and the edge cases not already covered by T-1's unit tests: a mirror hand-edited earlier gets overwritten and reported as "regenerated" (not "fixed"); a dataset-wide drift produces one commit, not noise across feature commits; running publish-pr twice commits nothing the second time (AC-2 applied at the publish-pr level, not just the script level).

Acceptance Criteria:

  • Primary deliverable: an automated test (fixture repo, following the existing InMemoryFileSystemService/fixture-repo conventions used elsewhere in packages/dev-tools) exercising: drifted mirror → regenerated + committed separately; clean branch → silent no-op; missing dataset/no git → non-zero exit, publish-pr HALTs before touching the PR; unstaged authored changes left untouched after the run.
  • Quality standard: test data matches the story's stated test-data requirement ("fixture repo with a dataset and its mirrors, one deliberately drifted").
  • Integration requirement: exercises T-1 through T-4 together, not each in isolation.
  • Verification method: test suite green; pnpm quality-gate green on the fixture scenario.

Technical Requirements:

  • Functionality: covers AC-1, AC-2, AC-4, AC-5, AC-7 in combination.
  • Performance: n/a.
  • Security: n/a.
  • Compatibility: n/a.

Implementation Approach:

  • Technical Design: reuse the fixture-repo pattern from source-resolution.sh / existing dev-tools test fixtures rather than inventing a new harness.
  • Bounded Context & Modules: packages/dev-tools (or wherever publish-pr's underlying automation lives, if it is code rather than pure skill-prose — confirm during implementation; if publish-pr is skill-prose only, this becomes a scripted smoke scenario alongside scripts/smoke-tests/scenarios/source-resolution.sh).
  • Files to Modify/Create:
    • New test/fixture file(s) — exact path TBD during implementation, following the 1:1 module-to-test-file convention where code exists, or a new smoke-test scenario otherwise.
  • Technical Standards: testing strategy README.

Dependencies:

  • Technical: T-1, T-4 must both be implemented.
  • Tasks: T-1, T-4.

Implementation Steps:

  1. Set up a fixture repo with a dataset and a deliberately drifted mirror.
  2. Run the full path (T-1's script via T-4's publish-pr step) and assert one regeneration commit.
  3. Re-run on a clean fixture and assert silence (no commit, no report).
  4. Break the fixture (remove dataset / detach git) and assert non-zero exit + HALT.
  5. Add an unstaged authored-change fixture and assert it survives the run untouched.

Testing Strategy:

  • Unit Tests: n/a (this task IS the integration-level test).
  • Integration Tests: as described above.
  • Manual Testing: one live run against this repo's own working tree, right before closing the story.

Notes: This task is the story's acceptance test, not incidental coverage — it directly implements "Acceptance Testing Approach" from the story body.


T-6: Verify pnpm format / gate:composition unchanged and close the ADL's Open Decision

Priority: P2 | Estimated Hours: 1h | Bounded Context: Integration & Process Standardization

Summary: Confirm pnpm format behaves exactly as before this story (AC-6), confirm gate:composition stays green, and update ADL 2026-07-31-pre-push-gate-is-check-only.md to record that its Open Decision is now closed by this story.

Type: Documentation / Verification

Description: pnpm format must not gain any mirror-regeneration behavior — the whole point of a dedicated command is that formatting stays formatting. This task is the final DoD sweep: run the full pnpm quality-gate locally, confirm format is untouched (diff package.json's format/format:check/prettier:fix/mdlint:fix scripts against main), and record closure of the ADL's Open Decision (the story body already states "this story closes its Open Decision" — the ADL file itself should reflect that per the project's decision-recording convention).

Acceptance Criteria:

  • Primary deliverable: pnpm format scripts diff empty against main; pnpm gate:composition green; ADL updated to show the Open Decision as closed, referencing this story.
  • Quality standard: no drive-by changes to format/format:check scripts.
  • Integration requirement: n/a.
  • Verification method: git diff main -- package.json shows no change to the format/format:check/prettier:*/mdlint:* script entries; pnpm gate:composition.

Technical Requirements:

  • Functionality: n/a — verification task.
  • Performance: n/a.
  • Security: n/a.
  • Compatibility: n/a.

Implementation Approach:

  • Technical Design: run the gate, diff the scripts, edit the ADL.
  • Bounded Context & Modules: .pair/adoption/decision-log/2026-07-31-pre-push-gate-is-check-only.md.
  • Files to Modify/Create:
    • .pair/adoption/decision-log/2026-07-31-pre-push-gate-is-check-only.md — mark the Open Decision closed, linking this story.
  • Technical Standards: record-decision guidance (ADL maintenance is normally /pair-capability-record-decision's job — invoke it rather than hand-editing, per the project's "record decisions" quick rule).

Dependencies:

  • Technical: T-1 through T-5 complete.
  • Tasks: T-1, T-2, T-3, T-4, T-5.

Implementation Steps:

  1. Run pnpm quality-gate and confirm green.
  2. Diff package.json's format-related scripts against main.
  3. Invoke /pair-capability-record-decision (or hand-edit if unavailable) to close the ADL's Open Decision, referencing this story.
  4. Final DoD checklist sweep against the story body.

Testing Strategy:

  • Unit Tests: n/a.
  • Integration Tests: pnpm quality-gate (full local run).
  • Manual Testing: read the ADL to confirm the Open Decision section now reads as closed.

Notes: This is the story's closing task — nothing here should require new code if T-1 through T-5 are correctly scoped.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

risk:yellowClassification: medium risk tiertech-debtTracked technical debt (living backlog, R7.2 — never blocks a PR)user storyWork item representing a user story

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions