Conversation
Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: #118
Capture the investigation that backs PR #119: timeline of CI/CD failures, requirement-by-requirement breakdown, root causes, the solution plan, a comparison with the four link-foundation pipeline templates, and a one-shot recipe for back-filling the two pre-badge JS releases (js-v0.25.5, js-v0.26.0). Raw inputs (issue JSON, recent run history, release bodies, failed-run logs) live alongside the analysis so the next investigator can pick up where this one stopped.
- check-mjs-syntax.sh: pre-flight `node --check` over every .mjs file so syntax errors fail in 5s instead of inside a test job. - simulate-fresh-merge.sh: validate the actual merged tree by merging the PR head into a throw-away worktree and re-running the syntax check. Catches conflict-resolution bugs that wouldn't show up against the PR head in isolation. - debug-print.mjs: ::debug:: logger that activates on START_DEBUG / RUNNER_DEBUG / ACTIONS_STEP_DEBUG so verbose tracing can stay in the code with the default state off. All three are ports of patterns from the link-foundation pipeline templates (see docs/case-studies/issue-118/comparison-with-templates.md).
Closes the gap between this repo and the link-foundation pipeline templates around release confidence. - check-release-needed.mjs: query the public registry (npm or crates.io) instead of git tags to decide whether a publish is required. Heals automatically if a previous run pushed a tag but failed to publish. - preflight-credentials.mjs: assert each required credential (GH_TOKEN, npm OIDC, crates.io reachability) before any publish step runs, emit a single ::error:: per failure so the cause is at the top of the run summary. - verify-release-badge.mjs: post-condition guard that fetches the freshly-created release body and fails the job when the per-version npm/crates.io badge is missing — the false-positive case the issue called out. - backfill-release-notes.mjs: one-shot helper to regenerate a release body for a given tag using the same template the publish path uses, for fixing pre-badge releases.
Adopts the patterns from the link-foundation pipeline templates: - New `syntax-check` job runs check-mjs-syntax.sh and (on PRs) simulate-fresh-merge.sh, fast-failing in <30s on broken .mjs. - Bump actions/checkout @v4→@v6, setup-node @v4→@v6, setup-dotnet @v4→@v5; Node 20.x → 24.x. - Per-job `timeout-minutes` everywhere so a hung step can't burn the free runner budget. - `if: always() && !cancelled()` guards on lint/test/coverage/release so transitive `if: always()` upstream no longer propagates `skipped` silently down the chain. - `concurrency.cancel-in-progress: github.ref != refs/heads/main` so in-flight main runs are never cancelled by a follow-up push. - Release jobs (auto + instant + manual) now call: 1. check-release-needed.mjs — registry-driven self-healing, 2. preflight-credentials.mjs — clear error on missing token/OIDC, 3. verify-release-badge.mjs — post-condition badge assertion. - Rust coverage job caches the cargo-tarpaulin binary (3-4 min/run).
`git merge --no-commit` requires a committer identity even though it won't write a commit (it still records MERGE_MSG). The GitHub runner has no global identity configured, so the script failed with "empty ident name" and then mis-reported it as a merge conflict. - Configure a local user.email/user.name on the throw-away worktree if no identity is set, scoped to that worktree only. - Distinguish "merge produced conflicts" from "git merge errored for another reason" by checking `git ls-files --unmerged` and printing the captured merge output before either ::error:: line.
Make the case-study claim true: each new pipeline script now imports debug() / dumpEnv() and prints its resolved arguments + relevant env keys when START_DEBUG, RUNNER_DEBUG, or ACTIONS_STEP_DEBUG is set. Default-off, so no extra log noise on green runs. Also tighten case-study/solutions.md and comparison-with-templates.md to match what's actually shipped (drop overstated claims about secretlint, jscpd, auto-detect crate name, and wiring debug into the bun-based scripts — those move to follow-up).
Member
Author
Working session summaryAll checks on PR #119 pass cleanly on the latest SHA
PR #119 is ready for review: #119 This summary was automatically extracted from the AI working session output. |
Member
Author
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $17.340520📊 Context and tokens usage:Claude Opus 4.7: (5 sub-sessions)
Total: (28.5K new + 487.5K cache writes + 21.7M cache reads) input tokens, 131.5K output tokens, $17.340520 cost 🤖 Models used:
📎 Log file uploaded as Gist (7074KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
Member
Author
✅ Ready to mergeThis pull request is now ready to be merged:
Monitored by hive-mind with --auto-restart-until-mergeable flag |
konard
added a commit
that referenced
this pull request
May 3, 2026
…oken `scripts/preflight-credentials.mjs` validated the token with `gh api user --jq .login`. That endpoint requires a *user* identity, so the auto-issued `secrets.GITHUB_TOKEN` (an installation token) returned HTTP 403 "Resource not accessible by integration", and both release pipelines failed at preflight on every push to `main` after PR #119 (runs 25282945820 and 25282945817). Switch the probe to `gh api repos/${GITHUB_REPOSITORY}` which works for both the installation token and personal access tokens (it only needs `metadata: read`, already granted by every workflow). Fall back to `gh auth status` when running locally without `GITHUB_REPOSITORY`. Also annotate `crates-io` HTTP 404 as "package not yet published" so a real lookup miss is not silently reported as `ok`. Switch from `execSync` to `execFileSync` so the repo slug is passed as a literal argv element rather than interpolated into a shell string. See `docs/case-studies/issue-120/root-cause.md`.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #118.
This PR is a CI/CD sweep across the JS and Rust pipelines, plus a deep
case study under
docs/case-studies/issue-118/thatcaptures the analysis behind every change.
Highlights
scripts/verify-release-badge.mjsafter the publish step. If theper-version
npm/crates.iobadge is missing from the releasebody, the job fails with a single
::error::line. (Fixes the"release exists but no badge — was it actually published?" case the
issue calls out.)
scripts/check-release-needed.mjsasks the registry (npm or crates.io) whether the current
package.json/Cargo.tomlversion is already published, instead oftrusting git tags. If a previous run created a tag but failed to
publish, the next push to
mainrecovers automatically.scripts/preflight-credentials.mjsasserts each required token (
GH_TOKEN, npm OIDC, crates.ioreachability) before any publish step runs. Missing or expired
credentials surface as a clear top-level error rather than a
buried
gh/npmfailure.scripts/debug-print.mjsexposes adebug()helper that activates on
START_DEBUG=1,RUNNER_DEBUG=1, orACTIONS_STEP_DEBUG=true. Off by default per CLAUDE.md guidelines.timeout-minutes,actions/checkout @v4 → @v6,setup-node @v4 → @v6, Node 20 → 24,if: always() && !cancelled()guards ondownstream jobs (so transitive
if: always()upstream no longerpropagates
skippedsilently), andconcurrency.cancel-in-progressscoped to non-
mainrefs only.syntax-checkjob runsscripts/check-mjs-syntax.sh(node --checkover every.mjs)and, on PRs,
scripts/simulate-fresh-merge.shwhich merges the PRhead into a throw-away worktree and re-runs the check against the
actual merged tree.
docs/case-studies/issue-118/contains thetimeline, requirement-by-requirement breakdown, root causes,
solution plan, comparison vs. the four link-foundation pipeline
templates, raw inputs (issue JSON, recent run history, release
bodies, failed-run logs), and a one-shot
backfill-recipe.mdfor the two pre-badge JS releases.
One-shot post-merge work (documented, not run by CI)
js-v0.25.5andjs-v0.26.0release bodies using therecipe in
docs/case-studies/issue-118/backfill-recipe.md. This isa manual step because mutating older releases is a one-time fix-up
rather than something CI should re-run on every push.
rust-v0.13.0/rust-v0.14.0were referenced in earlier drafts ofthe case study as also needing back-fill — verification (see the
recipe) showed neither tag nor release object exists for them, so
there is nothing to PATCH.
Test plan
bash scripts/check-mjs-syntax.sh— 26 .mjs files parse withnode --check.bash scripts/simulate-fresh-merge.sh— clean merge againstorigin/main.bun run lint(eslint) andbun run format:check(prettier) — both clean.node scripts/check-file-size.mjs— every file under the 1000-line limit.python3 -c 'import yaml; yaml.safe_load(...)'on both workflow files — valid.check-release-needed,preflight-credentials,verify-release-badge,backfill-release-notes --dry-run).