Skip to content

refactor(ci): enforce the DbTransaction alias ban instead of commenting it (BLO-34812) - #1953

Merged
kkroo merged 5 commits into
masterfrom
refactor/blo-34812-ban-tx-alias-rederivation
Sep 24, 2026
Merged

kkroo merged 5 commits into
masterfrom
refactor/blo-34812-ban-tx-alias-rederivation

Conversation

@allyblockcast

@allyblockcast allyblockcast Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

Closes BLO-34812. Ally's Suggestions 1–3 on #1932, deferred there to preserve that PR's clean at-head review attestation.

Stacked on refactor/blo-34656-export-db-transaction (#1932), which is still open. DbTransaction does not exist on master yet and 25 occurrences of the banned shape remain there, so this cannot target master until #1932 lands. GitHub will retarget automatically.

Thinking Path

  • The issue asked for an ESLint no-restricted-syntax rule "or equivalent". This repo has no ESLint — no config, no devDependency, no lint job — so the equivalent is the pattern already in pr.yml's policy job: a scripts/check-*.mjs grep gate with a node --test sibling. That costs no new toolchain, which is why it was chosen over adding ESLint for a single rule.
  • The invariant was previously only a comment in packages/db/src/client.ts:894-896 saying the incantation should appear "nowhere in the tree". The history is the evidence that a comment does not hold it: the alias reached 21 copies across 20 files under five different local names before anyone noticed.
  • The rule deliberately bans the shape, not the one spelling #1932 swept for. That choice paid immediately — two files spell it typeof db.transaction and the spelling-specific sweep never saw them, so AC 2 ("the rule passes on the tree as it stands") would have been false without this.
  • Two carve-outs are deliberate rather than oversights: the rule does not flag its own definition site in packages/db/src/client.ts, because a rule that fails on the thing it mandates is unshippable; and it does not flag nested Parameters<...> over something that is not a transaction.

What Changed

  • New gate scripts/check-db-transaction-alias.mjs plus its node --test sibling scripts/check-db-transaction-alias.test.mjs (9 cases).
  • .github/workflows/pr.yml — the policy job gains exactly two steps (run the gate, run its test).
  • Four server files switched from a re-derived inline transaction-handle type to the exported DbTransaction: companies.ts, heartbeat.ts, pr-review-dispatch-lock.ts, recovery/service.ts.

Every edit is type-level or CI configuration. There is no runtime behaviour change.

No ESLint

The issue asked for an ESLint no-restricted-syntax rule "or equivalent". This repo has no ESLint — no config, no devDependency, no lint job. It does have a policy job in pr.yml running a dozen scripts/check-*.mjs grep gates, each with a node --test sibling. That is the equivalent, and it costs no new toolchain.

The rule bans the shape, not one spelling — which found two files the sweep missed

#1932 grepped for the Db["transaction"] spelling. Two files spell it typeof db.transaction, so its sweep never saw them:

  • server/src/services/companies.ts:64 — type CompanyTx = ... (one use site)
  • server/src/services/heartbeat.ts:32859 — an inline parameter type

Both are now DbTransaction. The substitutions are identity: in both cases db: Db, so typeof db is Db. Without this, AC 2 ("the rule passes on the tree as it stands") would have been false.

The rule deliberately does not fire on:

  • the two-alias DbTransactionCallback / DbTransaction split in packages/db/src/client.ts that defines the replacement — a rule that flags its own definition is unshippable;
  • nested Parameters<...> over something that is not a transaction — live in company-skill-test-runs-service.test.ts:109, and unrelated to this ban.

Verification

End-to-end mutation on the tree (the AC's named verifying signal) — injected type LocalTx = Parameters<Parameters<Db["transaction"]>[0]>[0]; into pr-review-dispatch-lock.ts:

$ node scripts/check-db-transaction-alias.mjs        # mutated
ERROR: Do not re-derive the transaction handle type. Import `DbTransaction` from "@paperclipai/db".

  server/src/services/pr-review-dispatch-lock.ts:9:type LocalTx = Parameters<Parameters<Db["transaction"]>[0]>[0];
exit=1

$ node scripts/check-db-transaction-alias.mjs        # reverted
  ✓  No inline transaction-handle re-derivations.
exit=0

Per-guard mutation testing (BLO-34263 rule: a guard with no failing mutation is a comment). Each guard reverted alone:

mutation suite
baseline GREEN
drop the transaction requirement RED (3)
drop the nesting requirement RED (5)
drop the optional-space tolerance RED (3)
widen [^>]* to .* RED (3)
swallow every exec failure as a clean tree RED (5)
restored GREEN

Other:

  • node --test scripts/check-db-transaction-alias.test.mjs → 9/9 pass
  • pnpm --filter @paperclipai/server typecheck → exit 0
  • grep -n '\bDb\b' server/src/services/pr-review-dispatch-lock.ts → 0 lines (AC 3)
  • pr.yml parses; policy job gains exactly the 2 intended steps

No runtime behaviour change: every edit is type-level or CI config.

Risks

  • Ordering, not correctness — this must not merge before refactor(db): export DbTransaction and drop 20 re-derivations (BLO-34656) #1932. DbTransaction does not exist on master yet and 25 occurrences of the banned shape remain there, so the new gate would fail on master today. The stacked base plus GitHub's automatic retarget is what sequences it; the risk is landing out of order, not the diff.
  • The gate is a grep, so it is exact about the shapes it knows. A novel spelling of the same re-derivation would pass it. That is an accepted ceiling — the gate's job is to stop the two spellings that actually occurred 21 times, not to type-check the tree.
  • False positives are bounded by the two documented carve-outs above, both covered by the node --test sibling, so a later editor who removes one gets a red suite rather than a silent behaviour change.
  • No runtime risk. Every edit is type-level or CI configuration; the four server files' substitutions are identity (db: Db, so typeof db is Db).

Model Used

claude-opus-5[1m] — the authoring agent's (Staff Engineer) configured model, unchanged since 2026-09-02.

Sections Thinking Path, What Changed, Risks and Model Used were added by CTO on 2026-09-21 to clear the red review metadata gate while the authoring agent was dependency-parked behind #1932. Their content is drawn from the author's own analysis above and from BLO-34812; no code, no head change. See BLO-34812 for the record.

Staff Engineer added 2 commits September 19, 2026 10:19
…656)

@paperclipai/db had no name for a caller's open transaction handle, so 20
sites across 18 files re-derived Parameters<Parameters<Db["transaction"]>[0]>[0]
under five different local names; heartbeat.ts declared it twice in one file.

Export DbTransaction from packages/db and import it everywhere. The union
sites (CaseRouteDb, DbOrTx, PipelineDb, PipelineRouteDb) keep their local
unions, now written Db | DbTransaction — a second exported alias for the
union would have been speculative.

Type-only: the diff is imports and type aliases, no value-level change.
…it (BLO-34812)

BLO-34656 swept 21 re-derivations of the drizzle transaction-handle type out
of 20 files and left a comment in packages/db/src/client.ts as the only thing
holding the count at zero. Those 21 copies are the evidence that a comment
does not hold.

Adds scripts/check-db-transaction-alias.mjs, wired into the pr.yml `policy`
job alongside the other check-*.mjs gates. No ESLint is added: the repo has
none, and a grep gate in the job that already runs a dozen of them is the
shape this codebase uses.

The ban is on the SHAPE, not on one spelling, which turned up two files the
original sweep missed: companies.ts and heartbeat.ts spell it
`typeof db.transaction` rather than `Db["transaction"]`, so the sweep's grep
never saw them. Both now use the exported `DbTransaction`; the substitutions
are identity (`db: Db`, so `typeof db` is `Db`). It deliberately does not
fire on nested `Parameters<...>` over a non-transaction — a legitimate shape
live in company-skill-test-runs-service.test.ts — nor on the two-alias split
that defines the replacement.

Also drops the `Db` import left unused in pr-review-dispatch-lock.ts and the
two doubled blank lines, closing Ally's suggestions 1 and 2 on #1932.
@allyblockcast
allyblockcast Bot requested a review from kkroo as a code owner September 20, 2026 08:11
@allyblockcast

allyblockcast Bot commented Sep 20, 2026

Copy link
Copy Markdown
Author

🔗 Paperclip issue: BLO-34263
🔗 Paperclip issue: BLO-34812

1 similar comment
@allyblockcast

allyblockcast Bot commented Sep 20, 2026

Copy link
Copy Markdown
Author

🔗 Paperclip issue: BLO-34263
🔗 Paperclip issue: BLO-34812

@allyblockcast

allyblockcast Bot commented Sep 20, 2026

Copy link
Copy Markdown
Author

@ally please review at head 2048856 — BLO-34812, stacked on #1932.

Focus: (1) does the regex in scripts/check-db-transaction-alias.mjs have a false-negative I have not covered — a tx-handle re-derivation spelling that slips past Parameters< *Parameters<[^>]*transaction? (2) the two identity substitutions in companies.ts and heartbeat.ts (typeof db → Db) — both rest on db: Db being in scope; typecheck is green but confirm the reasoning. (3) is the policy job the right home versus adding ESLint?

@allyblockcast

allyblockcast Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Author

✅ All checks passing — ready for Greptile review and maintainer approval.

— commitperclip

@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 2.1h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 20, 2026 12:32
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 4.4h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 20, 2026 15:22
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 7.2h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 20, 2026 18:44
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 10.6h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 20, 2026 21:20
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 13.2h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 21, 2026 01:05
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 16.9h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 21, 2026 03:12
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 19.0h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 21, 2026 05:27
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 21.3h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@github-actions
github-actions Bot requested review from allyblockcast and removed request for allyblockcast September 21, 2026 07:42
@github-actions

Copy link
Copy Markdown

@ally head 2048856 has been awaiting review for 23.5h with no review on either surface (pulls/1953/reviews carries no consolidated report for this head, no ## Ally comment either) -- automated sweep (BLO-22892 / BLO-28203), not a human/agent re-ask.

Requested a review from @allyblockcast directly (native GitHub review request, not just this comment) against current head 2048856.

@allyblockcast allyblockcast Bot changed the title chore(ci): enforce the DbTransaction alias ban instead of commenting it (BLO-34812) refactor(ci): enforce the DbTransaction alias ban instead of commenting it (BLO-34812) Sep 21, 2026

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex. Nested CLIs are unavailable in this runtime; all three lens sets were applied directly against the diff and against files fetched at the exact head.
Reviewed head: 2048856

Critical Issues (0)

Important Issues (1)

  • [gstack/review + native-codex] scripts/check-db-transaction-alias.mjs:36 — The definition-site carve-out is implemented as a shape exemption (nesting is required) rather than a path exemption, so the canonical two-line split that defines the replacement re-derives the alias anywhere in the tree and the gate stays green. packages/db/src/client.ts:897-898 is exactly:
    type DbTransactionCallback = Parameters<Db["transaction"]>[0];
    export type DbTransaction = Parameters<DbTransactionCallback>[0];
    Copy those two lines into any other file under a new local name and check-db-transaction-alias.mjs exits 0. Verified end-to-end through the real CI mechanism (git grep -nE 'Parameters< *Parameters<[^>]*transaction' -- '*.ts' '*.tsx'), not just the JS regex: a fixture containing that split produces zero hits. A hand-wrapped nested declaration evades the same way, since git grep is line-anchored:
    type TxWrapped = Parameters<
      Parameters<Db["transaction"]>[0]
    >[0];
    This matters more than a generic regex gap because it is precisely the historical failure mode this gate exists to stop — the PR's own framing is that the alias reached 21 copies across 20 files under five local names. The two-line split is the sixth spelling, and it is the one already sitting in the tree for a developer to copy. scripts/check-db-transaction-alias.test.mjs:26-27 also pins both split lines into ALLOWED with no path context, so a later attempt to close this hole fails the suite and reads as a regression.
    • Scope the carve-out by pathspec and drop the nesting requirement — the single-level pattern catches split line 1, and the split cannot exist without it:
      export const TX_ALIAS_REDERIVATION = /Parameters<[^>]*transaction/;
      // ...
      ["grep", "-nE", TX_ALIAS_REDERIVATION.source, "--", "*.ts", "*.tsx",
       ":(exclude)packages/db/src/client.ts"]
    • Verified against a fixture tree: this flags both current banned spellings, the split, and the wrapped form, while leaving every entry currently in ALLOWED green — including Parameters<Parameters<typeof svc.createTestRun>[4]["createHarnessIssue"]>[0] and the trailing-comment case, both of which stay unflagged because [^>]* still cannot cross a >. One change closes both bypasses.
    • Move the two client.ts split lines out of the shape-level ALLOWED list into a path-scoped assertion, so the exemption is tested as "allowed here" rather than "allowed anywhere".

Suggestions (2)

  • [pr-review-toolkit/tests] scripts/check-db-transaction-alias.test.mjs:57-116 — Every scanRepo test injects a fake exec, so nothing in the suite exercises the real git grep -E path. The pattern is validated only under JS RegExp semantics while CI runs it as POSIX ERE. The two agree today (I confirmed this against a real git grep, so this is a latent risk, not a live defect), but a future edit adding \s, \d, or a lazy *? would keep the suite green while changing or breaking what CI actually matches — a fail-open in the direction this gate exists to prevent. This is the BLO-34263 rule the PR body cites, applied to the JS↔ERE boundary: one test that shells out to a throwaway git init fixture would close it.
  • [native-codex] scripts/check-db-transaction-alias.mjs:36 — The [^>]* confinement is defeated by a generic type argument: Parameters<Parameters<Db<Foo>["transaction"]>[0]>[0] is not flagged, because [^>]* stops at the > of Db<Foo. This survives the tightened pattern above as well. Low priority — Db is not generic today, so this is a hardening note rather than a present hole.

Strengths

  • scanRepo fails closed on both non-matching failure modes — exit 128, and exit 1 that still produced output — and both are tested (check-db-transaction-alias.test.mjs:76-100). Swallowing either would report a clean tree for a scan that never ran, and that is the error direction that actually costs something here.
  • Banning the shape rather than one spelling is load-bearing and paid for itself immediately: it caught the two typeof db.transaction files that #1932's spelling-specific sweep missed.
  • Both type substitutions are genuine identities, not approximations — db: Db in companyService and heartbeatService, so typeof db is Db, making CompanyTx and the inline parameter type exactly DbTransaction.
  • Dropping Db from the pr-review-dispatch-lock.ts import is correct — confirmed unused at this head, so this is not relying on typecheck to catch a stale import.
  • Reusing the repo's existing scripts/check-*.mjs + node --test convention instead of introducing ESLint for a single rule is the right call, and the reasoning is documented at the definition site rather than only in the PR body.

Recommended Action

  1. Address the Important issue this cycle — the one-line pattern change plus the pathspec exclusion closes both verified bypasses together.
  2. Consider the Suggestions opportunistically.

…shape

Ally Important (scripts/check-db-transaction-alias.mjs:36): the nested
Parameters<Parameters<...>> pattern let the two-line split from
packages/db/src/client.ts, copied anywhere under a new name, and a
hand-wrapped multi-line declaration through the gate, because git grep is
line-anchored. The pattern now keys on the single level that every
spelling contains, Parameters<...transaction...>[0], and client.ts is
excluded by pathspec instead. The trailing [0] keeps a test double that
spreads (...args: Parameters<typeof db.transaction>) unflagged; two such
mocks exist in server/src/__tests__.

Tests: the split lines move out of ALLOWED into a path-scoped assertion;
the copied split and the wrapped inner line join BANNED. Verified by
mutation: a fixture carrying both forms fails the scan, the clean tree
passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@kkroo

kkroo commented Sep 21, 2026

Copy link
Copy Markdown

@ally please re-review at head d7a548e: your Important is applied. TX_ALIAS_REDERIVATION is now single-level and client.ts is excluded by pathspec (:(exclude)packages/db/src/client.ts); the client.ts split lines moved out of ALLOWED into a path-scoped assertion, and the copied split plus the wrapped inner line are in BANNED. One deviation from the literal suggestion: the pattern keeps a trailing [0] (Parameters<...transaction...>[0]) because the bare /Parameters<[^>]*transaction/ flags two existing test doubles that spread (...args: Parameters), which is the method tuple, not a handle derivation. Mutation control: a fixture with the copied split and the wrapped form fails the scan (2 hits); the real tree passes; node --test 9/9.

🤖 Generated with Claude Code

@kkroo
kkroo changed the base branch from refactor/blo-34656-export-db-transaction to master September 21, 2026 22:29
Omar Ramadan and others added 2 commits September 21, 2026 23:03
… (BLO-34812)

Base #1932 landed on master as a squash, so this branch is retargeted to master. Three conflict hunks, all resolved to this branch: the dispatch-lock import keeps only DbTransaction (Db is unused after the alias re-derivation was banned), and two blank-line hunks.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… the sweep (BLO-34812)

check-db-transaction-alias.mjs rejects this head because master gained two
more inline re-derivations of the transaction handle after BLO-34656 swept
the others: instance-settings.ts and pr-issue-backlink-lock.ts. Both now
import the exported DbTransaction from @paperclipai/db, the same way
cases.ts, pipelines.ts and agent-invokability.ts already do. The guard's own
tests still pass and the server typechecks.

The shard-manifest freshness step in the same policy job also reports
missing suites; that step is continue-on-error by design and the suites it
names are master's, so it is left for the refresh-shard-manifest workflow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@kkroo

kkroo commented Sep 22, 2026

Copy link
Copy Markdown

Lease: pushing one mechanical fix for the red policy lane at 11a9b98. The alias guard this PR adds now trips on two files master added after the sweep (instance-settings.ts, pr-issue-backlink-lock.ts); both now import DbTransaction from @paperclipai/db. Guard clean, its 9 tests pass, server typecheck clean. The shard-manifest freshness step's missing suites are master's and that step is continue-on-error, so it is not touched here. Branch owner has no live run; lease-protected non-force push.

🤖 Generated with Claude Code

@allyblockcast allyblockcast Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ally — Consolidated PR Review

Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex. Nested CLIs are unavailable in this runtime; all three lens sets were applied directly against the diff and against a checkout at the exact head.
Reviewed head: 34b9355

Prior Findings Dispositioned (1)

  • prior:2048856 important 1 — fixed — scripts/check-db-transaction-alias.mjs:44 — The carve-out is now a path exemption, not a shape one. TX_ALIAS_REDERIVATION is single-level (/Parameters<[^>]*transaction[^>]*>\[0\]/, :44), the definition site is excluded by pathspec (:(exclude)packages/db/src/client.ts, :67, placed after -- at :64), the copied split line and the wrapped inner line are in BANNED (check-db-transaction-alias.test.mjs:21, :24), and the client.ts split is now asserted path-scoped (:62-75) rather than sitting shape-exempt in ALLOWED.
    Verified end-to-end through the real CI mechanism, not the JS regex: a tracked fixture carrying both evasions (type LocalTxCallback = Parameters<Db["transaction"]>[0]; plus the hand-wrapped inner line) produces 2 hits and exit 1; the real tree at this head is clean (exit 0); node --test is 9/9. Both bypasses named in the prior finding are closed.

Critical Issues (0)

Important Issues (0)

Suggestions (3)

  • [pr-review-toolkit/comments] scripts/check-db-transaction-alias.test.mjs:48 — The test is still named "does not flag the client.ts split that defines the replacement", but it iterates ALLOWED (:29), and the client.ts split moved out of ALLOWED into DEFINITION_SPLIT (:57) as part of this fix. Read alongside :62 — "the client.ts split is exempted by path, not by shape", which asserts split line 1 is flagged — the two names now make opposite claims about the same subject, and only the second is true. Rename :48 to what it actually covers ("does not flag legitimate unrelated Parameters<...> expressions"). Naming-only; no behavior change.
  • [native-codex] scripts/check-db-transaction-alias.mjs:44 — The trailing >\[0\] anchor leaves a three-name split unflagged, because the transaction line no longer carries an adjacent [0]:
    type TxArgs = Parameters<Db["transaction"]>;   // tuple — not matched
    type Tx     = Parameters<TxArgs[0]>[0];        // no `transaction` — not matched
    Confirmed through real git grep at this head: a tracked fixture with those two lines scans clean, while the spaced (> [0]) and newline-broken (>[\n 0\n]) forms are both caught. The previous head's generic-Db note (Parameters<Parameters<Db<Foo>["transaction"]>[0]>[0]) also survives, for the separate [^>]* reason. Both are hardening notes rather than live holes — neither spelling exists in the tree, and neither is a natural way to split this alias, whereas the two-line client.ts form was sitting there to be copied. Worth a line in the block comment at :35-42 so the next editor knows the anchor's cost is deliberate.
  • [pr-review-toolkit/tests] scripts/check-db-transaction-alias.test.mjs:62-116 — Unchanged from the previous head: every scanRepo test injects a fake exec, so the suite validates the pattern only under JS RegExp while CI runs it as POSIX ERE. This matters slightly more now that the pattern has been edited. I re-verified the two agree for the current pattern by shelling out to a real git grep this run, so it remains a latent risk rather than a live defect — but that verification lives in this review, not in the suite. One test against a throwaway git init fixture would close it permanently.

Strengths

  • The deviation from the literal suggestion is correct and I confirmed the reason rather than taking it on trust: the bare /Parameters<[^>]*transaction/ flags exactly two lines at this head — execution-workspaces-service.test.ts:1495 and productivity-review-service.test.ts:3268, both (...args: Parameters<typeof db.transaction>) — which is the method's argument tuple, not a handle derivation. Keeping >\[0\] is the right trade against the residual noted above.
  • The exemption is now tested as "allowed here": :74 asserts the exclude is present and :75 asserts it falls after --, so it is a pathspec rather than an option. That second assertion is the one that would catch the silent-no-op version of this fix.
  • Every type substitution is a genuine identity, re-checked at this head: companyService(db: Db) (companies.ts:58) and heartbeatService(db: Db, …) (heartbeat.ts:12284) make typeof db exactly Db, and the removed local aliases in instance-settings.ts / pr-issue-backlink-lock.ts were character-identical to client.ts:1019-1020.
  • No import went stale in either direction: Db is still used in all three files that kept it, and dropping it from pr-review-dispatch-lock.ts is right — zero remaining references.
  • Reusing the repo's check-*.mjs + check-*.test.mjs convention (~20 existing pairs) rather than pulling in ESLint for one rule is the correct call, and check-forbidden-tokens.mjs is not a reuse target — it is a secret-leak scanner, not a lint host.
  • scanRepo fails closed on both non-matching failure modes (exit 128, and exit 1 that still produced output), and both are tested. That is the error direction that costs something here.

Recommended Action

  1. No blocking changes requested.
  2. Merge once the remaining required CI checks finish green.

@kkroo
kkroo added this pull request to the merge queue Sep 22, 2026
@allyblockcast

allyblockcast Bot commented Sep 22, 2026

Copy link
Copy Markdown
Author

This PR is clean at its current head but still has an outstanding code-owner review request (kkroo, allyblockcast). GitHub does not enforce CODEOWNERS on this repository, so the landing routine holds it here rather than enqueuing it.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 24, 2026
@github-actions

Copy link
Copy Markdown

Merge-queue ejection detected for PR #1953. The merge-group run was cancelled (a job timeout surfaces this way) and GitHub may have removed the PR from the queue and dropped auto-merge. Inspect the merge-group jobs, fix or rerun the failing checks, then re-enqueue the PR.

Run: https://github.com/Blockcast/paperclip/actions/runs/35948766367

@kkroo
kkroo merged commit c302029 into master Sep 24, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant