Skip to content

Coverage ratchet has no adopter path — flag documented, capability pair-internal, enabling it is a silent no-op #409

Description

@rucka

Problem

#372 (PR #405) ships the coverage-baseline ratchet and documents it in the adopter-facing KB — the flag, the push-not-PR trigger, the bot-PR landing, the credential. But the capability is pair-internal, and nothing tells an adopter so:

  • the logic lives in packages/knowledge-hub/src/tools/coverage-baseline-ratchet.ts, not among the shipped provider-agnostic assets (coverage-gate.sh, tier-resolve.sh);
  • /pair-capability-setup-gates never asks about the nested Coverage baseline commit-back flag, never records it, and never emits the CI step.

So an adopter who sets Coverage baseline commit-back: enabled gets a silent no-op — the worst shape of failure for an opt-in: the config says on, the docs say what it does, nothing happens, and nothing complains.

Found by the independent review of PR #405 (finding M4). #405 closes the honesty gap by stating the pair-internal scope in the guideline and the config example. This story closes the capability gap.

Scope

  1. Ship the ratchet as a provider-agnostic asset alongside coverage-gate.sh, or define the extension point through which an adopter's pipeline invokes it. The decision matters: coverage-gate.sh is shell and provider-agnostic by design, whereas the ratchet is a TypeScript module in a pair-internal package — porting versus exposing is an ADR-worthy call, not an implementation detail.
  2. /pair-capability-setup-gates: ask about the nested flag (only when the parent Coverage guardrail is enabled), record it, and emit the step when it is on.
  3. Remove the now-stale note in setup-gates/SKILL.md:199 (both corpora) that reads "automated commit-back is story Coverage baseline auto commit-back — CI persists/ratchets the baseline (opt-in, on top of #282) #372" — true until [US-372] feat: coverage baseline auto commit-back — monotonic ratchet as a bot PR, opt-in and off by default #405 merges, wrong after.
  4. Remove the pair-internal caveat this story makes obsolete, from tier-aware-pipeline.md and coverage-config-example.md (both corpora).
  5. The credential is the adopter's, not pair's: document what scope they must provision (contents: write + pull requests: write, no protection bypass) and what happens without it (warning, verdict untouched — AC6 of Coverage baseline auto commit-back — CI persists/ratchets the baseline (opt-in, on top of #282) #372).

Why it is not part of #372

#372's own AC set is about the ratchet's behaviour (monotonic, terminating, PR-not-push, degrade-to-warning), and all six are met. The adopter path is a distinct deliverable with its own decision (asset vs extension point) and its own surface (setup-gates interview + generated pipeline). Widening #372 to cover it would have meant editing setup-gates/SKILL.md, a file two currently-parked PRs (#389, #390) already modify — a three-way contention for a one-line change, in a PR whose disjointness is a stated business rule.

Dependencies

Notes

The framework default stays disabled throughout. Nothing here turns the ratchet on for anyone; it makes turning it on actually do something.

Classification

risk:yellow · cost:green · coupling: not assessed

Matrix — per dimension
Dimension Tier Source Note
Service/domain criticality yellow KB default no Criticality Table declared ⇒ Medium (D21)
Change/diff risk yellow file ownership setup-gates/SKILL.md is modified by #389 and #390; the guideline/asset pair by #405
Business impact yellow subdomain class Integration & Process Standardization (Supporting) — but this is the adopter-facing surface, so a silent no-op is a credibility cost, not just an internal one
Security relevance yellow credential the adopter must provision a write credential; documenting the wrong scope is an authorization error on their repo, not ours
Coupling balance not assessed absent decided by T-1 (asset vs extension point)

Acceptance Criteria

Given-When-Then:

  1. Given an adopter with Coverage guardrail: enabled
    When /pair-capability-setup-gates runs
    Then it asks about Coverage baseline commit-back only then — the nested flag is never offered for a guardrail that is off

  2. Given the adopter answers enabled
    When the pipeline is generated
    Then the commit-back step is emitted, and the generated step is the same shape pair runs — not a second implementation that can drift from it

  3. Given the adopter answers disabled, or is never asked
    When the pipeline is generated
    Then no step is emitted and behaviour is byte-identical to today

  4. Given the ratchet is reachable by an adopter
    When they read the guideline and the config example
    Then the pair-internal caveats added by Coverage baseline auto commit-back — CI persists/ratchets the baseline (opt-in, on top of #282) #372 are gone — a caveat that outlives the limitation it describes is worse than none

  5. Given setup-gates' Notes
    When read after Coverage baseline auto commit-back — CI persists/ratchets the baseline (opt-in, on top of #282) #372 merges
    Then the stale "automated commit-back is story Coverage baseline auto commit-back — CI persists/ratchets the baseline (opt-in, on top of #282) #372" line is corrected — it is true only until that merge

  6. Given the adopter has not provisioned COVERAGE_RATCHET_TOKEN
    When the generated step runs
    Then it warns naming the missing credential and the gate's verdict is unchanged — Coverage baseline auto commit-back — CI persists/ratchets the baseline (opt-in, on top of #282) #372's AC6 must hold on the generated step too, not only on pair's own

Business Rules

  • Default stays disabled everywhere. This story makes enabling it work; it turns it on for nobody.
  • One implementation, not two. Whatever mechanism T-1 chooses, the adopter's step and pair's own step must run the same logic. A ported copy is a drift source.
  • Least privilege documented as the adopter's own: contents: write + pull requests: write, no protection bypass. The credential lives in their repo, so the scope is guidance, not configuration we control.

Edge Cases

  • Guardrail enabled but no committed baseline — bootstrap-only mode; the ratchet must propose nothing rather than invent a first baseline (Coverage baseline auto commit-back — CI persists/ratchets the baseline (opt-in, on top of #282) #372 keeps the first baseline human).
  • Adopter's base branch is protected — the bot-PR shape already handles it; the generated step must not assume an unprotected base.
  • Adopter's CI is not GitHub Actions — the guideline is provider-agnostic; state plainly what is Actions-specific rather than implying portability the step does not have.

Definition of Done

  • All acceptance criteria implemented and verified
  • T-1's decision recorded as an ADR (it changes what pair ships, not just how)
  • Conformance covers the caveat removal, so a stale caveat cannot survive
  • pnpm quality-gate green; PR follows pr-template.md

Task Breakdown

  • T-1: Decide asset-vs-extension-point and record the ADR
  • T-2: Make the ratchet reachable per T-1 (ship as asset, or expose the entry point)
  • T-3: setup-gates: the nested question, gated on the parent flag, recorded in the adoption
  • T-4: setup-gates: emit the step when enabled; nothing when not
  • T-5: Remove the pair-internal caveats (guideline + config example, both corpora) and the stale #372 note
  • T-6: Document the adopter's credential scope where they will read it; conformance for T-5

Dependency Graph

T-1 ── T-2 ──┬── T-3 ── T-4 ── T-6
             └── T-5 ──────────┘

T-1 gates everything: whether the step invokes a shipped asset or a declared entry point changes what T-3 asks and what T-4 writes. T-5 is independent of the mechanism but must not land before T-2, or the caveat is removed while still true.

AC Coverage

AC Tasks
1 nested question T-3
2 step emitted, one implementation T-2, T-4
3 disabled ⇒ byte-identical T-4
4 caveats gone T-5
5 stale note T-5
6 AC6 holds on the generated step T-2, T-6

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