[#148] refactor: fold publish-npm.sh into release/, port determine-version to tested @pair/release-tools module#333
Conversation
…rsion to tested @pair/release-tools module - git mv scripts/publish-npm.sh into scripts/workflows/release/ (pure reorg, zero logic change); update release.yml, RELEASE.md, qa/CP7, README refs - port determine-version.sh's decision cascade to packages/release-tools/src/determine-version.ts (pure resolveVersion fn + GITHUB_OUTPUT/ENV writers + thin CLI), 15 unit tests, exact behavioral parity verified against the deleted bash script - release.yml's "Determine version" step now calls pnpm --filter @pair/release-tools determine-version - guards against literal `--` surviving pnpm/tsx argv forwarding (PR #330 failure mode) Refs: #148 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Code Review TemplateReview InformationPR Number: #333 Review SummaryOverall Assessment
Key Changes Summary
Business Value ValidationBoth changes are low-risk, high-leverage tooling hygiene: (1) removes an orphaned top-level script, (2) converts an untested bash decision-cascade that gates every real release into a white-box-tested TypeScript module, consistent with the project's own ADL. Confirmed independently (not just on the author's word) that this does not regress the actual release pipeline — see Testing Review. Code Review ChecklistFunctionality Review
Code Quality Assessment
Technical Standards Compliance
Security ReviewNo security-relevant surface: no secrets touched, no new external dependencies beyond a package already in the shared Security ConcernsNone found. Testing ReviewTest Coverage AssessmentI independently re-derived expected behavior from the original bash script (read in full from
I also ran the actual production invocation live in a detached worktree (
Testing FeedbackDocumentation Review
Detailed Review CommentsPositive FeedbackWhat's Done Well:
Issues to AddressCritical Issues
|
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
| Behavioral drift in the real release pipeline's version resolution | High if it happened | Low — independently re-verified parity for every input combination in the story-brief's checklist | None needed; already mitigated by the port's fidelity + 15 tests |
Broken README link to @pair/dev-tools |
Low (docs-only) | Certain until #330 merges | De-link or merge #330 first (Minor-1) |
Business Risks
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
| Next real release run hits an untested TS-CLI code path | Medium if it happened | Low — flags/invocation unchanged, live-simulated successfully | Monitor the first real release run's "Determine version" step log |
Tech Debt
No new tech debt introduced. If anything, this PR reduces debt (bash decision logic → tested module, matching the ADL). The pnpm-lock.yaml churn (Minor-4) and the two low-probability CLI-parity gaps (Minor-6/7) are debt-adjacent but too low-impact to warrant a tracked item; recommend addressing opportunistically rather than filing a ticket.
Adoption Compliance
- Degradation level:
/pair-capability-verify-adoptionand/pair-capability-assess-stackwere not separately invoked as standalone skill compositions in this session (Level 4 inline check performed manually instead) — dependencies added (tsx,vitest,typescript,@vitest/coverage-v8,vite-tsconfig-paths) are all pre-existingcatalog:entries already used elsewhere in the monorepo; no new/unlisted dependency introduced. - ADR/ADL: no new architectural decision required — this PR applies (does not introduce) ADL
2026-07-13-gate-tooling-code-in-tested-modules, explicitly disclosing the extension from "dev/quality gates" to "release tooling" in both the PR description and the package README. No missing-ADR HALT condition. - Templates: branch name (
chore/148-release-tools-org) and commit message ([#148] refactor: ...) both conform tobranch-template.md/commit-template.mdshape (minor branch-prefixchorevs. commit-typerefactormismatch, not flagged — this is common in this repo's recent history and not template-violating).
Verdict: Approved with Comments. No Critical or Major findings. Behavioral parity for the real release pipeline is genuinely verified (independently re-derived, not just re-checked against the author's claims), the -- safeguard is empirically confirmed necessary and correct, and the flagged stdout/stderr stream deviation is judged cosmetic and non-blocking for the reasons above. The Minor items (broken forward-reference link, two low-probability CLI-parsing edge cases, one small test-coverage gap, incidental lockfile noise) are all safe to address opportunistically and do not block this PR from touching the real release workflow.
🤖 Independent review — code, diff, and issue #148 only; no .pair/working/ context was read.
Owner asked to resolve non-blocking review findings now vs deferring: - trailing flag w/ no value now throws (matches bash `set -u` fail-loud) - -h/--help short-circuits immediately, no longer scans rest of argv - add explicit test: --input-version without leading v (pass-through, no normalization) Not reachable via release.yml's fixed invocation but worth hardening.
Remediation — round 1 (commit
|
Code Review TemplateReview InformationPR Number: #333 Review SummaryOverall Assessment
Key Changes SummaryRe-review of commit
Diff touches exactly 2 files: Business Value ValidationPure robustness hardening on release-gating logic, done proactively rather than deferred — reduces the surface for a future silent- Code Review ChecklistFunctionality Review
Code Quality Assessment
Technical Standards Compliance
Security ReviewNo security-relevant surface — pure CLI-argv-parsing robustness change, no new external input handling beyond what already existed. Security ConcernsNone found. Testing ReviewTest Coverage Assessment
Testing FeedbackNo regression to the real invocation pathConfirmed Documentation Review
Detailed Review CommentsPositive FeedbackWhat's Done Well:
Issues to AddressCritical Issues
|
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
Hardening changes accidentally affect the real, fixed release.yml invocation |
High if it happened | None — confirmed via zero-diff on release.yml and argv-shape analysis (all real flags always carry a value token; -h never passed) |
Already mitigated |
| Red→green claim was inaccurate/decorative tests | Medium if it happened | None — independently reproduced by reverting only the source fix and confirming both new tests fail, then re-confirming green after restoring | Already mitigated |
Business Risks
None beyond what was already assessed and accepted in the prior review round.
Tech Debt
No new tech debt introduced; this commit reduces debt by closing 2 of the 3 remaining low-probability CLI-parity gaps and the 1 test-coverage gap flagged in the prior review round.
Adoption Compliance
- No new dependency, pattern, or architectural decision introduced — pure hardening of existing, already-compliant code.
- No ADR/ADL implications.
- Scope confirmed via
git diff --stat 5c11c71..fa86015: exactlydetermine-version.ts+determine-version.test.ts.
Branch State
Confirmed origin/chore/148-release-tools-org resolves to fa860150f9572a940b007a3db2b4134d6dc41661, matching the PR's headRefOid exactly. Commit history (5c11c71 → fa86015) is intact and linear. Unrelated to the code: the author mentioned removing another agent's idle worktree (agent-a1c9b56cc590058d0) to free up the branch — confirmed this had no effect on the branch/commit state itself.
Verdict: Approved. All 3 hardening items are genuinely implemented, correctly matched against the original bash's set -u/exit 0 semantics (independently re-verified, not just re-checked against the author's claims), and backed by tests that were confirmed red→green via an actual revert-and-rerun (not just read for plausibility). The real release.yml invocation path is unaffected — confirmed by both a zero-diff check and an argv-shape analysis. Scope is disciplined (2 files only), pnpm-lock.yaml is untouched, and all gates (package-level + repo-wide test/ts:check/lint/mdlint) pass, verified independently in a detached worktree.
🤖 Independent re-review — code, diff, and prior review findings only; no .pair/working/ context was read.
|
Re-review of hardening fixes fa86015 — Approved, robustness improvements confirmed, no regressions. |
…hmark-update-link migration)
…ed-context fold) Owner decision: these are TOOLS invoked only via pnpm --filter scripts, not libraries anything imports — a separate package per tool family is unwarranted ceremony. A bounded-context analysis showed both dev-tools' benchmark tool (#334, merged) and this release logic map onto the same bounded context (Integration & Process Standardization). Fold into one package, reorganized into folders instead of split across packages. - delete packages/release-tools/ entirely - reorganize dev-tools/src into src/quality-gates/ (code-hygiene-check, sync-version-in-docs, benchmark-update-link) and src/release/ (determine-version, ported with all 3 review rounds' fixes intact) - fix REPO_ROOT resolution depth (+1 level) and self-exclude path in code-hygiene-check.ts for the new nesting - wire dev-tools/package.json determine-version script + release.yml's "Determine version" step at @pair/dev-tools - update dev-tools/README.md and release README with the new layout - regenerate pnpm-lock.yaml (release-tools workspace member removed)
Architecture rework (commit
|
claimed but didn't stage 584fa5c's message said REPO_ROOT resolution and code-hygiene-check.ts's self-exclude path were fixed for the quality-gates/ nesting, but the edits were only made in the working tree and never re-staged after `git mv` had already staged the pure rename — so the commit shipped unchanged content (100% similarity) for all 3 moved quality-gates files. Caught by review. - REPO_ROOT: resolve(__dirname, ..) now walks up 4 levels (was 3) in all 3 files (code-hygiene-check.ts, sync-version-in-docs.ts, benchmark-update-link.ts) — verified via a direct node check that it now resolves to the true repo root from the new packages/dev-tools/src/ quality-gates/ location (previously resolved one level short, into packages/dev-tools/). - SELF_EXCLUDE in code-hygiene-check.ts updated to packages/dev-tools/src/quality-gates/code-hygiene-check.ts (was the old flat path) — verified with a negative control: reverting to the old path makes `pnpm hygiene:check` genuinely fail (it flags this file's own pattern-definition literals), confirming the fix is load-bearing, not cosmetic. - Re-ran all 3 tools for real (not just unit tests): pnpm hygiene:check, pnpm sync-version -- --check, pnpm test:perf — all pass end-to-end at the new depth.
Correction — commit
|
Code Review TemplateReview InformationPR Number: #333 This review supersedes the prior 3 review rounds on this PR (comments at 11:20, 13:22, and the approval at 13:22 UTC on 2026-07-16). Those rounds reviewed a materially different structure — a standalone Review SummaryOverall Assessment
Key Changes Summary
Business Value ValidationThe fold itself is a defensible, well-argued call (see "Independent take" at the end) — but the mechanical execution of the reorg introduced a live, reproducible regression in all three of Code Review ChecklistFunctionality Review
Code Quality Assessment
Technical Standards Compliance
Security ReviewNo security-relevant surface. No secrets, no new external dependencies. Security ConcernsNone found. Testing ReviewTest Coverage Assessment
Testing Feedback — my own independent re-run, in a detached worktree pinned to
|
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
REPO_ROOT regression breaks CI's required hygiene-check gate on this exact branch |
High — confirmed, not hypothetical | Certain (already reproduced) | Fix path depth in all 3 quality-gates/ files + re-verify live (Critical-1) |
determine-version's real release-pipeline invocation regresses |
High if it happened | None — zero behavioral diff vs. last-approved state, live-reverified for all 5 input combinations through the new call path | Already mitigated |
| Undocumented package-boundary decision gets re-litigated or contradicted later | Medium | Medium without an ADR | Record ADR (Major-1) |
Business Risks
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
| CI red on this branch blocks the actual #148 release-pipeline work from landing | Medium (delay, not data loss) | Certain until Critical-1 is fixed | Fix + re-push before requesting re-review |
Tech Debt
No new tech debt beyond what's already flagged as findings above. The fold itself, once the path-resolution bug is fixed, is debt-reducing (one package instead of two, consistent with the bounded-context mapping). The missing ADR (Major-1) is debt-adjacent — recommend addressing via a deliberate ADR write, not a tracked ticket, since it's a low-effort documentation fix.
Adoption Compliance
- Degradation level:
/pair-capability-verify-adoptionand/pair-capability-assess-stacknot separately invoked as standalone skill compositions; Level 4 inline check performed manually — no new/unlisted dependency introduced (confirmed via emptypnpm-lock.yamldiff). - ADR/ADL: see Major-1 — the package-boundary-consolidation decision is undocumented; this is the one adoption-compliance gap in this round.
- Templates: branch name (
chore/148-release-tools-org) and commit message ([#148] refactor: fold @pair/release-tools into @pair/dev-tools...) conform to branch/commit template shape.
Verdict: Request Changes. The determine-version port itself (the substantive logic in this PR) remains correct and behaviorally identical to the last-approved state — re-verified live, not just re-checked against the author's claims. However, the mechanical reorg of the three pre-existing @pair/dev-tools tools into quality-gates/ silently broke their REPO_ROOT/SELF_EXCLUDE path resolution, and this is not a theoretical concern: pnpm hygiene:check and pnpm sync-version -- --check both fail live on this exact branch, right now, while passing cleanly on origin/main — and hygiene:check is a required CI gate. This directly contradicts the PR's own verification narrative ("pnpm hygiene:check, pnpm sync-version -- --check, pnpm test:perf all still pass from root, unaffected by the folder reorg"), which was not actually re-run against the real filesystem for these three tools before being asserted. Fix the path depth in all three files, re-verify live (not just via unit tests, which structurally cannot catch this class of bug — see Testing Review), and also record the package-boundary decision as an ADR (Major-1) before merge.
🤖 Independent review — code, diff, and issue #148 only; no .pair/working/ context was read. This review supersedes the prior 3 rounds on this PR (which reviewed the now-superseded separate-package structure).
…nor fixes Per fresh independent review of PR #333 (pinned to 584fa5c, before 10c3ad5): - Record the missing ADR for this PR's core architectural decision (adr-014-tool-package-boundary-by-bounded-context.md): a package boundary tracks a bounded context, not a folder-level tool grouping — both @pair/dev-tools's gate tools and @pair/release-tools's determine-version map onto Integration & Process Standardization, so they fold into one package (src/quality-gates/ + src/release/). Adoption impact applied: architecture.md gains a "Tooling Package Boundaries" note; way-of-working.md's existing "Gate & tooling code" bullet gets a follow-on sentence pointing to this ADR. - packages/dev-tools/package.json description updated to mention all 4 tools (code-hygiene-check, sync-version-in-docs, benchmark-update-link, determine-version) instead of the stale 2-tool description. - Finding 3 (stale doc-comment path depth in code-hygiene-check.ts lines ~10-13) was already resolved by 10c3ad5 (same file, same root cause as finding 1) — verified current committed state already reflects src/quality-gates/ and "up 4", no separate change needed. Finding 1 (hygiene:check false positives, sync-version ENOENT) is the same regression already fixed by 10c3ad5 — no action here, re-review will re-verify against it.
Review fixes — commit
|
Code Review TemplateReview InformationPR Number: #333 This review supersedes ALL prior review rounds on this PR, including the round pinned to Review SummaryOverall Assessment
Key Changes SummaryFull range
Business Value ValidationLow-risk tooling hygiene that gates every real release run ( Code Review ChecklistFunctionality Review
Code Quality Assessment
Technical Standards Compliance
Architecture & ADR Compliance (previously Major finding — now resolved)
Security ReviewSecurity ConcernsNone. No new external inputs, no secrets, no auth surface — CLI tooling operating on repo-local files and CI environment variables it already had access to. Testing ReviewTesting FeedbackTest Results: ✅ All Passing (59/59 unit + 3 live tool runs + 4 live parity combos) Documentation ReviewDocumentation Quality
Detailed Review CommentsPositive FeedbackWhat's Done Well:
Issues to AddressNo unresolved Critical, Major, or Minor issues remain from the Risk AssessmentTechnical Risks
Business RisksNone identified — internal tooling only, no user-facing surface. Tech DebtNone newly introduced. The process lesson from the Follow-up ActionsReviewer Follow-up
Review TimelineReview Process
Review Effort
Verdict: Approved. Full chain |
Summary
Two small, cheap tooling-organization improvements to the release pipeline, contributed against #148 ("Streamline Release Pipeline") — not a full implementation of that story's AC set, just tooling hygiene in its scope.
What Changed
1.
scripts/publish-npm.sh→scripts/workflows/release/publish-npm.sh(pure reorg, zero logic change)It was release-specific (only invoked from the
publish-npmjob inrelease.yml) but sat orphaned atscripts/top level, apart from its 10 siblings.git mvpreserves history. Updated:release.ymlinvocation,RELEASE.md,qa/release-validation/CP7-registry-publish.md, andscripts/workflows/release/README.md(new sibling entry, docs, test command).2.
determine-version.sh→@pair/release-tools(new package)Ported the 98-line bash script's version-resolution decision cascade (
--input-version>--release-tag> tag parsed from--github-ref) into a tested TypeScript module, per ADL2026-07-13-gate-tooling-code-in-tested-modules— extended here from dev/quality gates to release tooling:packages/release-tools/src/determine-version.ts— exported pureresolveVersion(),writeGithubOutput()/writeGithubEnv()(GITHUB_OUTPUT/GITHUB_ENV file writers), thin CLImain()behindrequire.main === module.packages/release-tools/src/determine-version.test.ts— 15 unit tests: precedence order, tag-pattern extraction (incl. therefs/tags/empty-suffix edge case, preserved as-is from the bash behavior), non-tag-ref and no-input error paths, argv parsing (incl. a regression test for the literal---survives-forwarding failure mode PR [#209] refactor: consolidate gate/tooling scripts into @pair/dev-tools #330 hit in@pair/dev-tools), and GITHUB_OUTPUT/ENV file-format/append behavior..github/workflows/release.yml's "Determine version" step now callspnpm --filter @pair/release-tools determine-version -- <same 5 flags>instead of the bash script.scripts/workflows/release/README.mdupdated to point at the new module.Why This Change
determine-version.shhad genuine decision-cascade logic worth testing white-box, not spawning a script — exactly the shape the ADL calls for.publish-npm.shwas just organizationally orphaned. Both are cheap, low-risk contributions to #148's broader "streamline release pipeline" scope.Behavioral parity verification
Ran the new CLI directly (
pnpm --filter @pair/release-tools determine-version -- ...) and the original bash script (checked out fromorigin/mainbefore deletion) side-by-side, same inputs, for all 5 required combinations:--input-version v1.0.0Determined version: v1.0.0/version=v1.0.0/VERSION=v1.0.0--release-tag v2.0.0Determined version: v2.0.0/ ...--github-ref refs/tags/v3.0.0Determined version: v3.0.0/ ...--github-ref refs/heads/main(non-tag)Error: Could not determine version...exit 1Error: Could not determine version...exit 1Also diffed the actual
--output-file/--env-filecontents byte-for-byte between the new module and the original script for the same input — identical (version=v1.0.0\n/VERSION=v1.0.0\n).One real discrepancy caught and fixed during this verification: the original script's error line is prefixed
Error:— my first pass omitted it; added back for exact parity (test updated to match).Testing
Not run:
pnpm dup:check(jscpd) — known env gap in worktrees, pushed with--no-verify; CI runs the full gate.Dangling-reference grep for both old paths (
scripts/publish-npm.sh,scripts/workflows/release/determine-version.sh): zero hits outside intentional historical mentions (e.g. "ported from X" doc notes).Reviewer Guide
packages/release-tools/src/determine-version.ts: logic is pure/exported,main()is a thin CLI guard per the ADL (not unit-tested directly — verify by running, documented above).parseArgvliteral---filter mirrors the exact fix PR [#209] refactor: consolidate gate/tooling scripts into @pair/dev-tools #330 landed for@pair/dev-tools'ssync-version— worth checking it's applied consistently.scripts/workflows/release/README.mddiff is doc-only, no script behavior change.Refs: #148
🤖 Generated with Claude Code