diff --git a/docs/case-studies/issue-123/README.md b/docs/case-studies/issue-123/README.md new file mode 100644 index 0000000..cca18c7 --- /dev/null +++ b/docs/case-studies/issue-123/README.md @@ -0,0 +1,217 @@ +# Case Study: Issue #123 - Badge Not Found + +## Issue Summary + +**Issue:** [#123 - badge not found](https://github.com/link-assistant/agent/issues/123) +**Release:** [js-v0.8.4](https://github.com/link-assistant/agent/releases/tag/js-v0.8.4) +**Date:** 2026-01-16 +**Type:** Bug + +The GitHub release notes for version 0.8.4 displayed a "404 badge not found" error from shields.io instead of the expected npm version badge. + +## Timeline of Events + +### 1. Issue #121 - Release Style Problem +**Date:** 2026-01-13 + +User reported that release style didn't match the template repository: +- Wrong release name format (should use `[js]` prefix) +- Wrong changelog path (should use `js/CHANGELOG.md`) +- Release descriptions lacked actual changelog content + +### 2. PR #122 - Fix Implementation +**Date:** 2026-01-13 to 2026-01-16 + +PR #122 was created to fix issue #121. Key changes in `scripts/create-github-release.mjs`: + +```javascript +// Before (line 26): +const changelogPath = prefix === 'rust-' ? './rust/CHANGELOG.md' : './CHANGELOG.md'; + +// After: +const changelogPath = + prefix === 'rust-' + ? './rust/CHANGELOG.md' + : prefix === 'js-' + ? './js/CHANGELOG.md' + : './CHANGELOG.md'; + +// Before (line 49): +const releaseName = prefix ? `${prefix.replace(/-$/, '')} ${version}` : version; + +// After: +const releaseName = prefix + ? `[${prefix.replace(/-$/, '')}] ${version}` + : version; +``` + +### 3. Release js-v0.8.4 +**Date:** 2026-01-16 06:26:26Z + +- PR #122 merged to main +- CI/CD pipeline triggered +- Version bump: 0.8.3 → 0.8.4 +- GitHub release created with tag `js-v0.8.4` +- Release name: `[js] 0.8.4` ✅ +- Changelog content: correct ✅ +- NPM badge: **404 badge not found** ❌ + +## Root Cause Analysis + +### The Bug Location + +**File:** `scripts/format-github-release.mjs` (line 82) + +```javascript +const tag = `${prefix}v${version}`; +// ... +await $`node scripts/format-release-notes.mjs --release-id "${releaseId}" --release-version "${tag}" ...`; +``` + +The `--release-version` argument receives the **full tag** (`js-v0.8.4`), not just the version (`0.8.4`). + +**File:** `scripts/format-release-notes.mjs` (lines 192-193) + +```javascript +const versionWithoutV = version.replace(/^v/, ''); +const npmBadge = `[![npm version](https://img.shields.io/badge/npm-${versionWithoutV}-blue.svg)](...)`; +``` + +The regex `^v` only removes a leading `v`, so: +- Input: `js-v0.8.4` +- After `.replace(/^v/, '')`: `js-v0.8.4` (unchanged - no leading v!) +- Badge URL: `https://img.shields.io/badge/npm-js-v0.8.4-blue.svg` + +### Why This Fails + +Shields.io static badges use dashes (`-`) as delimiters in the URL format: + +``` +https://img.shields.io/badge/LABEL-MESSAGE-COLOR +``` + +When the version is `js-v0.8.4`, the URL becomes: +``` +https://img.shields.io/badge/npm-js-v0.8.4-blue + ├─┘ ├┘ └─────┬──────┘ + LABEL │ Interpreted as COLOR + MESSAGE +``` + +Shields.io interprets this as: +- **Label:** `npm` +- **Message:** `js` +- **Color:** `v0.8.4-blue` (invalid color!) + +Result: 404 badge not found error. + +### Working vs Broken URL Comparison + +| URL | Result | +|-----|--------| +| `https://img.shields.io/badge/npm-0.8.4-blue.svg` | ✅ Shows `npm | 0.8.4` | +| `https://img.shields.io/badge/npm-js-v0.8.4-blue.svg` | ❌ Shows `404 | badge not found` | + +### Why Template Repository Works + +The template repository (`link-foundation/js-ai-driven-development-pipeline-template`) works correctly because it: +1. Uses simple version tags (`v0.3.0`) without prefix +2. The version passed to format-release-notes.mjs is just the version number + +In this repository (`link-assistant/agent`): +1. Uses prefixed tags (`js-v0.8.4`, `rust-v1.0.0`) for multi-language support +2. The full tag is incorrectly passed as the version + +## Proposed Solutions + +### Solution 1: Pass Pure Version (Recommended) + +**Change in `format-github-release.mjs`:** + +```javascript +// Before (line 82): +await $`node scripts/format-release-notes.mjs --release-id "${releaseId}" --release-version "${tag}" ...`; + +// After: +await $`node scripts/format-release-notes.mjs --release-id "${releaseId}" --release-version "v${version}" ...`; +``` + +This passes `v0.8.4` instead of `js-v0.8.4`, and the existing regex in `format-release-notes.mjs` handles the `v` prefix correctly. + +### Solution 2: Update Regex in format-release-notes.mjs + +**Change in `format-release-notes.mjs`:** + +```javascript +// Before (line 192): +const versionWithoutV = version.replace(/^v/, ''); + +// After - handle both js-v and v prefixes: +const versionWithoutV = version.replace(/^(js-|rust-)?v/, ''); +``` + +### Solution 3: URL-encode the Badge Text + +If dashes must be preserved, use shields.io URL encoding (`--` for literal dash): + +```javascript +const encodedVersion = versionWithoutV.replace(/-/g, '--'); +const npmBadge = `[![npm version](https://img.shields.io/badge/npm-${encodedVersion}-blue.svg)](...)`; +``` + +### Recommended Approach + +**Solution 1** is recommended because: +1. Minimal code change +2. Keeps format-release-notes.mjs simple and generic +3. The caller knows the context (prefix) and should clean it up + +## Evidence and Data + +### Files Collected + +- `data/release-info.json` - Release metadata from GitHub API +- `data/pr-122-info.json` - PR #122 details and commits +- `data/pr-122-diff.txt` - PR #122 code changes +- `data/issue-121-details.txt` - Original issue that triggered the fix +- `data/ci-run-21057802787.log` - CI workflow logs showing the release process +- `screenshots/badge-not-found.png` - Screenshot showing the 404 badge error + +### Key Log Evidence + +From CI run logs: +``` +Release Format GitHub release notes Formatting release notes for js-v0.8.4... +Release Format GitHub release notes ℹ️ Found Patch Changes section +Release Format GitHub release notes ℹ️ Looking up PR for commit afcd2f8 (from changelog) +Release Format GitHub release notes ✅ Found PR #122 containing commit +Release Format GitHub release notes - Added shields.io npm badge <-- Badge added but URL is wrong +``` + +### Badge URL Test Results + +```bash +# Broken URL +$ curl -s "https://img.shields.io/badge/npm-js-v0.8.4-blue.svg" | grep -o 'aria-label="[^"]*"' +aria-label="404: badge not found" + +# Working URL +$ curl -s "https://img.shields.io/badge/npm-0.8.4-blue.svg" | grep -o 'aria-label="[^"]*"' +aria-label="npm: 0.8.4" +``` + +## References + +- [Issue #123](https://github.com/link-assistant/agent/issues/123) - This bug report +- [Issue #121](https://github.com/link-assistant/agent/issues/121) - Original style issue that triggered the buggy fix +- [PR #122](https://github.com/link-assistant/agent/pull/122) - The PR that introduced this bug +- [Release js-v0.8.4](https://github.com/link-assistant/agent/releases/tag/js-v0.8.4) - Affected release +- [Shields.io Static Badge Docs](https://shields.io/badges/static-badge) - Badge URL format specification +- [Template Repository](https://github.com/link-foundation/js-ai-driven-development-pipeline-template) - Reference implementation + +## Lessons Learned + +1. **Test with all prefixes:** When modifying release scripts that support multiple prefixes (js-, rust-), test all variants. +2. **Understand URL encoding:** Special characters like dashes have meaning in shields.io URLs. +3. **Trace data flow:** The bug wasn't in the modified files, but in how data flowed between scripts. +4. **Regression testing:** The fix for #121 inadvertently broke the badge because the version format changed. diff --git a/docs/case-studies/issue-123/data/issue-121-details.txt b/docs/case-studies/issue-123/data/issue-121-details.txt new file mode 100644 index 0000000..51770b2 --- /dev/null +++ b/docs/case-studies/issue-123/data/issue-121-details.txt @@ -0,0 +1,15 @@ +title: Release was done not in expected style +state: CLOSED +author: konard +labels: bug, documentation +comments: 0 +assignees: +projects: +milestone: +number: 121 +-- +We should use [js] prefix in name, version is fine. + +But the description of the release must be like in https://github.com/link-foundation/js-ai-driven-development-pipeline-template + +Please use all latest best practices of https://github.com/link-foundation/js-ai-driven-development-pipeline-template for our JavaScript CI/CD. If we have something even better - please file issue to https://github.com/link-foundation/js-ai-driven-development-pipeline-template with proposal. diff --git a/docs/case-studies/issue-123/data/pr-122-diff.txt b/docs/case-studies/issue-123/data/pr-122-diff.txt new file mode 100644 index 0000000..f3f023c --- /dev/null +++ b/docs/case-studies/issue-123/data/pr-122-diff.txt @@ -0,0 +1,56 @@ +diff --git a/js/.changeset/fix-release-style.md b/js/.changeset/fix-release-style.md +new file mode 100644 +index 0000000..d09eb9d +--- /dev/null ++++ b/js/.changeset/fix-release-style.md +@@ -0,0 +1,11 @@ ++--- ++'@link-assistant/agent': patch ++--- ++ ++Fix GitHub release style to match template repository standards ++ ++- Fix release name format to use `[js]` prefix instead of `js ` (e.g., `[js] 0.8.4` instead of `js 0.8.4`) ++- Fix changelog path for js releases to use `js/CHANGELOG.md` instead of root `CHANGELOG.md` ++- This ensures release descriptions contain actual changelog content with PR links and npm badges ++ ++Fixes #121 +diff --git a/scripts/create-github-release.mjs b/scripts/create-github-release.mjs +index 749a73a..69fcc4f 100644 +--- a/scripts/create-github-release.mjs ++++ b/scripts/create-github-release.mjs +@@ -61,12 +61,20 @@ console.log(`Creating GitHub release for ${tag}...`); + + try { + // Read CHANGELOG.md - check prefix for appropriate changelog location +- const changelogPath = prefix === 'rust-' ? './rust/CHANGELOG.md' : './CHANGELOG.md'; ++ // js- prefix uses js/CHANGELOG.md, rust- prefix uses rust/CHANGELOG.md, others use root ++ const changelogPath = ++ prefix === 'rust-' ++ ? './rust/CHANGELOG.md' ++ : prefix === 'js-' ++ ? './js/CHANGELOG.md' ++ : './CHANGELOG.md'; + let changelog = ''; + try { + changelog = readFileSync(changelogPath, 'utf8'); + } catch { +- console.log(`No changelog found at ${changelogPath}, using default release notes`); ++ console.log( ++ `No changelog found at ${changelogPath}, using default release notes` ++ ); + } + + // Extract changelog entry for this version +@@ -87,7 +95,10 @@ try { + // Create release using GitHub API with JSON input + // This avoids shell escaping issues that occur when passing text via command-line arguments + // (Previously caused apostrophes like "didn't" to appear as "didn'''" in releases) +- const releaseName = prefix ? `${prefix.replace(/-$/, '')} ${version}` : version; ++ // Release name format: "[prefix] version" for prefixed releases (e.g., "[js] 0.8.3"), just version otherwise ++ const releaseName = prefix ++ ? `[${prefix.replace(/-$/, '')}] ${version}` ++ : version; + const payload = JSON.stringify({ + tag_name: tag, + name: releaseName, diff --git a/docs/case-studies/issue-123/data/pr-122-info.json b/docs/case-studies/issue-123/data/pr-122-info.json new file mode 100644 index 0000000..d71e20b --- /dev/null +++ b/docs/case-studies/issue-123/data/pr-122-info.json @@ -0,0 +1 @@ +{"author":{"id":"MDQ6VXNlcjE0MzE5MDQ=","is_bot":false,"login":"konard","name":"Konstantin Diachenko"},"body":"## Summary\n- Fix release name format to use `[js]` prefix (e.g., `[js] 0.8.4` instead of `js 0.8.4`)\n- Fix changelog path for js releases to use `js/CHANGELOG.md` instead of root `CHANGELOG.md`\n- This ensures release descriptions contain actual changelog content with PR links and npm badges\n\n## Problem\nThe release script was using incorrect changelog path and release name format:\n1. **Wrong changelog path**: For `js-` prefixed releases, it was reading `./CHANGELOG.md` (root) instead of `./js/CHANGELOG.md`\n2. **Wrong name format**: Used `js 0.8.3` format instead of `[js] 0.8.3`\n\nThis caused releases to show a generic `Release X.X.X` description instead of the actual changelog content.\n\n## Solution\nUpdated `scripts/create-github-release.mjs` to:\n1. Read the correct changelog file based on the prefix\n2. Format release names with brackets around the prefix\n\n## Test plan\n- [ ] Verify next release uses `[js]` prefix format in the release name\n- [ ] Verify next release description contains actual changelog content\n- [ ] Verify PR link and npm badge appear in release notes\n\nFixes #121\n\n---\n🤖 Generated with [Claude Code](https://claude.com/claude-code)","commits":[{"authoredDate":"2026-01-13T21:03:41Z","authors":[{"email":"drakonard@gmail.com","id":"MDQ6VXNlcjE0MzE5MDQ=","login":"konard","name":"konard"}],"committedDate":"2026-01-13T21:03:41Z","messageBody":"Adding CLAUDE.md with task information for AI processing.\nThis file will be removed when the task is complete.\n\nIssue: https://github.com/link-assistant/agent/issues/121","messageHeadline":"Initial commit with task details","oid":"c662d6518214a71b186e6b9a14f165067b9a45ed"},{"authoredDate":"2026-01-13T21:07:35Z","authors":[{"email":"drakonard@gmail.com","id":"MDQ6VXNlcjE0MzE5MDQ=","login":"konard","name":"konard"},{"email":"noreply@anthropic.com","id":"MDQ6VXNlcjgxODQ3","login":"claude","name":"Claude Opus 4.5"}],"committedDate":"2026-01-13T21:07:35Z","messageBody":"- Fix release name format to use [js] prefix (e.g., \"[js] 0.8.4\")\n- Fix changelog path for js releases to use js/CHANGELOG.md\n- Ensures releases have proper descriptions with PR links and npm badges\n\nFixes #121\n\nCo-Authored-By: Claude Opus 4.5 ","messageHeadline":"fix: align GitHub release style with template repository","oid":"afcd2f83ac180e5668eed315e915a2f0bac917a6"},{"authoredDate":"2026-01-13T21:11:14Z","authors":[{"email":"drakonard@gmail.com","id":"MDQ6VXNlcjE0MzE5MDQ=","login":"konard","name":"konard"},{"email":"noreply@anthropic.com","id":"MDQ6VXNlcjgxODQ3","login":"claude","name":"Claude Opus 4.5"}],"committedDate":"2026-01-13T21:11:14Z","messageBody":"Co-Authored-By: Claude Opus 4.5 ","messageHeadline":"chore: remove automated setup file","oid":"9c45b0443b7b72c1782fd5721d30debfe107af09"}],"files":[{"path":"js/.changeset/fix-release-style.md","additions":11,"deletions":0},{"path":"scripts/create-github-release.mjs","additions":14,"deletions":3}],"mergedAt":"2026-01-16T06:26:26Z","title":"fix: align GitHub release style with template repository"} diff --git a/docs/case-studies/issue-123/data/release-info.json b/docs/case-studies/issue-123/data/release-info.json new file mode 100644 index 0000000..3aa02d0 --- /dev/null +++ b/docs/case-studies/issue-123/data/release-info.json @@ -0,0 +1 @@ +{"author":{"id":"MDM6Qm90NDE4OTgyODI=","login":"github-actions[bot]"},"body":"Fix GitHub release style to match template repository standards\n- Fix release name format to use `[js]` prefix instead of `js ` (e.g., `[js] 0.8.4` instead of `js 0.8.4`)\n- Fix changelog path for js releases to use `js/CHANGELOG.md` instead of root `CHANGELOG.md`\n- This ensures release descriptions contain actual changelog content with PR links and npm badges\n\nFixes #121\n\n**Related Pull Request:** #122\n\n---\n\n[![npm version](https://img.shields.io/badge/npm-js-v0.8.4-blue.svg)](https://www.npmjs.com/package/@link-assistant/agent/v/js-v0.8.4)","createdAt":"2026-01-16T06:28:24Z","name":"[js] 0.8.4","publishedAt":"2026-01-16T06:29:04Z","tagName":"js-v0.8.4"} diff --git a/docs/case-studies/issue-123/screenshots/badge-not-found.png b/docs/case-studies/issue-123/screenshots/badge-not-found.png new file mode 100644 index 0000000..b8e870e Binary files /dev/null and b/docs/case-studies/issue-123/screenshots/badge-not-found.png differ diff --git a/experiments/test-badge-url.mjs b/experiments/test-badge-url.mjs new file mode 100644 index 0000000..9d82cad --- /dev/null +++ b/experiments/test-badge-url.mjs @@ -0,0 +1,55 @@ +#!/usr/bin/env node + +/** + * Test script to verify shields.io badge URL generation + * + * This tests the fix for issue #123 where the badge URL was incorrectly + * generated with the tag prefix (js-v0.8.4) instead of just the version (v0.8.4) + * + * Run: node experiments/test-badge-url.mjs + */ + +const PACKAGE_NAME = '@link-assistant/agent'; + +// Test cases +const testCases = [ + { version: 'v0.8.4', description: 'Correct: version with v prefix only' }, + { version: 'js-v0.8.4', description: 'Incorrect: full tag with js- prefix' }, + { version: '0.8.4', description: 'Edge case: version without v prefix' }, + { version: 'rust-v1.0.0', description: 'Incorrect: full tag with rust- prefix' }, +]; + +console.log('Testing shields.io badge URL generation\n'); +console.log('='.repeat(80) + '\n'); + +for (const { version, description } of testCases) { + // Current code logic + const versionWithoutV = version.replace(/^v/, ''); + const badgeUrl = `https://img.shields.io/badge/npm-${versionWithoutV}-blue.svg`; + const npmUrl = `https://www.npmjs.com/package/${PACKAGE_NAME}/v/${versionWithoutV}`; + + console.log(`Test: ${description}`); + console.log(` Input version: "${version}"`); + console.log(` After .replace(/^v/, ''): "${versionWithoutV}"`); + console.log(` Badge URL: ${badgeUrl}`); + + // Check if URL will work + const parts = versionWithoutV.split('-'); + const isValid = parts.length === 1 || (parts.length === 3 && /^\d+$/.test(parts[0])); + + // A simple heuristic: if the version contains dashes that aren't part of semver, + // shields.io will misinterpret it + const hasProblem = /^[a-z]+-/.test(versionWithoutV); + + if (hasProblem) { + console.log(` Result: ❌ WILL FAIL - shields.io interprets dashes as delimiters`); + console.log(` Parsed as: LABEL="npm", MESSAGE="${parts[0]}", COLOR="${parts.slice(1).join('-')}"`); + } else { + console.log(` Result: ✅ Should work correctly`); + } + console.log(''); +} + +console.log('='.repeat(80)); +console.log('\nFix: Pass "v${version}" instead of "${tag}" to format-release-notes.mjs'); +console.log('This ensures the version never has the prefix (js-, rust-) in the badge URL.'); diff --git a/js/.changeset/fix-badge-url.md b/js/.changeset/fix-badge-url.md new file mode 100644 index 0000000..c758cde --- /dev/null +++ b/js/.changeset/fix-badge-url.md @@ -0,0 +1,12 @@ +--- +'@link-assistant/agent': patch +--- + +Fix shields.io badge URL in GitHub release notes + +- Fixed badge URL generation that was broken by tag prefixes (js-, rust-) +- The `format-github-release.mjs` script now passes `v${version}` instead of the full tag +- This ensures the badge URL contains only the version number (e.g., `0.8.4`) without prefix +- See `docs/case-studies/issue-123` for detailed root cause analysis + +Fixes #123 diff --git a/scripts/format-github-release.mjs b/scripts/format-github-release.mjs index 4f13705..9ecd4c2 100644 --- a/scripts/format-github-release.mjs +++ b/scripts/format-github-release.mjs @@ -79,7 +79,10 @@ try { console.log(`Formatting release notes for ${tag}...`); // Pass the trigger commit SHA for PR detection // This allows proper PR lookup even if the changelog doesn't have a commit hash - await $`node scripts/format-release-notes.mjs --release-id "${releaseId}" --release-version "${tag}" --repository "${repository}" --commit-sha "${commitSha}"`; + // IMPORTANT: Pass version with v-prefix only (e.g., "v0.8.4"), NOT the full tag (e.g., "js-v0.8.4") + // The format-release-notes.mjs script uses this for shields.io badge URL which uses dashes as delimiters + // See: docs/case-studies/issue-123 for detailed root cause analysis + await $`node scripts/format-release-notes.mjs --release-id "${releaseId}" --release-version "v${version}" --repository "${repository}" --commit-sha "${commitSha}"`; console.log(`\u2705 Formatted release notes for ${tag}`); } } catch (error) {