Skip to content

[#416] feat: llms.txt drift gate - #475

Merged
rucka merged 9 commits into
mainfrom
feature/US-416-llms-txt-drift-gate
Sep 14, 2026
Merged

[#416] feat: llms.txt drift gate#475
rucka merged 9 commits into
mainfrom
feature/US-416-llms-txt-drift-gate

Conversation

@rucka

@rucka rucka commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

PR Information

PR Title: [#416] feat: llms.txt drift gate
Story/Epic: #416 — no parent epic (cross-cutting quality-gate coverage)
Type: Feature (tech-debt)
Priority: P2 (Could-Have)
Labels: user story, tech-debt, risk:green, pr-state:to-be-reviewed

Summary

What Changed

pnpm llms-index:check — a gate that regenerates .pair/llms.txt with the CLI's own generateLlmsTxt and requires byte equality with the committed file, printing the missing/extra lines and never writing the file. Wired into ci.yml and the root quality-gate.

Around it, three environment axes that would each make a byte-compared artifact machine-dependent are closed at the generator, plus the remedy the gate prints:

Axis Before Now
Collation localeCompare, no locale ⇒ runtime ICU default UTF-16 code-unit order
Line terminator nothing pinned; core.autocrlf=true rewrote all 570 lines .gitattributes (* text=auto eol=lf + the index by name); the gate diagnoses any \r
Path separator path.join.pair\knowledge\... on Windows posix.join for emitted paths; join for file-system access only
Printed remedy pair update — no such executable here pnpm llms-index:regen, a root script that exists

Why This Change

.pair/llms.txt is generated and tracked, and it is the index CLAUDE.md points every agent at. Nothing compared it to its generator, so adding a guideline without regenerating silently hid it from every agent — two independent misses are on record (story-local-markers.md; the two how-to guides dropped in #246 and still indexed ~5 months later).

A gate on a byte-compared artifact is only trustworthy if the artifact is byte-reproducible. Each axis above was measured as a full-file false positive on an untouched tree — collation: 458 of 560 entries move under ICU; terminator: 570 missing + 570 extra; separator: 562 missing + 562 extra — with a closing imperative that either cannot work or, obeyed, commits the damage.

Story Context

User Story: As a maintainer of the pair knowledge base, I want a quality gate that fails when .pair/llms.txt no longer matches what the generator would emit, so that the machine-readable KB index that CLAUDE.md points agents at cannot silently go stale.

Acceptance Criteria:

AC Covered by
1 — byte equality against generateLlmsTxt checkLlmsIndexDrift; compareIndex returns in-sync only on exact equality
2 — prints the missing/extra LINES formatDrift, multiset deltas (a duplicated line is drift a set view cannot see), with look-alike pairs rendered byte-escaped
3 — red on the PR, not later on main .github/workflows/ci.yml step llms-index:check
4 — same check locally and in CI root quality-gate chain + the same ci.yml step; gate:composition guards the chain
5 — names the regeneration command, never regenerates REGENERATION_COMMAND = 'pnpm llms-index:regen', asserted as a literal AND resolved against the real root package.json; the gate is handed a 3-method read-only fs slice, so "cannot write" is a type fact
6 — consumes #393's trailing-newline form expectations are always generateLlmsTxt's own output; the form is never re-litigated

Changes Made

Implementation Details

  • The gatepackages/dev-tools/src/quality-gates/llms-txt-drift-check.ts. Four outcomes as a discriminated union, because they have different remedies: in-sync, drift, broken-setup (no indexable section — an unfinished install, not a stale index), unreadable-index (the tracked file's own permission bits or a directory in its place). Reports the terminator (trackedCarriesCr), a BOM (trackedCarriesBom) and emptied sections, and puts the matching precondition in front of the closing imperative — a report that says "restore the tree first" and then closes with a bare "regenerate" delivers, in its own call to action, the damage the caution exists to prevent.
  • The remedypackages/dev-tools/src/quality-gates/llms-txt-regenerate.ts (pnpm llms-index:regen). The check's exact inverse: same generator, same tracked path, one file written. It runs the check first and refuses on broken-setup, unreadable-index, an unreadable tree, trackedCarriesCr and emptiedSections, printing the gate's own message — so obeying the printed advice can never be the thing that loses a section. On the WRITE_MODE_FORMATTERS offender list: a gate that ran its own remedy would hide the drift it exists to reveal.
  • Determinism at the generatorapps/pair-cli/src/registry/llms-generation.ts: code-unit ordering and posix.join for emitted paths. This is published-CLI behaviour, so it carries a patch changeset.
  • The read-only sliceLlmsSourceFs (3 read methods) replaces FileSystemService in generateLlmsTxt's signature; the writer declares its extra power as a separate two-method LlmsIndexSink.
  • #216's byte-equality guard was replaced, not duplicatedllms-index-conformance.test.ts keeps only its output-shape assertions.

Files Changed

  • Added: packages/dev-tools/src/quality-gates/llms-txt-drift-check.ts + .test.ts, packages/dev-tools/src/quality-gates/llms-txt-regenerate.ts + .test.ts, apps/pair-cli/src/registry/llms-generation.win32.test.ts, .gitattributes, .changeset/llms-txt-deterministic-order.md, 3 ADLs under .pair/adoption/decision-log/
  • Modified: apps/pair-cli/src/registry/llms-generation.ts, apps/pair-cli/src/registry/llms-index-conformance.test.ts, packages/dev-tools/src/quality-gates/pre-push-gate-composition.ts + .test.ts, packages/dev-tools/{package.json,tsconfig.json,README.md}, root package.json, .github/workflows/ci.yml, .pair/llms.txt, .pair/adoption/tech/way-of-working.md, DEVELOPMENT.md, apps/website/content/docs/contributing/development-setup.mdx

Services to Release

  • @pair/pair-cli (patch changeset): .pair/llms.txt is now emitted in a deterministic, locale-independent order and with POSIX separators on every platform. An adopter's next pair-cli install / pair-cli update rewrites the file once — uppercase-first entries sort before their lowercase siblings; a Windows adopter additionally stops shipping 562 backslash-separated, unresolvable links. Generated file, so a one-time diff, not a migration.

Testing

Test Coverage

  • Unit: 112 rows for the gate (terminator domain closed: LF / CRLF / mixed / \r\r\n / bare CR / CR+LF / stray CR at EOF; invisible-character rendering; the four outcomes), 11 for the regeneration command (write path, idempotence, BOM strip, and one row per refusal state), 53 for gate composition, 221 across the CLI registry.
  • E2E: pnpm --filter @pair/website e2e — 39 passed (a docs .mdx changed).
  • Manual/boundary probes: below — the parts a unit test cannot establish.

Test Results

pnpm quality-gate    ✅ exit 0 (full chain — Pre-merge tiering: disabled)
pnpm --filter @pair/website e2e   ✅ 39 passed
pnpm llms-index:check             ✅ .pair/llms.txt matches the generator

Testing Strategy

  • Happy path: a guideline added without regenerating ⇒ the gate names the exact missing line.
  • Edge cases: every outcome above, plus multiset (duplicated-line) drift, an empty tracked file, an absent one, order/whitespace-only difference, and look-alike pairs whose only difference is invisible.
  • Error handling: an unreadable KB tree and an unreadable index file are distinct outcomes with distinct recipes — conflating them sends a contributor to reinstall a knowledge base that is fine.
  • Cross-platform: llms-generation.win32.test.ts binds path to Node's own path.win32 (what require('path') returns on Windows) and asserts the generator emits the same bytes a POSIX machine emits, order included. On POSIX join and posix.join are the same function, so this is the only shape of test that can tell the fixed code from the broken code on a Linux CI.

Reviewer Guide

Boundary probes (run against the real producers, not stubs)

Claim Probe Result
A Windows run breaks the index Real path.win32 bound to the real generator over the real repo tree, output into the real compareIndex/formatReport before: 562 missing + 562 extra, 1140-line report, no caution naming the cause, closing on the bare imperative — after: in-sync
The printed remedy works Fresh git clone + pnpm install (nothing built), add a guideline, run the gate, type the printed command verbatim, re-run red → ✓ regenerated → green; git diff --stat = `.pair/llms.txt
The index's producer is pair-cli install / pair-cli update, and pair is not a binary pnpm pack of apps/pair-cli + its two workspace deps → npm i the three real tarballs into an empty npm init project → run the linked bin in an empty target dir packed manifest bin = {"pair-cli":"dist/cli.js"}; node_modules/.bin links pair-cli, no pair; which pair → not found. pair-cli install --offline --source packages/knowledge-hub/dataset wrote .pair/llms.txt (474 lines, sha1 733c39ed…); after tampering the file, pair-cli update restored it byte-identical. The retired remedy string pair update names no executable — which is why the gate prints pnpm llms-index:regen
Regenerating cannot fix a CR checkout Real script on the real repo with .pair/llms.txt converted to CRLF exit 1, file byte-identical, message = the gate's own with the rm + git checkout -- recipe
That recipe is the one that works core.autocrlf=true clone git add --renormalize stages nothing and leaves all 583 CR lines; rm + git checkout -- gives w/lf, 0 CRs, gate green — with core.autocrlf left at true (the eol=lf attribute overrides it)
Regenerating does fix a BOM Real regeneration on a BOM-prefixed index rewritten BOM-free, gate green — so that caution adds no precondition
The gate survives a fresh install pnpm install, no build, run the gate verdict returned (this is why it is ts-node -T: type-checking made the verdict depend on whether someone had built @pair/content-ops)

Testing the Changes

git checkout feature/US-416-llms-txt-drift-gate && pnpm install
pnpm llms-index:check                                  # green on an untouched tree
printf '# Probe\n' > .pair/knowledge/guidelines/probe.md
pnpm llms-index:check                                  # red, names the one missing line
pnpm llms-index:regen && pnpm llms-index:check         # green again, one file changed
rm .pair/knowledge/guidelines/probe.md && pnpm llms-index:regen

Documentation

Technical Decisions

ADL What it settles
2026-09-01-a-gate-imports-its-generator-by-source-and-gets-a-read-only-slice.md A gate imports the artifact-producing code by SOURCE path (costs paid explicitly: "composite": false, a @pair/content-ops devDependency that orders ts:check and resolves nothing, one hardcoded hop) and runs it transpile-only; code invoked by a gate takes the narrowest fs capability
2026-09-01-a-byte-compared-generated-artifact-sorts-by-codepoint.md A tracked, byte-compared artifact is byte-reproducible on any machine that checks it out — three rules: content-derived order, eol=lf pin + terminator diagnosis, POSIX separators in emitted paths
2026-09-03-a-gate-names-a-remedy-it-can-run.md A gate's remedy names a command that exists for its audience, and that command refuses the states its own cautions warn about

Consumed, not re-decided: 2026-08-11-a-mirror-guard-compares-the-transform.md, 2026-07-31-pre-push-gate-is-check-only.md, 2026-08-20-a-user-facing-cli-fix-carries-its-changeset.md.

Docs updated: DEVELOPMENT.md + its byte-identical docs-site twin (development-setup.mdx), way-of-working.md (Quality Gates), packages/dev-tools/README.md.

One command family per statement: the adoption bullet and the gate's own header name the index's producer as pair-cli install / pair-cli update (the published bin is pair-cli), and pair update appears only where it is explicitly labelled the retired spelling. A file that credits a command with generating the index and denies that binary exists 100 words later re-teaches, in the project's source of truth for the gate, the exact string ADL 2026-09-03 retired.

Dependencies & Related Work

Related PRs

Follow-up Work

@rucka rucka added user story Work item representing a user story tech-debt Tracked technical debt (living backlog, R7.2 — never blocks a PR) risk:green Classification: low risk tier labels Sep 1, 2026
@rucka rucka self-assigned this Sep 1, 2026
@rucka rucka added the pr-state:to-be-reviewed PR state: awaiting review / gate label Sep 1, 2026
@rucka

rucka commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict

risk:green · cost:n/aCHANGES-REQUESTED — the gate's own comparison is not environment-independent: on a pristine checkout of this branch, LC_ALL=cs_CZ.UTF-8 (or et_EE.UTF-8) makes pnpm llms-index:check red with 0 missing / 0 extra, and the fix it names (pair update) reddens CI for everyone else.

Open findings: 5.

PR: #475 · Author: rucka · Reviewer: independent reviewer agent · Date: 2026-09-01 · Story: US-416 · Type: feature (tech-debt)

Classification matrix — per dimension
Dimension Tier Source Note
Service/domain criticality green Criticality Table packages/dev-tools = Low
Change/diff risk green diff footprint +673/-35; one new gate module + test + wiring; generator change is a type narrowing only
Business impact yellow subdomain class Integration & Process Standardization (Supporting) — KB integrity
Security relevance green /pair-capability-assess-security No untrusted input, no network, no secrets, read-only FS by type
Coupling balance green path heuristic New cross-package SOURCE import dev-tools → pair-cli, argued in an ADL; no new integration

Tier = max(assessed) with the default-artifact-downgrade on Business impact → green, confirming refinement (no raise).

Assessments

Security — Input validation

Verdict: green — the only inputs are repo-local file paths derived from __dirname; nothing untrusted reaches the gate.

Security — Output handling

Verdict: green — output is console text on a dev/CI TTY; no encoding surface.

Security — Authentication / Authorization

Verdict: green — no auth surface; the gate runs in CI with the repo checkout it already has.

Security — Introduced vulnerabilities

Verdict: green — 0 introduced, 0 pre-existing. The FileSystemServiceLlmsSourceFs narrowing strictly reduces capability (no writeFile/rm/chmod reachable from the gate).

Cost

Verdict: cost:n/a — capability not run; no paid provider, no infra, no runtime footprint beyond one extra CI step (~seconds).

Architecture (Coupling)

Verdict: not assessed (/pair-capability-assess-coupling unavailable until #263). Reviewer note: the new dev-tools → apps/pair-cli/src/... source reach-in is a real coupling increase, argued and bounded in the ADL, with a loud compile-time failure mode.

Bug fix — Red test before fix

Verdict: not applicable — not a bug fix (tech-debt/coverage story).

Details

Findings by severity

Critical (must fix before merge)

  • none.

Major (must fix before merge)

  • apps/pair-cli/src/registry/llms-generation.ts:55entries.sort((a, b) => a.path.localeCompare(b.path)) uses the ambient collation, so the "expected" index is a function of the machine's locale, and the gate this PR adds turns that into a red build on an unmodified tree. Verified on this branch's real corpus (4d3013cf), by re-implementing generateLlmsTxt in plain JS and diffing against the tracked .pair/llms.txt: LC_ALL=cs_CZ.UTF-8equal: false, first divergence at line 276 — Czech collation treats ch as a digraph sorting after h, so checkpoint-template.md moves after code-review-template.md; LC_ALL=et_EE.UTF-8equal: false as well (en_US, da_DK, sv_SE, tr_TRequal: true today). Concrete failure: a contributor with a Czech/Estonian locale (or any CI image with LANG set to one) runs pnpm llms-index:check on a clean checkout and gets ❌ ... is not what the generator emits, 0 missing line(s), 0 extra line(s), "the difference is their order", and the instruction to run pair update — which regenerates in their collation; committing that result flips the gate red for en-US CI. The two sides ping-pong and neither can make the gate green. The story's edge case names this exact class ("locale-dependent sort … must fail on a content difference, not on an environment artefact"), and the determinism test only re-runs the generator twice inside one process, so it cannot see it. Fix: pin the collation — a.path.localeCompare(b.path, 'en-US') (or a module-level Intl.Collator('en-US')). Verified zero-diff: with 'en-US' pinned, the generated index is byte-identical (66389 chars) to the tracked file under cs_CZ, et_EE and da_DK, so no regeneration of .pair/llms.txt is needed. Add a regression case that runs the generator under a second collation (or asserts the pinned comparator's order on names like checkpoint-* / code-*, aa-* / ab-*), otherwise the determinism AC stays same-process-only.

Minor (must fix before merge — same bar as Major, just lower impact)

  • packages/dev-tools/src/quality-gates/llms-txt-drift-check.ts:125hasIndexableSection returns true as soon as one ## heading exists, so "partially installed" is detected only in the degenerate case where nothing at all is indexable (the test at :168 hedges this in its own name). Concrete failure: a tree where .pair/adoption/** is present but .pair/knowledge/** is missing (sparse checkout, interrupted pair install, a bad rebase dropping the directory) is reported as drift with ~300 extra lines plus "Regenerate with pair update and commit the result" — and a contributor who follows that instruction commits a truncated index that the gate then passes green. That is the silent staleness the story exists to close, reached through the gate's own advice. Fix: treat a sectionDefs directory that the tracked index still references but that no longer exists on disk as broken-setup (or require every previously-indexed top-level section to be present), so a vanished section is an unfinished setup rather than drift.
  • package.json:32 / packages/dev-tools/package.json:17 — the gate is not runnable on its own from a fresh clone, which is the property the ADL uses to reject the @pair/content-ops alternative ("the root step is also runnable on its own — pnpm llms-index:check"). packages/content-ops/dist/ is gitignored (git check-ignore.gitignore:2:dist/), ts-node here type-checks (confirmed: pnpm --filter @pair/dev-tools exec ts-node -e "const x: number = 'a'"error TS2322, exit 1), and the gate's source import pulls llms-generation.ts — with its import type { FileSystemService } from '@pair/content-ops' — into that program. So pnpm install && pnpm llms-index:check (exactly the sequence in this PR's own Reviewer Guide, which omits a build) dies with a module-resolution error instead of a drift verdict. Inside pnpm quality-gate and ci.yml the ordering saves it, which is why the branch is green. Fix: make the root script self-sufficient (turbo build --filter @pair/content-ops && pnpm --filter @pair/dev-tools llms-index:check), or state the build prerequisite in the Reviewer Guide and next to the script.
  • apps/pair-cli/src/registry/llms-index-conformance.test.ts:35 — comment left stale by the move: "Byte equality above pins whatever the generator emits, so without this case the wrong output would be asserted correct" — there is no byte-equality case above (or anywhere) in this file any more, and the sentence is the justification for the cases that remain. A reader hunting the byte-equality guard is sent to a case that no longer exists. Fix: re-point it at packages/dev-tools/src/quality-gates/llms-txt-drift-check.ts.
  • packages/dev-tools/src/quality-gates/llms-txt-drift-check.ts:211main() has no try/catch, and void main() at :222 discards the promise. Concrete failure: readdir throws EACCES/EMFILE on a KB subdirectory (permission-mangled checkout, container volume) → node prints an unhandled-rejection stack trace instead of any llms-index: message. The exit code is still non-zero (fail-safe), but the contributor sees a crash from a gate whose entire AC-2/AC-5 contribution is an actionable message. Fix: wrap the body and print a broken-setup-shaped message naming the path that failed.

Questions (informational, never blocking)

  • none.
Positive feedback
  • The FileSystemServiceLlmsSourceFs narrowing is the strongest part of the change: "this gate cannot write the file it judges" stops being a promise and becomes unrepresentable, and existing callers stay untouched by structural typing.
  • broken-setup as a third variant of a discriminated union (not an ok/detail bag) makes "a missing tree is not a stale index" impossible for a caller to conflate.
  • Replacing Auto-development eligibility tag from refinement classification #216's byte-equality vitest case rather than duplicating it — and keeping its non-drift shape assertions with the reason spelled out — is exactly the "two guards that drift apart" avoidance the story asked for.
  • The ADL argues the two alternatives a reviewer actually reaches for (move the generator into @pair/content-ops; widen rootDir instead of dropping composite) with the specific compiler errors observed, not in the abstract.
Functionality & requirements (AC coverage)
AC Verdict Evidence
1 — regenerate + byte compare met checkLlmsIndexDrift :196-207; compareIndex returns in-sync only on actual === expected
2 — missing/extra lines, not "files differ" met formatReport :149-177; tests at :73 and :95 assert the exact line text
3 — red on the PR, not later on main met ci.yml pull_request trigger has no .pair/** path exclusion (only .changeset/**), and the step runs unconditionally rather than through cacheable turbo test
4 — wired into ci.yml + root quality-gate met same command via the root alias in both; husky pre-push reaches it through quality-gate
5 — names the regen command, never writes met REGENERATION_COMMAND; the mtime assertion at :120 is the right proof, and broken-setup correctly withholds the command
6 — consumes #393's trailing-newline form met independently regenerated the index from the branch's corpus: byte-identical to the tracked file (66389 chars), single trailing newline
Edge — non-deterministic/locale-dependent sort not met Major finding above
Edge — tree absent or partially installed partial Minor finding above; only the "no indexable section at all" case is distinguished
Edge — tracked file empty met test :134
Testing & quality gates
  • Coverage: 14 fixture-based cases, all on temp trees; the real corpus is never read by the suite, so a legitimately added guideline cannot redden it — the right split against the gate's job.
  • Gap: main() (exit-code path) is untested; determinism is asserted same-process only (see Major).
  • Gates: not re-run here (no node_modules in the review worktree). Independently verified the branch is green on the check that matters: a faithful re-implementation of generateLlmsTxt reproduces the tracked .pair/llms.txt byte for byte under the default locale.
Adoption compliance
  • Degradation level: 1 — decision recorded before/with the code.
  • ADL present for both new conventions (source-path import; narrowest FS capability), with alternatives and consequences; way-of-working.md § Quality Gates updated in the same PR, matching the "guard over repo-wide state ⇒ root gate step" rule it extends.
  • No new external dependency (@pair/content-ops is an existing workspace package), so no tech-stack.md change is owed. Gate placement matches ADR-014 / the gate-tooling ADL: logic in a tested module, thin require.main CLI, root script delegating via pnpm --filter.
Tech debt
  • Dropping composite on @pair/dev-tools removes the compiler's objection to any future cross-package reach-in from that package; the ADL records this and asks that a second one be argued. Surfaced, not blocking.
Documentation
  • Module header, tsconfig comment at the edit site, ci.yml step comment, ADL and way-of-working.md all updated. One stale comment (Minor above).
Performance & deployment
  • One extra CI step; a single generator walk of .pair/** plus one file read, no repeated walks.
  • Reversible: the gate is check-only and writes nothing; removing the two wiring lines fully rolls it back.

@rucka

This comment has been minimized.

rucka added a commit that referenced this pull request Sep 1, 2026
…depoint

Review round 1 on PR #475.

- `llms-index:check` was a bare `ts-node`, so it type-checked a source file whose
  first line imports @pair/content-ops TYPES from dist/. On a fresh `pnpm install`
  the gate produced none of its three outcomes — TS2307 + a ts-node stack, exit 1.
  Now `ts-node -T`; type-checking that source is ts:check's job, and turbo ts:check
  is the task carrying the ^build edge.
- compareIndex diffed through Sets, so a duplicated entry line (the shape a
  "keep both sides" merge of llms.txt produces) reported `0 missing / 0 extra` plus
  "the difference is their order or whitespace" — wrong, in the case that most needs
  the diff. Multiset deltas now.
- localeCompare with no locale sorted the index by the runtime's ICU default: 458 of
  560 entries move under a small-icu Node, so the gate went red on an untouched tree
  and its own message told the contributor to commit the churn. Codepoint order;
  .pair/llms.txt regenerated through writeProjectLlmsTxt.
- main() had no try/catch: an unreadable KB dir (chmod 000) escaped as an unhandled
  rejection instead of the broken-setup outcome. Caught, reported, exit 1.
- A tree missing ONE whole section clears the broken-setup guard and is reported as
  drift telling the contributor to regenerate — which deletes that section from the
  index. Caution line when a tracked heading has zero generated entries.
- dev-tools README Tools table: the gate had no row (plus the two stale ones).

ADLs: codepoint-sort rule recorded; the source-import ADL gains the -T rationale.
rucka added a commit that referenced this pull request Sep 1, 2026
Round 2 of PR #475.

- formatDrift's closing paragraph branches on emptiedSections: a sparse tree
  reads "Once the tree is complete, regenerate with `pair update`", so the
  message no longer ends with the imperative the caution above it forbids.
- the catch-path test drives main() with an injected throwing readdir instead
  of an if-wrapped chmod probe that ran zero assertions under root; the chmod
  variant survives as an explicitly skipped case.
- ADL restated: the invariant is determinism, the comparator is UTF-16
  code-unit order (= codepoint for every BMP path). No comparator change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQJzGMhRqBRRboxMrRqFPP
@rucka
rucka force-pushed the feature/US-416-llms-txt-drift-gate branch from 4bc1f26 to a7de384 Compare September 1, 2026 16:20
rucka added a commit that referenced this pull request Sep 1, 2026
…depoint

Review round 1 on PR #475.

- `llms-index:check` was a bare `ts-node`, so it type-checked a source file whose
  first line imports @pair/content-ops TYPES from dist/. On a fresh `pnpm install`
  the gate produced none of its three outcomes — TS2307 + a ts-node stack, exit 1.
  Now `ts-node -T`; type-checking that source is ts:check's job, and turbo ts:check
  is the task carrying the ^build edge.
- compareIndex diffed through Sets, so a duplicated entry line (the shape a
  "keep both sides" merge of llms.txt produces) reported `0 missing / 0 extra` plus
  "the difference is their order or whitespace" — wrong, in the case that most needs
  the diff. Multiset deltas now.
- localeCompare with no locale sorted the index by the runtime's ICU default: 458 of
  560 entries move under a small-icu Node, so the gate went red on an untouched tree
  and its own message told the contributor to commit the churn. Codepoint order;
  .pair/llms.txt regenerated through writeProjectLlmsTxt.
- main() had no try/catch: an unreadable KB dir (chmod 000) escaped as an unhandled
  rejection instead of the broken-setup outcome. Caught, reported, exit 1.
- A tree missing ONE whole section clears the broken-setup guard and is reported as
  drift telling the contributor to regenerate — which deletes that section from the
  index. Caution line when a tracked heading has zero generated entries.
- dev-tools README Tools table: the gate had no row (plus the two stale ones).

ADLs: codepoint-sort rule recorded; the source-import ADL gains the -T rationale.
rucka added a commit that referenced this pull request Sep 1, 2026
Round 2 of PR #475.

- formatDrift's closing paragraph branches on emptiedSections: a sparse tree
  reads "Once the tree is complete, regenerate with `pair update`", so the
  message no longer ends with the imperative the caution above it forbids.
- the catch-path test drives main() with an injected throwing readdir instead
  of an if-wrapped chmod probe that ran zero assertions under root; the chmod
  variant survives as an explicitly skipped case.
- ADL restated: the invariant is determinism, the comparator is UTF-16
  code-unit order (= codepoint for every BMP path). No comparator change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQJzGMhRqBRRboxMrRqFPP
@rucka
rucka force-pushed the feature/US-416-llms-txt-drift-gate branch from 3580a61 to a5235fa Compare September 1, 2026 19:48
rucka added a commit that referenced this pull request Sep 1, 2026
…depoint

Review round 1 on PR #475.

- `llms-index:check` was a bare `ts-node`, so it type-checked a source file whose
  first line imports @pair/content-ops TYPES from dist/. On a fresh `pnpm install`
  the gate produced none of its three outcomes — TS2307 + a ts-node stack, exit 1.
  Now `ts-node -T`; type-checking that source is ts:check's job, and turbo ts:check
  is the task carrying the ^build edge.
- compareIndex diffed through Sets, so a duplicated entry line (the shape a
  "keep both sides" merge of llms.txt produces) reported `0 missing / 0 extra` plus
  "the difference is their order or whitespace" — wrong, in the case that most needs
  the diff. Multiset deltas now.
- localeCompare with no locale sorted the index by the runtime's ICU default: 458 of
  560 entries move under a small-icu Node, so the gate went red on an untouched tree
  and its own message told the contributor to commit the churn. Codepoint order;
  .pair/llms.txt regenerated through writeProjectLlmsTxt.
- main() had no try/catch: an unreadable KB dir (chmod 000) escaped as an unhandled
  rejection instead of the broken-setup outcome. Caught, reported, exit 1.
- A tree missing ONE whole section clears the broken-setup guard and is reported as
  drift telling the contributor to regenerate — which deletes that section from the
  index. Caution line when a tracked heading has zero generated entries.
- dev-tools README Tools table: the gate had no row (plus the two stale ones).

ADLs: codepoint-sort rule recorded; the source-import ADL gains the -T rationale.
rucka added a commit that referenced this pull request Sep 1, 2026
Round 2 of PR #475.

- formatDrift's closing paragraph branches on emptiedSections: a sparse tree
  reads "Once the tree is complete, regenerate with `pair update`", so the
  message no longer ends with the imperative the caution above it forbids.
- the catch-path test drives main() with an injected throwing readdir instead
  of an if-wrapped chmod probe that ran zero assertions under root; the chmod
  variant survives as an explicitly skipped case.
- ADL restated: the invariant is determinism, the comparator is UTF-16
  code-unit order (= codepoint for every BMP path). No comparator change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UQJzGMhRqBRRboxMrRqFPP
@rucka

This comment has been minimized.

@rucka

This comment has been minimized.

@rucka

rucka commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

In response to the first review: #475 (comment)

Remediation — final synthesis (converged, entire cycle)

This cycle re-opened twice after prior convergences (comments #issuecomment-5499957423 and #issuecomment-5511252662, minimized below — each time a fresh independent re-review surfaced new findings, and this run found none). Full audit of every finding raised against this PR across all review runs and every fix round, rounds 0–8 carried over verbatim from the prior synthesis, rounds 9–11 from .pair/working/reviews/416.md, round 12 the current re-review (zero actionable, all dispositioned below).

round severity location resolution commit
0 Major apps/pair-cli/src/registry/llms-generation.ts:55localeCompare is locale-dependent (cs_CZ/et_EE reorder the tracked index) Comparator pinned to UTF-16 code-unit (codepoint) order, environment-independent; .pair/llms.txt regenerated 55047883
0 Minor packages/dev-tools/src/quality-gates/llms-txt-drift-check.ts:125hasIndexableSection only catches a tree with zero indexable sections, not one missing section emptiedSections added: a heading present in the tracked file with zero generated entries prints a "do not regenerate" caution ahead of the regen advice 55047883
0 Minor package.json:32 / packages/dev-tools/package.json:17llms-index:check dies with TS2307 on an unbuilt tree; PR's own Reviewer Guide fails as written Script switched to ts-node -T; verdict prints correctly on a clean, unbuilt checkout 55047883
0 Minor apps/pair-cli/src/registry/llms-index-conformance.test.ts:35 — stale comment: "Byte equality above" refers to a guard no longer in this file Comment repointed to llms-txt-drift-check.ts, the guard's real location e58f21db
0 Minor packages/dev-tools/src/quality-gates/llms-txt-drift-check.ts:211main() has no try/catch; an I/O error (e.g. EACCES) surfaces as an unhandled-rejection stack, not the broken-setup outcome main() wraps the body, prints formatUnreadableTree(...) with the underlying error, sets process.exitCode = 1 55047883
1 Major packages/dev-tools/package.json:17 (+ package.json:32, ci.yml:115) — gate dies with TS2307 standalone (unbuilt cross-package source import) ts-node -T; ts:check (turbo ^build edge) kept for the type check 55047883
1 Major llms-txt-drift-check.ts:104-118compareIndex diffs via Sets, losing line multiplicity; a duplicated entry surfaces as 0 missing / 0 extra + a false "order or whitespace" message Diff rewritten as multisets (per-line occurrence budget); duplicate now reports as extra 55047883
1 Minor llms-generation.ts:55 — determinism assertion runs the generator twice in one process, can't see locale drift Codepoint comparator (see round 0) + fixture asserting a pair where ICU and codepoint disagree (PRD.md vs context-map.md) 55047883
1 Minor llms-txt-drift-check.ts:211-223main() uncaught I/O error try/catch, broken-setup-shaped message, process.exitCode = 1; 3 main tests incl. real chmod 000 55047883
1 Minor llms-txt-drift-check.ts:125-127 — emptied section reported as ordinary drift DriftReport.emptiedSections; caution printed ahead of regen advice 55047883
1 Minor PR #475 description — AC numbering didn't match story #416 Renumbered against the story's AC list (PR body)
1 Minor packages/dev-tools/README.md:7-14 — Tools table missing llms-index:check row (+2 pre-existing stale rows) Row added; two stale rows fixed; count-based prose removed 55047883
2 Minor llms-txt-drift-check.ts:236-241 — closing paragraph always printed the bare regen imperative, even with an emptied section callToAction(treeLooksComplete) branch; precondition-gated when a section was emptied 4bc1f26b
2 Minor llms-txt-drift-check.test.ts:288-320 — catch-path test conditioned on uid (root skips assertions silently) Injected-error test always runs; chmod 000 variant calls ctx.skip(reason) instead of an empty body when the bit doesn't bind 4bc1f26b
2 Questions ADL …sorts-by-codepoint.md + llms-generation.ts:55 — comparator's invariant mischaracterized as a collation choice Restated as determinism; comparator named as UTF-16 code-unit order; propagated to way-of-working.md, comment, test name 4bc1f26b
3 Major llms-generation.ts:69 + .changeset/ — user-facing reordering shipped with no changeset .changeset/llms-txt-deterministic-order.md added (patch on @pair/pair-cli) 5ff87240
3 Minor ADL …sorts-by-codepoint.md:88-100 — Adoption Impact section missing Section appended; ADL title widened to cover the terminator axis 5ff87240
3 Minor PR #475 body — stale "Not this story" bullet Deleted, body rewritten against current head (PR body)
3 Questions llms-txt-drift-check.ts:115-117 — CRLF-checkout platform scope unresolved .gitattributes (* text=auto eol=lf) + gate strips trailing CR, prints terminator caution, gates its call-to-action on a normalized checkout 5ff87240, d3284372
3 (self-correction) 5ff87240's own printed recipe (git add --renormalize) didn't converge the loop it prescribed Recipe replaced with rm + git checkout --; proven to end the loop d3284372
4 Minor ADL / llms-txt-drift-check.ts:125 / test:242 — stale figures (562/~1124) didn't match measured 570/~1140 Figures corrected; ADL states the three quantities that must reconcile a5235fa0
4 Questions llms-txt-drift-check.ts:129-134,190 — terminator handling covered CRLF only, not bare CR / mixed / doubled \r LINE_TERMINATOR regex split on the full domain; flag renamed trackedCarriesCr; one test row per terminator variant a5235fa0
5 Minor DEVELOPMENT.md:124 — documented gate chain (9 of 12 steps) didn't match the executed quality-gate chain Enumeration updated to the executed chain/order in both DEVELOPMENT.md and its mdx twin 62fb750b
5 Questions llms-txt-drift-check.ts:263 — printed recipe's git config core.autocrlf false step unreachable/unjustified Dropped; recipe reduced to rm + git checkout --; RED-first test asserts its absence 62fb750b, e58f21db
6 Minor llms-txt-drift-check.ts:405-414 + :389-390 (tracked read inside the tree try) Tracked-file read moved to its own try/catch → new DriftReport kind unreadable-index (path, detail); formatReport prints could not read the tracked index <path> + error + git checkout -- .pair/llms.txt; formatUnreadableTree now sees generator failures only. Rows: EACCES (injected + real chmod-000, root-skipped), EISDIR, dangling symlink (→ absent-file drift, pinned), KB-tree EACCES control e6cd2ff9
6 Minor llms-txt-drift-check.ts:140-142 / :211-214 (a) trackedCarriesBom flag + byteOrderMarkCaution (no precondition: regeneration fixes it); (b) renderLines renders a missing/extra pair whose visibleForm matches as JSON.stringify + \uXXXX for BOM/zero-width/NBSP-class chars, plus one invisibleDifferenceCaution; visibly different lines stay raw. Rows: BOM, trailing space, leading space, NBSP, ZWSP, raw control, mixed (2 look-alikes + 1 real), BOM+CRLF, BOM-free flag, compareIndex BOM at start / not at start e6cd2ff9
6 Minor PR #475 description "Files Changed" Count kept at 18 — verified against git diff and gh pr view at both reviewed head and current head; enumeration = 6 added + 12 modified PR body (publish-pr)
7 Minor llms-txt-drift-check.ts:234 (ZERO_WIDTH) Class widened to [\p{Cf}\p{Zl}\p{Zp}\p{Variation_Selector}] with u; hex uses codePointAt, prints \u{XXXXX} above the BMP. Rows: 13 BMP members, 3 astral, real fixture tree, astral emoji stays raw 3969a4eb
7 Minor :268-273 (lookAlikeForms / renderLines) needsEscaping = paired || INVISIBLE.test(line) — an unpaired line carrying any class member is escaped and counted 3969a4eb
7 Minor :283-290 + :412 (caution count) Count = escaped lines across both lists; wording spells out the character classes 3969a4eb
7 Minor :242 vs :262 (SPACE_LIKE duplicated) One SPACE_LIKE_CLASS string builds SPACE_LIKE (+\t), SPACE_LIKE_ESCAPED, INVISIBLE; it.each over all 8 members asserts detected AND escaped 3969a4eb
7 Questions :251 (visibleForm, space runs) visibleForm collapses / {2,}/ → one space; escapeInvisible spells a run of 2+ spaces; single space stays literal 3969a4eb
8 Minor llms-txt-drift-check.ts:240 (ZERO_WIDTH_CLASS) Class is \p{Default_Ignorable_Code_Point}\p{Zl}\p{Zp}; JSDoc names DI as the class and lists DI \ Cf + Cf \ DI. Rows: one per range × general category of DI \ Cf (Mn/Lo/Cn/astral) d789dc31
8 Questions :240 / :329-333 (visible Cf under the caution) Tightened: \p{Cf} dropped (⊃ \p{Variation_Selector}, itself ⊂ DI) — DI's contract is "renders as nothing", keeping Cf would leave the caution's claim false for 32 members. Negative rows: one per range of Cf \ DI, all raw + no caution d789dc31
9 Major apps/pair-cli/src/registry/llms-generation.ts:45,51posix.join-mixed paths break under Windows path semantics Emitted entry paths built with posix.join; join kept for fs access only; llms-generation.win32.test.ts runs the generator against real path.win32 (5 rows: byte-equality with the POSIX doc, no backslash at any depth, sections found, separator-as-sort-key order); ADL …sorts-by-codepoint.md gains rule 3 d99834e2
9 Major packages/dev-tools/src/quality-gates/llms-txt-drift-check.ts:73REGENERATION_COMMAND named a command with no real executable (pair update) REGENERATION_COMMANDpnpm llms-index:regen; new llms-txt-regenerate.ts (+11-row suite), the check's exact inverse, refusing on broken-setup/unreadable-index/unreadable tree/trackedCarriesCr/emptiedSections; root + package scripts added; DEVELOPMENT.md, its mdx twin, way-of-working.md, dev-tools README moved with it; ADL 2026-09-03-a-gate-names-a-remedy-it-can-run.md d99834e2
9 Minor packages/dev-tools/src/quality-gates/llms-txt-drift-check.test.ts:124-132 — AC-5 row asserted the stale literal Asserts the literal pnpm llms-index:regen; two new rows — script name resolved against the real root package.json, REGENERATION_COMMAND never matching \bpair\b(?!-cli) d99834e2
10 re-dispatch of the round-9 finding set, authored against the round-8 head, not the branch head No code change — all three already fixed at d99834e2; re-verified line-by-line against the pushed head (evidence re-run in the log) d99834e2 (round 9)
11 Minor .pair/adoption/tech/way-of-working.md:88 (+ llms-txt-drift-check.ts:5-6, same contract cell, same fix) — producer clause named pair update/pair install then denied pair update exists, a self-contradiction Opening clause now names the real producer (pair-cli install/pair-cli update, writeProjectLlmsTxt); later clause records pair update as the retired spelling that names no executable; same fix applied to the gate module's header ade14617

Accepted / non-actionable

location severity disposition
ADL 2026-09-01-a-byte-compared-generated-artifact-sorts-by-codepoint.md:72 (also llms-txt-drift-check.ts:246) Questions Historical record — "all 583 CR-carrying lines stay on disk" is a measurement scoped "at this commit" (ADL L46); figure diverges across later commits as expected for an intermediate-commit measurement, not an error. Non-blocking.
.github/workflows/ci.yml job secret-scan on head d33cc67c / scripts/smoke-tests/scenarios/pr-state-flow.sh @ 5e99d538 Questions Pre-existing on main since 5e99d538 — this PR's own commits scan clean; resolves independently of this diff.
apps/pair-cli/src/commands/update/handler.ts (untouched by PR) — behaviour of the printed remedy pair update (pre-round-9 remedy) Questions By convention at the time — superseded by round 9's fix, which replaced the remedy with pnpm llms-index:regen for exactly this reason (blast radius on unrelated files).
packages/dev-tools/tsconfig.json:19exclude: ["node_modules", "**/*.test.ts"] keeps the new 1261-line test file outside tsc --noEmit, checked only incidentally via vitest transpile Minor Matches the package's existing convention — exclude is unchanged by this PR; widening it pulls every dev-tools suite into ts:check as unrelated scope.
packages/dev-tools/src/quality-gates/llms-txt-regenerate.ts:141 — refusal prints check.message verbatim, itself the check's closing line, so the regenerator's own trailing sentence ("This check never writes .pair/llms.txt…") can read as "you ran the wrong command" Minor By design (ADL 2026-09-03-a-gate-names-a-remedy-it-can-run.md decision point 2: refusal prints the gate's OWN message, never a second wording); the very next line, Nothing was written to <path>., disambiguates. Concrete probe: real CRLF-converted .pair/llms.txt + real pnpm llms-index:regen → exit 1, file byte-identical.
packages/dev-tools/src/quality-gates/llms-txt-regenerate.ts:120 — sparse-tree refusal triggers on a whole-section loss only; a partial loss inside a surviving section regenerates without caution Questions Below severity floor — carried to the merge gate unfixed. Concrete probe: real repo, one file moved out of a non-emptying section → pnpm llms-index:regen prints ✓ regenerated, diff shows the missing file committed as a deletion. Legitimate by design (a real removal is byte-identical to a partial checkout); only the threshold statement is undocumented.
.pair/adoption/tech/way-of-working.md:88 (+ llms-txt-drift-check.ts:5-6) — "on every pair-cli install/update" is false when tally.ok === 0 (all registries fail) Questions Below severity floor — carried to the merge gate unfixed. Real call sites (install/handler.ts:423-425, update/handler.ts:239-241) gate the write on tally.ok > 0; quantifier pre-dates this delta (only the command name changed).
DEVELOPMENT.md:132 and :155 — still teach the retired pair update spelling (prescriptive remedy line + CLI command table), one paragraph below this PR's corrected line; real probe: which pair → not found, pair-cli publishes as the only bin Minor Below severity floor — carried to the merge gate unfixed. Out of the fix delta (d99834e2...ade14617) and unchanged surface in prior rounds; human call at merge: sweep to pair-cli in this PR or confirm "retired" is scoped to the gate's printed remedy only.

Final verdict: review clean — zero actionable findings on the latest independent re-review (round 12); every finding across all runs of this cycle (rounds 0–8 from the prior syntheses, rounds 9–11 from .pair/working/reviews/416.md, round 12 above) resolved in place or dispositioned above.

@rucka
rucka force-pushed the main branch 4 times, most recently from 454a6ab to 3d1d33e Compare September 13, 2026 15:52
Squashed rebase of 17 commits onto main cbf390b (post-#468/#467/#466).
Gate compares tracked .pair/llms.txt vs generateLlmsTxt byte-for-byte,
reports missing/extra lines and regeneration command, wired into
ci.yml + root quality-gate. Reuses generator, never writes.

Refs: #416
@rucka
rucka force-pushed the feature/US-416-llms-txt-drift-gate branch from ade1461 to 9ee089a Compare September 14, 2026 10:46
Drift gate imports FileSystemService from @pair/content-ops.
Squashed rebase had dropped it, causing tsc Cannot find module.

Refs: #416
T added 2 commits September 14, 2026 12:59
Squashed rebase had overwritten workflow/skill files with old versions
from fa4fc10, causing lint failures. Restore to cbf390b and keep
only 416's 22-file drift gate.

Refs: #416
Add 6 catalog deps that were added to apps/website/package.json
in main (6e37df9..cbf390b) but missing from squashed lock.

Refs: #416
Drift gate was red: 3 ADL lines missing (byte-compared artifact
decisions added by this branch but not in tracked index).
Regenerated via pnpm llms-index:regen.

Refs: #416
Squashed rebase had overwritten with old version, causing
format-workflow-composition lint failures. Restore to cbf390b.

Refs: #416
pnpm format fix for prettier:check.

Refs: #416
@rucka
rucka merged commit 5a18b8d into main Sep 14, 2026
5 checks passed
@rucka
rucka deleted the feature/US-416-llms-txt-drift-gate branch September 14, 2026 11:20
rucka pushed a commit that referenced this pull request Sep 14, 2026
Squashed rebase onto 5a18b8d (post-#475). 14 commits -> 1.
Risk: green (trivial-diff override is opt-in, no behaviour change by default).
Intended changes: 35 files (12 from branch already on main via #413/#475).

Refs: #438
rucka pushed a commit that referenced this pull request Sep 14, 2026
Branch removed <!-- process-step: id=bootstrap --> marker (added by #475
in cc169bd) and downgraded version 0.9.0->0.8.0. Also removed workflow
skills from skills-guide. Restore to main.

Refs: #438
rucka pushed a commit that referenced this pull request Sep 14, 2026
Root package.json and dev-tools missing llms-index:check/regen scripts
(added by #475). Regenerated index to match current tree.

Refs: #438
rucka pushed a commit that referenced this pull request Sep 14, 2026
Drift gate without the release-triggering changeset, per project decision:
no auto-release.

Refs: #416
rucka pushed a commit that referenced this pull request Sep 14, 2026
Drift gate without the release-triggering changeset, per project decision:
no auto-release.

Refs: #416
rucka pushed a commit that referenced this pull request Sep 14, 2026
Squashed rebase onto 69b6321 (post-#475). 17 commits -> 1.
Risk: yellow (docs sweep, no behaviour change).
Intended changes: 149 files (US-449 scope only).

Refs: #449
rucka added a commit that referenced this pull request Sep 14, 2026
* [#416] feat: llms.txt drift gate

Squashed rebase of 17 commits onto main cbf390b (post-#468/#467/#466).
Gate compares tracked .pair/llms.txt vs generateLlmsTxt byte-for-byte,
reports missing/extra lines and regeneration command, wired into
ci.yml + root quality-gate. Reuses generator, never writes.

Refs: #416

* [#416] fix: restore dev-tools package.json after squashed rebase

Merge main's yaml + format-workflow:check with 416's llms-index scripts.
Pre-push hook bypassed for squashed rebase fix.

* [#416] fix: sync pnpm-lock after squashed rebase

CI frozen-lockfile failed: 6 deps removed from apps/website/package.json
vs lock. Regenerated lock via pnpm install.

Refs: #416

* [#416] fix: restore @pair/content-ops dep for drift gate

Drift gate imports FileSystemService from @pair/content-ops.
Squashed rebase had dropped it, causing tsc Cannot find module.

Refs: #416

* [#416] fix: restore main files after squashed rebase

Squashed rebase had overwritten workflow/skill files with old versions
from fa4fc10, causing lint failures. Restore to cbf390b and keep
only 416's 22-file drift gate.

Refs: #416

* [#416] fix: sync pnpm-lock for website deps after rebase

Add 6 catalog deps that were added to apps/website/package.json
in main (6e37df9..cbf390b) but missing from squashed lock.

Refs: #416

* [#416] fix: regenerate .pair/llms.txt after rebase

Drift gate was red: 3 ADL lines missing (byte-compared artifact
decisions added by this branch but not in tracked index).
Regenerated via pnpm llms-index:regen.

Refs: #416

* [#416] fix: restore pre-push gate to main after squashed rebase

Squashed rebase had overwritten with old version, causing
format-workflow-composition lint failures. Restore to cbf390b.

Refs: #416

* [#416] fix: format dev-tools package.json

pnpm format fix for prettier:check.

Refs: #416

---------

Co-authored-by: T <t@e.com>
rucka pushed a commit that referenced this pull request Sep 14, 2026
Squashed rebase onto 5a18b8d (post-#475). 14 commits -> 1.
Risk: green (trivial-diff override is opt-in, no behaviour change by default).
Intended changes: 35 files (12 from branch already on main via #413/#475).

Refs: #438
rucka pushed a commit that referenced this pull request Sep 14, 2026
Branch removed <!-- process-step: id=bootstrap --> marker (added by #475
in cc169bd) and downgraded version 0.9.0->0.8.0. Also removed workflow
skills from skills-guide. Restore to main.

Refs: #438
rucka pushed a commit that referenced this pull request Sep 14, 2026
Root package.json and dev-tools missing llms-index:check/regen scripts
(added by #475). Regenerated index to match current tree.

Refs: #438
rucka added a commit that referenced this pull request Sep 14, 2026
* [#416] feat: llms.txt drift gate (#475)

* [#416] feat: llms.txt drift gate

Squashed rebase of 17 commits onto main cbf390b (post-#468/#467/#466).
Gate compares tracked .pair/llms.txt vs generateLlmsTxt byte-for-byte,
reports missing/extra lines and regeneration command, wired into
ci.yml + root quality-gate. Reuses generator, never writes.

Refs: #416

* [#416] fix: restore dev-tools package.json after squashed rebase

Merge main's yaml + format-workflow:check with 416's llms-index scripts.
Pre-push hook bypassed for squashed rebase fix.

* [#416] fix: sync pnpm-lock after squashed rebase

CI frozen-lockfile failed: 6 deps removed from apps/website/package.json
vs lock. Regenerated lock via pnpm install.

Refs: #416

* [#416] fix: restore @pair/content-ops dep for drift gate

Drift gate imports FileSystemService from @pair/content-ops.
Squashed rebase had dropped it, causing tsc Cannot find module.

Refs: #416

* [#416] fix: restore main files after squashed rebase

Squashed rebase had overwritten workflow/skill files with old versions
from fa4fc10, causing lint failures. Restore to cbf390b and keep
only 416's 22-file drift gate.

Refs: #416

* [#416] fix: sync pnpm-lock for website deps after rebase

Add 6 catalog deps that were added to apps/website/package.json
in main (6e37df9..cbf390b) but missing from squashed lock.

Refs: #416

* [#416] fix: regenerate .pair/llms.txt after rebase

Drift gate was red: 3 ADL lines missing (byte-compared artifact
decisions added by this branch but not in tracked index).
Regenerated via pnpm llms-index:regen.

Refs: #416

* [#416] fix: restore pre-push gate to main after squashed rebase

Squashed rebase had overwritten with old version, causing
format-workflow-composition lint failures. Restore to cbf390b.

Refs: #416

* [#416] fix: format dev-tools package.json

pnpm format fix for prettier:check.

Refs: #416

---------

Co-authored-by: T <t@e.com>

* [#438] feat: opt-in business-impact.trivial-diff override

Squashed rebase onto 5a18b8d (post-#475). 14 commits -> 1.
Risk: green (trivial-diff override is opt-in, no behaviour change by default).
Intended changes: 35 files (12 from branch already on main via #413/#475).

Refs: #438

* [#438] fix: sync pnpm-lock for website deps

Refs: #438

* [#438] fix: add @pair/content-ops dep for dev-tools type-check

Squashed rebase dropped it from package.json (was in branch but not on main).
Refs: #438

* [#438] fix: sync pnpm-lock for content-ops dep

Refs: #438

* [#438] fix: restore process-step marker and skills-guide counts

Branch removed <!-- process-step: id=bootstrap --> marker (added by #475
in cc169bd) and downgraded version 0.9.0->0.8.0. Also removed workflow
skills from skills-guide. Restore to main.

Refs: #438

* [#438] fix: add llms-index scripts + regenerate .pair/llms.txt

Root package.json and dev-tools missing llms-index:check/regen scripts
(added by #475). Regenerated index to match current tree.

Refs: #438

* [#438] fix: drop stale workflow spillover, keep US-438 hunks on main files

pair-implement-batch (all 4 copies) restored to main: branch carried
#219-era full-file versions missing args.entryCapsules, breaking
engine-boundaries/engine-integration dry-runs. No US-438 commit ever
touched those files. US-438 one-liners (bootstrap trivial-diff sentence,
skills-guide Classification row) re-applied onto main versions.
turbo.json/tech-stack/way-of-working restored (post-base main additions).
Changeset file removed per no-release decision.

Refs: #438

* [#438] chore: retrigger CI

Refs: #438

* [#438] fix: ADL names workflows/agents carve-out classes like §6

Review Minor: ADL point 2 said 'skill/workflow file' but enumerated
only SKILL.md + .claude/skills/**; §6 + risk-matrix carve out
.claude/workflows/** + .claude/agents/** since round 5. Aligned.

Refs: #438

* [#438] chore: trigger mergeability recompute

Refs: #438

---------

Co-authored-by: T <t@e.com>
rucka pushed a commit that referenced this pull request Sep 14, 2026
Squashed rebase onto 69b6321 (post-#475). 17 commits -> 1.
Risk: yellow (docs sweep, no behaviour change).
Intended changes: 149 files (US-449 scope only).

Refs: #449
rucka pushed a commit that referenced this pull request Sep 14, 2026
Branch predated #479/#434/#475/#466 additions: restoring those files
to main versions reverted repo-citation gate (5b/5c), US-479 loop text,
#475 conformance test, turbo inputs. Re-applied only US-449 hunks
(rename + invocation-awareness) via 3way port, verified per-file.
Also: 2 missed pair-run (US-479 Host-runtime lines, both trees),
ADL chalk-loss half-sentence. Local: 183+40+56+167+105+536 tests
green, conformance PASS, docs:staleness PASS.

Refs: #449
rucka added a commit that referenced this pull request Sep 14, 2026
…ate can see when it does not (#473)

* [#449] docs: every invocation names pair-cli

Squashed rebase onto 69b6321 (post-#475). 17 commits -> 1.
Risk: yellow (docs sweep, no behaviour change).
Intended changes: 149 files (US-449 scope only).

Refs: #449

* [#449] fix: port US-449 hunks onto main files, drop stale spillover

Branch predated #479/#434/#475/#466 additions: restoring those files
to main versions reverted repo-citation gate (5b/5c), US-479 loop text,
#475 conformance test, turbo inputs. Re-applied only US-449 hunks
(rename + invocation-awareness) via 3way port, verified per-file.
Also: 2 missed pair-run (US-479 Host-runtime lines, both trees),
ADL chalk-loss half-sentence. Local: 183+40+56+167+105+536 tests
green, conformance PASS, docs:staleness PASS.

Refs: #449

---------

Co-authored-by: T <t@e.com>
rucka pushed a commit that referenced this pull request Sep 14, 2026
Branch predated #451/#479/#434/#475/#473/#466: restoring those files
to main reverted run/ adapter, US-479 loop text, repo-citation gate,
skill counts, review-identity scenario. Re-applied only #217 hunks
via per-file 3way port (hash-verified), renamed #217-owned bare
pair-run to pair-cli (staleness gate now flags them), fixed 2 missed
occurrences + ADL chalk note pattern. Local: pair-cli 1998,
conformance 4576, website 216, dev-tools 697, mirrors green.

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

Labels

pr-state:to-be-reviewed PR state: awaiting review / gate risk:green Classification: low risk tier tech-debt Tracked technical debt (living backlog, R7.2 — never blocks a PR) user story Work item representing a user story

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant