From fb6be461be35321ce258d583f7946ceff34a28d4 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 13 Sep 2026 17:42:30 +0900 Subject: [PATCH 1/4] [agent] docs(devlog): archive logic-analysis unit to _fin (v0.2.27 shipped) Co-authored-by: Cursor --- devlog/{_plan => _fin}/260913_logic_analysis_skill/000_plan.md | 0 .../260913_logic_analysis_skill/001_analysis_synthesis.md | 0 .../260913_logic_analysis_skill/010_wp1_skill_content.md | 0 .../260913_logic_analysis_skill/020_wp2_release.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename devlog/{_plan => _fin}/260913_logic_analysis_skill/000_plan.md (100%) rename devlog/{_plan => _fin}/260913_logic_analysis_skill/001_analysis_synthesis.md (100%) rename devlog/{_plan => _fin}/260913_logic_analysis_skill/010_wp1_skill_content.md (100%) rename devlog/{_plan => _fin}/260913_logic_analysis_skill/020_wp2_release.md (100%) diff --git a/devlog/_plan/260913_logic_analysis_skill/000_plan.md b/devlog/_fin/260913_logic_analysis_skill/000_plan.md similarity index 100% rename from devlog/_plan/260913_logic_analysis_skill/000_plan.md rename to devlog/_fin/260913_logic_analysis_skill/000_plan.md diff --git a/devlog/_plan/260913_logic_analysis_skill/001_analysis_synthesis.md b/devlog/_fin/260913_logic_analysis_skill/001_analysis_synthesis.md similarity index 100% rename from devlog/_plan/260913_logic_analysis_skill/001_analysis_synthesis.md rename to devlog/_fin/260913_logic_analysis_skill/001_analysis_synthesis.md diff --git a/devlog/_plan/260913_logic_analysis_skill/010_wp1_skill_content.md b/devlog/_fin/260913_logic_analysis_skill/010_wp1_skill_content.md similarity index 100% rename from devlog/_plan/260913_logic_analysis_skill/010_wp1_skill_content.md rename to devlog/_fin/260913_logic_analysis_skill/010_wp1_skill_content.md diff --git a/devlog/_plan/260913_logic_analysis_skill/020_wp2_release.md b/devlog/_fin/260913_logic_analysis_skill/020_wp2_release.md similarity index 100% rename from devlog/_plan/260913_logic_analysis_skill/020_wp2_release.md rename to devlog/_fin/260913_logic_analysis_skill/020_wp2_release.md From f9377c0685fd7717657ea34accb1a137d52c0236 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 13 Sep 2026 17:42:37 +0900 Subject: [PATCH 2/4] [agent] docs(devlog): logic-analysis unit done record Co-authored-by: Cursor --- .../260913_logic_analysis_skill/095_done.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 devlog/_fin/260913_logic_analysis_skill/095_done.md diff --git a/devlog/_fin/260913_logic_analysis_skill/095_done.md b/devlog/_fin/260913_logic_analysis_skill/095_done.md new file mode 100644 index 00000000..efedb49f --- /dev/null +++ b/devlog/_fin/260913_logic_analysis_skill/095_done.md @@ -0,0 +1,57 @@ +# 095 — done: logic-analysis skill unit + +## Outcome: DONE + +All four goal criteria met with fresh evidence; goalplan E8 gate passes +(`cxc loop validate` OK at close). + +## What shipped (v0.2.27) + +- **New reference** `plugins/codexclaw/skills/dev-debugging/references/logic-analysis.md` + (142 lines): the logic-analysis loop (precise question → isolate → domain + language → inventory the callable surface → slice → hypothesize from + names/strings/errors → observe static AND dynamic → prove by writing a + client), controlled mutation (one variable, canary, two encodings), the + incremental UNKNOWN-field model, a technique routing table with the + agent-accessible CLI slice, the honest human-lab boundary, the 9-rung + anti-give-up escalation ladder, and authorization/hostile-code limits. + Core rule: "I can't analyze this" is a skipped loop, not a limit. +- **dev-debugging SKILL.md** (414 → 431 lines): frontmatter triggers + (로직 파악, 뜯어봐, reverse engineer, ...), boundary route, compact Logic + Analysis section, references-table row, compact-summary item (8). +- **Cross-links** (user-requested mid-release): `cxc-dev` §2 + routing table + row; `cxc-search` Korean Intent Guard rule 1 fourth target class. +- **CHANGELOG** 0.2.27 entry; version bump across 16 surfaces. + +## Evidence + +- Analysis synthesis with `/tmp` clone `path:line` citations: `001_analysis_synthesis.md` + (three grok-4.6 explorer subagents: mytechnotalent, wtsxDev, Z0F). +- A-phase: reviewer da454926, round 1 GO-WITH-FIXES (1 blocker: Edit B + literal/anchor) → folded → round 2 PASS. +- Validation: gate OK; inventory+gate suites 21 pass / 0 fail; receipts at + `.codexclaw/evidence/0d7e3cc3-8bdf-4645-8cf6-a7d42a2fb2ab/test-receipt.json`. +- Delivery: PR #167 merged `b9e68930`; PR #168 merged `7e998908`; Release + run 34748229204 success → GitHub Release v0.2.27 (3 assets, non-prerelease, + target 7e998908, published 2026-09-13T08:41:40Z). A duplicate Release + dispatch queued in the same minute was cancelled; the fail-closed + re-publish guard never fired. + +## What did not improve / died hypotheses (LOOP-PESSIMIST-01) + +- Initial assumption "deploy = merge to main" died: the Release workflow is + dispatch-only with `expected_sha` + version-surface gates, so a 0.2.27 + prep commit + second PR was required. Recorded in 020 for the next release. +- The wtsxDev awesome-list turned out to be a binary-malware map with no + source-available or web/API lane — its value to the unit is the routing + table and the honest-lab boundary, not technique content. +- Not done (out of scope): cursorclaw/zclaw plugin port sync of the edited + skills; the installed cursor plugin copy still carries the pre-0.2.27 + dev-debugging until the port pipeline runs. + +## Commits + +- `352acc24` docs(plan): roadmap + synthesis +- `1c3ce3dc` feat(dev-debugging): logic-analysis reference +- `dfafaecf` chore(release): prepare 0.2.27 +- `b8d16f85` feat(skills): dev + search cross-links From 0691c1a0f47ea203d77a26ad6ff60213fe94ecd5 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 13 Sep 2026 18:17:23 +0900 Subject: [PATCH 3/4] [agent] ci(release): prepend the version's CHANGELOG section to release notes Co-authored-by: Cursor --- .github/scripts/changelog-section.cjs | 77 +++++++++++++++++++ .github/scripts/changelog-section.test.cjs | 87 ++++++++++++++++++++++ .github/workflows/release.yml | 9 ++- package.json | 2 +- 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 .github/scripts/changelog-section.cjs create mode 100644 .github/scripts/changelog-section.test.cjs diff --git a/.github/scripts/changelog-section.cjs b/.github/scripts/changelog-section.cjs new file mode 100644 index 00000000..3f4151b9 --- /dev/null +++ b/.github/scripts/changelog-section.cjs @@ -0,0 +1,77 @@ +"use strict"; + +/** + * Extract a version's section from CHANGELOG.md for the release body. + * + * The release workflow passes the section to `gh release create + * --generate-notes --notes-file
`; GitHub prepends the file to the + * auto-generated PR list. Kept as a pure module so the heading grammar can be + * unit-tested without Actions. + * + * Heading grammar (Keep a Changelog): + * ## [0.2.27] - 2026-09-13 (ASCII hyphen, current) + * ## [0.2.16] — 2026-08-30 (em dash, 0.2.1-0.2.16) + * The section ends at the next `#`/`##` heading or a `[label]:` link + * definition (the compare-link footer). + */ + +/** Escape a version string for use inside a RegExp. */ +function escapeRegExp(value) { + return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** + * Return the raw `## [version]` section (heading included, trailing + * whitespace trimmed). Throws Error with `.code` "MISSING" when no heading + * matches and "EMPTY" when the section has no body. + */ +function extractChangelogSection(changelogText, version) { + if (!version) { + const err = new Error("version is empty"); + err.code = "MISSING"; + throw err; + } + const startRe = new RegExp(`^## \\[${escapeRegExp(version)}\\](?:\\s|$)`); + const lines = String(changelogText).split(/\r?\n/); + const start = lines.findIndex((line) => startRe.test(line)); + if (start < 0) { + const err = new Error(`CHANGELOG.md has no "## [${version}]" section`); + err.code = "MISSING"; + throw err; + } + let end = lines.length; + for (let i = start + 1; i < lines.length; i++) { + if (/^#{1,2}\s/.test(lines[i]) || /^\[[^\]]+\]:\s/.test(lines[i])) { + end = i; + break; + } + } + const section = + lines + .slice(start, end) + .join("\n") + .replace(/[ \t]+$/gm, "") + .replace(/\n+$/, "") + "\n"; + const body = section.replace(/^##[^\n]*\n/, "").trim(); + if (!body) { + const err = new Error(`CHANGELOG.md section for ${version} is empty`); + err.code = "EMPTY"; + throw err; + } + return section; +} + +if (require.main === module) { + const version = process.argv[2]; + const path = process.argv[3] || "CHANGELOG.md"; + try { + const section = extractChangelogSection(require("node:fs").readFileSync(path, "utf8"), version); + process.stdout.write(section); + process.stdout.write("\n---\n"); + } catch (err) { + console.error(err.message); + process.exit(1); + } +} + +module.exports = { extractChangelogSection, escapeRegExp }; diff --git a/.github/scripts/changelog-section.test.cjs b/.github/scripts/changelog-section.test.cjs new file mode 100644 index 00000000..a9631efa --- /dev/null +++ b/.github/scripts/changelog-section.test.cjs @@ -0,0 +1,87 @@ +"use strict"; + +const fs = require("node:fs"); +const path = require("node:path"); +const { describe, it } = require("node:test"); +const assert = require("node:assert/strict"); +const { extractChangelogSection } = require("./changelog-section.cjs"); + +const SAMPLE = [ + "# Changelog", + "", + "## [Unreleased]", + "", + "## [0.2.27] - 2026-09-13", + "", + "### Added", + "", + "- Logic analysis ships", + "", + "## [0.2.16] — 2026-08-30", + "", + "### Fixed", + "", + "- Em-dash heading entry", + "", + "## [0.2.20] - 2026-08-01", + "", + "- Twenty", + "", + "## [0.2.2] - 2026-07-01", + "", + "- Two", + "", + "[Unreleased]: https://example/compare/v0.2.27...HEAD", + "# Changelog", + "", + "## 0.2.7 (2026-08-22)", + "", + "- leftover document", + "", +].join("\n"); + +describe("extractChangelogSection", () => { + it("extracts a hyphen-dated section with its body", () => { + const out = extractChangelogSection(SAMPLE, "0.2.27"); + assert.ok(out.startsWith("## [0.2.27] - 2026-09-13")); + assert.ok(out.includes("- Logic analysis ships")); + assert.ok(!out.includes("0.2.16"), "stops before the next section"); + assert.ok(out.endsWith("\n")); + }); + + it("accepts em-dash headings", () => { + const out = extractChangelogSection(SAMPLE, "0.2.16"); + assert.ok(out.includes("- Em-dash heading entry")); + }); + + it("matches the exact version, never a prefix", () => { + const out = extractChangelogSection(SAMPLE, "0.2.2"); + assert.ok(out.includes("- Two")); + assert.ok(!out.includes("Twenty"), "0.2.2 must not steal the 0.2.20 section"); + }); + + it("stops at compare-link footers and repeated top headings", () => { + const out = extractChangelogSection(SAMPLE, "0.2.2"); + assert.ok(!out.includes("[Unreleased]:"), "link footer excluded"); + assert.ok(!out.includes("leftover document"), "second document excluded"); + }); + + it("fails closed when the section is missing", () => { + assert.throws(() => extractChangelogSection(SAMPLE, "0.2.25"), /no "## \[0\.2\.25\]" section/); + }); + + it("fails closed when the section has no body", () => { + assert.throws(() => extractChangelogSection(SAMPLE, "Unreleased"), /is empty/); + }); + + it("fails closed on an empty version", () => { + assert.throws(() => extractChangelogSection(SAMPLE, ""), /version is empty/); + }); + + it("extracts the real 0.2.27 section from this repo's CHANGELOG.md", () => { + const real = fs.readFileSync(path.join(__dirname, "..", "..", "CHANGELOG.md"), "utf8"); + const out = extractChangelogSection(real, "0.2.27"); + assert.ok(out.includes("logic-analysis"), "curated notes reach the release body"); + assert.ok(!out.includes("## [0.2.26]"), "does not bleed into the previous release"); + }); +}); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3dc79e93..0d2f2ef4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -284,7 +284,14 @@ jobs: set -euo pipefail tag="v${VERSION}" if ! gh release view "$tag" >/dev/null 2>&1; then - args=(--title "codexclaw $tag" --target "$GITHUB_SHA" --generate-notes) + # Curated notes first: extract this version's CHANGELOG.md section + # and pass it via --notes-file. GitHub prepends it to the + # --generate-notes PR list (the flags compose; the exclusive pair + # is --notes-from-tag). Fails closed when the section is missing + # or heading-only, so a forgotten CHANGELOG entry blocks publish. + notes="$(mktemp)" + node .github/scripts/changelog-section.cjs "$VERSION" > "$notes" + args=(--title "codexclaw $tag" --target "$GITHUB_SHA" --generate-notes --notes-file "$notes") kind="$(node bin/codexclaw.mjs release classify --version "$VERSION")" [ "$kind" = prerelease ] && args+=(--prerelease) gh release create "$tag" "${args[@]}" diff --git a/package.json b/package.json index 46596990..aa4f2a6d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "scripts": { "build": "node plugins/codexclaw/scripts/build.mjs", "gate": "node plugins/codexclaw/scripts/gate.mjs", - "test": "node plugins/codexclaw/scripts/test.mjs \"plugins/codexclaw/components/pabcd-state/test/*.test.ts\" \"plugins/codexclaw/components/config-guard/test/*.test.ts\" \"plugins/codexclaw/components/cxc-ops/test/*.test.ts\" \"plugins/codexclaw/components/recall/test/*.test.ts\" \"plugins/codexclaw/components/provider-bridge/test/*.test.ts\" \"plugins/codexclaw/components/subagent-config/test/*.test.ts\" \"plugins/codexclaw/components/messenger-bridge/test/*.test.ts\" \"plugins/codexclaw/components/skill-search/test/*.test.ts\" \"plugins/codexclaw/components/bg-wake/test/*.test.ts\" \"plugins/codexclaw/gui/test/*.test.ts\" \"plugins/codexclaw/test/*.test.mjs\" \".github/scripts/pr-labeler.test.cjs\" \".github/scripts/closed-pr-branch-cleanup.test.cjs\"", + "test": "node plugins/codexclaw/scripts/test.mjs \"plugins/codexclaw/components/pabcd-state/test/*.test.ts\" \"plugins/codexclaw/components/config-guard/test/*.test.ts\" \"plugins/codexclaw/components/cxc-ops/test/*.test.ts\" \"plugins/codexclaw/components/recall/test/*.test.ts\" \"plugins/codexclaw/components/provider-bridge/test/*.test.ts\" \"plugins/codexclaw/components/subagent-config/test/*.test.ts\" \"plugins/codexclaw/components/messenger-bridge/test/*.test.ts\" \"plugins/codexclaw/components/skill-search/test/*.test.ts\" \"plugins/codexclaw/components/bg-wake/test/*.test.ts\" \"plugins/codexclaw/gui/test/*.test.ts\" \"plugins/codexclaw/test/*.test.mjs\" \".github/scripts/pr-labeler.test.cjs\" \".github/scripts/closed-pr-branch-cleanup.test.cjs\" \".github/scripts/changelog-section.test.cjs\"", "smoke": "node plugins/codexclaw/scripts/platform-smoke.mjs" }, "overrides": { From e6dc8ea71a3a21286b2dc150de5f1ba2beb0ca26 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Sun, 13 Sep 2026 18:29:26 +0900 Subject: [PATCH 4/4] [agent] chore: publish the measured test total after the changelog-section tests Co-authored-by: Cursor --- README.ko.md | 2 +- README.md | 2 +- README.zh.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.ko.md b/README.ko.md index 7d7de5ff..35736a36 100644 --- a/README.ko.md +++ b/README.ko.md @@ -13,7 +13,7 @@

CI - 3,142 tests passing + 3,150 tests passing 29 skills 28 hooks Documentation diff --git a/README.md b/README.md index 9ac3a395..2dcee674 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@

CI - 3,142 tests passing + 3,150 tests passing 29 skills 28 hooks Documentation diff --git a/README.zh.md b/README.zh.md index b1b759ac..f0a07a0b 100644 --- a/README.zh.md +++ b/README.zh.md @@ -13,7 +13,7 @@

CI - 3,142 tests passing + 3,150 tests passing 29 skills 28 hooks Documentation