From c91c2ceae017c674a3ce04a552ce60f5efb5b184 Mon Sep 17 00:00:00 2001
From: SteveBot <1153461+unbraind@users.noreply.github.com>
Date: Mon, 7 Sep 2026 23:01:32 +0200
Subject: [PATCH 1/2] Clarify import previews and certify CLI 2026.9.7
Pin the published CLI/SDK 2026.9.7 and current changelog tooling, preserve consumer contracts, and record installed-tool validation with measured coverage limitations in the package tracker.
---
.agents/pm/chores/pm-github-5ow3.toon | 20 +++++++++++++++++++
.agents/pm/history/pm-github-5ow3.jsonl | 6 ++++++
.../pm-github-github-ee4d59c27b67-35.jsonl | 8 ++++++++
.../pm-github-github-ee4d59c27b67-35.toon | 19 ++++++++++++++++++
index.ts | 5 +++--
package-lock.json | 16 +++++++--------
package.json | 4 ++--
test/dryrun-preview.test.ts | 11 ++++++----
8 files changed, 73 insertions(+), 16 deletions(-)
create mode 100644 .agents/pm/chores/pm-github-5ow3.toon
create mode 100644 .agents/pm/history/pm-github-5ow3.jsonl
create mode 100644 .agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl
create mode 100644 .agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon
diff --git a/.agents/pm/chores/pm-github-5ow3.toon b/.agents/pm/chores/pm-github-5ow3.toon
new file mode 100644
index 0000000..906ab68
--- /dev/null
+++ b/.agents/pm/chores/pm-github-5ow3.toon
@@ -0,0 +1,20 @@
+id: pm-github-5ow3
+title: Certify the CLI and SDK on PM 2026.9.7
+description: "Upgrade the development CLI and SDK to published 2026.9.7 and pm-changelog to published 2026.9.6. Re-run real package quality and acceptance gates against the exact installed dependencies, preserving the manifest compatibility floor unless runtime behavior requires a higher minimum. Companion tracking: pm-cli-companion item pm-cli-website-6yiv."
+type: Chore
+status: in_progress
+priority: 2
+tags[3]: cli,compatibility,sdk
+created_at: "2026-09-07T20:37:28.620Z"
+updated_at: "2026-09-07T21:01:19.914Z"
+assignee: codex-pm-ecosystem
+claim_principal: codex-pm-ecosystem
+author: codex-pm-ecosystem
+acceptance_criteria: Exact installed dependency versions are verified; package checks and real CLI acceptance pass without weakened gates; independent coverage and privacy limitations remain explicit
+comments[1]{created_at,author,text}:
+ "2026-09-07T21:01:19.914Z",codex-pm-ecosystem,"Final local release:check passed using installed CLI/SDK 2026.9.7 and pm-changelog 2026.9.6. Final top-level test report: tests=299, pass=299, fail=0, skipped=0. ℹ all files | 92.57 | 82.94 | 92.27 | . Coverage is the current reported scope; complete four-dimensional source certification and independent production/privacy/review gates remain open."
+tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}:
+ "npm run release:check",project,1200,codex-pm-ecosystem,"2026-09-07T20:37:31.983Z",local_mutation,chore/certify-pm-cli-2026-9-7
+docs[1]{path,scope,note}:
+ "https://github.com/unbraind/pm-cli/releases/tag/v2026.9.7",project,Published CLI and SDK release
+body: ""
diff --git a/.agents/pm/history/pm-github-5ow3.jsonl b/.agents/pm/history/pm-github-5ow3.jsonl
new file mode 100644
index 0000000..8a460b0
--- /dev/null
+++ b/.agents/pm/history/pm-github-5ow3.jsonl
@@ -0,0 +1,6 @@
+{"ts":"2026-09-07T20:37:28.620Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-github-5ow3"},{"op":"add","path":"/metadata/title","value":"Certify the CLI and SDK on PM 2026.9.7"},{"op":"add","path":"/metadata/description","value":"Upgrade the development CLI and SDK to published 2026.9.7 and pm-changelog to published 2026.9.6. Re-run real package quality and acceptance gates against the exact installed dependencies, preserving the manifest compatibility floor unless runtime behavior requires a higher minimum. Companion tracking: pm-cli-companion item pm-cli-website-6yiv."},{"op":"add","path":"/metadata/type","value":"Chore"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":["cli","compatibility","sdk"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-07T20:37:28.620Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-07T20:37:28.620Z"},{"op":"add","path":"/metadata/author","value":"codex-pm-ecosystem"},{"op":"add","path":"/metadata/acceptance_criteria","value":"Exact installed dependency versions are verified; package checks and real CLI acceptance pass without weakened gates; independent coverage and privacy limitations remain explicit"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"0b147c6809270a45ceb25eda5451df5dfcd74d0b1a291e78267c1cfa29d12ac9","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"4f8d3d33ca44440ccb0d0432dfeb4b9736ec3f1afe45890366162f0a9bc54595"}
+{"ts":"2026-09-07T20:37:29.592Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:37:29.592Z"},{"op":"add","path":"/metadata/assignee","value":"codex-pm-ecosystem"},{"op":"add","path":"/metadata/claim_principal","value":"codex-pm-ecosystem"}],"before_hash":"0b147c6809270a45ceb25eda5451df5dfcd74d0b1a291e78267c1cfa29d12ac9","after_hash":"0013b99d694fb7f2691231ae44f64c6c8418e0c6f7a40032a2887f854aceeabe","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"704ce3149e7413e4db647b00eb2b6488a49075edf78d69fb72b18ac019eebe94"}
+{"ts":"2026-09-07T20:37:30.250Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:37:30.250Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"0013b99d694fb7f2691231ae44f64c6c8418e0c6f7a40032a2887f854aceeabe","after_hash":"378914a4a470e062bacdb86debaa3e7df66d96915efa2738dd09d7aa4934bcb2","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"8b4793d0b72a9a99c94021f2298d56de0498ff746fbdccd97ba15c14c9c32158"}
+{"ts":"2026-09-07T20:37:30.968Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"docs_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:37:30.968Z"},{"op":"add","path":"/metadata/docs","value":[{"path":"https://github.com/unbraind/pm-cli/releases/tag/v2026.9.7","scope":"project","note":"Published CLI and SDK release"}]}],"before_hash":"378914a4a470e062bacdb86debaa3e7df66d96915efa2738dd09d7aa4934bcb2","after_hash":"4bcb51073aae2d77bd3f70808a577b991a26873c970fed4ae99f85014a6f0991","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"a98c0d4cddd045845e82839431734a797d0f12884b225e95e729afa54b775c29"}
+{"ts":"2026-09-07T20:37:32.073Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:37:32.073Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm run release:check","scope":"project","timeout_seconds":1200,"provenance":{"author":"codex-pm-ecosystem","created_at":"2026-09-07T20:37:31.983Z","source_kind":"local_mutation","source_ref":"chore/certify-pm-cli-2026-9-7"}}]}],"before_hash":"4bcb51073aae2d77bd3f70808a577b991a26873c970fed4ae99f85014a6f0991","after_hash":"7a8e5e1e9f5293db0603a495bcb19c27f4783f7899629052d86ed122d8a0e7b1","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"26b7a3f5e7dd6a682f60252e502da7c83022da78556fabf0f9441e5d92fb2632"}
+{"ts":"2026-09-07T21:01:19.914Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:01:19.914Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-07T21:01:19.914Z","author":"codex-pm-ecosystem","text":"Final local release:check passed using installed CLI/SDK 2026.9.7 and pm-changelog 2026.9.6. Final top-level test report: tests=299, pass=299, fail=0, skipped=0. ℹ all files | 92.57 | 82.94 | 92.27 | . Coverage is the current reported scope; complete four-dimensional source certification and independent production/privacy/review gates remain open."}]}],"before_hash":"7a8e5e1e9f5293db0603a495bcb19c27f4783f7899629052d86ed122d8a0e7b1","after_hash":"4e3dcaece81065686457db1b4f396c17f6e5858d74436abb5783c8416d79b510","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"e64de0beb326bcfcb575bab8b2dd95354f5aff39f637f9aaa5dde81e83715195"}
diff --git a/.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl b/.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl
new file mode 100644
index 0000000..82046f5
--- /dev/null
+++ b/.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl
@@ -0,0 +1,8 @@
+{"ts":"2026-09-07T20:43:08.649Z","author":"pm-github","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"create","patch":[{"op":"replace","path":"/body","value":"Found while functionally testing `pm-github` against real data — a `--dry-run` import of `unbraind/pm-cli` (19 open issues).\n\n## What it prints\n\n```\n [dry-run] #982 import pm test --remove reports ok:true and removes nothing when … (open, )\n [dry-run] #860 import pm-beads --preserve-source-ids changes source ID casing (open, bug,reliability)\n```\n\nTwo defects on the same line (`index.ts:2690`):\n\n```ts\nconsole.error(` [dry-run] #${issue.number} ${action} ${title} (${status}, ${labels.join(\",\")})`);\n```\n\n1. **The action verb runs into the title.** `${action} ${title}` with no separator makes `import` read as the first word of the issue title — `#982 import pm test --remove reports…`. For titles that begin with a verb this is genuinely ambiguous, and this is the output an operator reads to decide whether to run the real import.\n2. **A dangling comma when there are no labels.** `labels.join(\",\")` is `\"\"` for an unlabelled issue, so the line ends `(open, )`. Most issues in a real repo are unlabelled, so this is the common case, not the edge case.\n\n## Suggested fix\n\n```ts\nconst meta = labels.length > 0 ? `${status}, ${labels.join(\",\")}` : status;\nconsole.error(` [dry-run] #${issue.number} ${action}: ${title} (${meta})`);\n```\n\nGiving:\n\n```\n [dry-run] #982 import: pm test --remove reports ok:true and removes nothing when … (open)\n [dry-run] #860 import: pm-beads --preserve-source-ids changes source ID casing (open, bug,reliability)\n```\n\nThe atomic variant at `index.ts:2581` has the same verb-adjacency shape (`#${entry.issueNumber} ${action} ${entry.title}`) and should move with it, and `--link-deps` output nearby is worth a glance for the same pattern.\n\n## Context: the rest of the command is sound\n\nEverything else checked out against real data and is worth recording so this issue is not read as a general reliability concern:\n\n- `github validate` resolves the token via the `gh` CLI, reports remaining API quota with its reset time, and confirms repo accessibility with the HTTP status.\n- `github import --dry-run` fetched all 19 open issues correctly, classified each as `import` vs `update` against existing pm items, and wrote nothing.\n\nOnly the rendering of the plan is wrong.\n\n---\n\n### GitHub comments (1)\n\n> **@coderabbitai[bot]** (2026-08-11T19:45:51Z)\n>\n> \n> \n> 🔗 Related PRs
\n> \n> unbraind/pm-github#28 - feat(projects): production-grade bidirectional GitHub Projects v2 sync [merged]\n> unbraind/pm-github#31 - fix(github): route export dry-run preview to stderr so --format json stdout is valid JSON [merged]\n> unbraind/pm-github#5 - Make GitHub issue imports atomic and crash-resumable [merged]\n> unbraind/pm-github#9 - feat(import): --link-deps maps GitHub issue-body dependencies to pm edges [merged]\n> unbraind/pm-github#28 - fix: route closed-issue imports through pm close for pm-cli 2026.8.3 [merged]\n> \n> \n> ---\n> \n> 📝 Issue Planner
\n> \n> Check the box below or use the `@coderabbitai plan` command to generate an implementation plan and prompts that you can use with your favorite coding assistant.\n> \n> - [ ] Create Plan\n> \n> \n> \n> ---\n> \n> 🧪 Issue enrichment is currently in open beta.
\n> \n> \n> You can configure auto-planning by selecting labels in the issue_enrichment configuration.\n> \n> To disable automatic issue enrichment, add the following to your `.coderabbit.yaml`:\n> ```yaml\n> issue_enrichment:\n> auto_enrich:\n> enabled: false\n> ```\n> \n> \n> 💬 Have feedback or questions? Drop into our [discord](https://discord.gg/coderabbit)!"},{"op":"add","path":"/metadata/id","value":"pm-github-github-ee4d59c27b67-35"},{"op":"add","path":"/metadata/title","value":"github import --dry-run runs the action verb into the title and emits a dangling comma when an issue has no labels"},{"op":"add","path":"/metadata/description","value":"GH issue #35: https://github.com/unbraind/pm-github/issues/35 · author @unbraind · created 2026-08-11T19:45:43Z · updated 2026-08-11T19:45:51Z"},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":["gh:unbraind/pm-github#35","github_author:unbraind"]},{"op":"add","path":"/metadata/created_at","value":"2026-09-07T20:43:08.649Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-09-07T20:43:08.649Z"},{"op":"add","path":"/metadata/author","value":"pm-github"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"2f0545cda7f1b16a4873dce4264123780f6230ff06daffd4cf6be7a3060ca46a","item_hash_version":3,"message":"","context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"4e425409e9c74f6f5c7b977d73886774d9b0752ba1ffaad91eea66324bd22b30"}
+{"ts":"2026-09-07T20:43:08.861Z","author":"coderabbitai[bot]","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:43:08.861Z"},{"op":"add","path":"/metadata/comments","value":[{"created_at":"2026-09-07T20:43:08.861Z","author":"coderabbitai[bot]","text":"\n\n🔗 Related PRs
\n\nunbraind/pm-github#28 - feat(projects): production-grade bidirectional GitHub Projects v2 sync [merged]\nunbraind/pm-github#31 - fix(github): route export dry-run preview to stderr so --format json stdout is valid JSON [merged]\nunbraind/pm-github#5 - Make GitHub issue imports atomic and crash-resumable [merged]\nunbraind/pm-github#9 - feat(import): --link-deps maps GitHub issue-body dependencies to pm edges [merged]\nunbraind/pm-github#28 - fix: route closed-issue imports through pm close for pm-cli 2026.8.3 [merged]\n \n\n---\n\n📝 Issue Planner
\n\nCheck the box below or use the `@coderabbitai plan` command to generate an implementation plan and prompts that you can use with your favorite coding assistant.\n\n- [ ] Create Plan\n \n\n\n---\n\n 🧪 Issue enrichment is currently in open beta.
\n\n\nYou can configure auto-planning by selecting labels in the issue_enrichment configuration.\n\nTo disable automatic issue enrichment, add the following to your `.coderabbit.yaml`:\n```yaml\nissue_enrichment:\n auto_enrich:\n enabled: false\n```\n \n\n💬 Have feedback or questions? Drop into our [discord](https://discord.gg/coderabbit)!\n\n"}]}],"before_hash":"2f0545cda7f1b16a4873dce4264123780f6230ff06daffd4cf6be7a3060ca46a","after_hash":"7a7ece2554ad90ffa7ee39b7be119799cb78cfcae538a10f4817e96e599bd3e3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"bb44aa376574409eeecac6c742efbbb8fe5c85ef78e256320abd628e81261697"}
+{"ts":"2026-09-07T20:44:43.843Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:44:43.843Z"},{"op":"add","path":"/metadata/assignee","value":"codex-pm-ecosystem"},{"op":"add","path":"/metadata/claim_principal","value":"codex-pm-ecosystem"}],"before_hash":"7a7ece2554ad90ffa7ee39b7be119799cb78cfcae538a10f4817e96e599bd3e3","after_hash":"7046c99d638cc13afccf261534211214aed3a9e539b1f81cd117eef431f8a3e3","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"cf09a36310662bf15b20d057f6a0a0265db8f0881ffefbe8189cc7de0bf7e3c4"}
+{"ts":"2026-09-07T20:44:44.757Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:44:44.757Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"},{"op":"add","path":"/metadata/acceptance_criteria","value":"Both import modes separate the action from the issue title; unlabeled issues have no dangling comma; preview classification remains correct"}],"before_hash":"7046c99d638cc13afccf261534211214aed3a9e539b1f81cd117eef431f8a3e3","after_hash":"44871a3d8e079d7ad63908b28af421719f898aba0590d30fb42b680c850341d3","item_hash_version":3,"message":"TDD: both strengthened preview tests fail before changing the renderer","context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"63b0a19c85f2518e76ad1032d7b99d0b03b9ed7ae9539353381cb86c1a3a58fa"}
+{"ts":"2026-09-07T20:44:45.522Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T20:44:45.522Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"node --test test/dryrun-preview.test.ts","scope":"project","timeout_seconds":60,"provenance":{"author":"codex-pm-ecosystem","created_at":"2026-09-07T20:44:45.460Z","source_kind":"local_mutation","source_ref":"chore/certify-pm-cli-2026-9-7"}}]}],"before_hash":"44871a3d8e079d7ad63908b28af421719f898aba0590d30fb42b680c850341d3","after_hash":"232870d42d5f210e5baf8510de9b861012980df900d2387bf2b4ec4a9e48641f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"maintenance","record_hash_version":1,"record_hash":"992ccc749c27b7929549c213efe36b80a057b58cec06ed5af3c236d99b77957e"}
+{"ts":"2026-09-07T21:01:21.078Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:01:21.078Z"}],"before_hash":"232870d42d5f210e5baf8510de9b861012980df900d2387bf2b4ec4a9e48641f","after_hash":"f164fc53a7d6615d4c31c56cb9748b85d4d7ab2c3a9816280b108191d325e883","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f0b212ef7d6d305215752d323f89c68bb58c7daedbef3ecd395e347e3c435568"}
+{"ts":"2026-09-07T21:01:22.151Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"Regression fails before the correction and passes afterward; complete package gate passes; exact-head review and CI resolve before close"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:01:22.151Z"}],"before_hash":"f164fc53a7d6615d4c31c56cb9748b85d4d7ab2c3a9816280b108191d325e883","after_hash":"b441429e78dc934ef8ae54c36edc8db5f91fe9ea25c87e77af3043caa777253f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9b982d61646b0df807b8826d18e0ea23db41efeeafc949e61deb3dc05825793a"}
+{"ts":"2026-09-07T21:01:23.266Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-07T21:01:23.266Z","author":"codex-pm-ecosystem","text":"Imported from the real GitHub issue using pm-github runImport with atomic commit and native comment mapping. The existing regression assertions fail before the change and pass after it. Final local release:check passed using installed CLI/SDK 2026.9.7 and pm-changelog 2026.9.6. Final top-level test report: tests=299, pass=299, fail=0, skipped=0. ℹ all files | 92.57 | 82.94 | 92.27 | . Coverage is the current reported scope; complete four-dimensional source certification and independent production/privacy/review gates remain open."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:01:23.266Z"}],"before_hash":"b441429e78dc934ef8ae54c36edc8db5f91fe9ea25c87e77af3043caa777253f","after_hash":"9602589920cb37bae6c53716cd7f68205aea938f98c90cb390e0ccf625c66e78","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"5fc72af1db40ee016c458400aaf93134d0d66baf7f140213f1c91917bc5f22df"}
diff --git a/.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon b/.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon
new file mode 100644
index 0000000..24c96be
--- /dev/null
+++ b/.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon
@@ -0,0 +1,19 @@
+id: pm-github-github-ee4d59c27b67-35
+title: github import --dry-run runs the action verb into the title and emits a dangling comma when an issue has no labels
+description: "GH issue #35: https://github.com/unbraind/pm-github/issues/35 · author @unbraind · created 2026-08-11T19:45:43Z · updated 2026-08-11T19:45:51Z"
+type: Issue
+status: in_progress
+priority: 2
+tags[2]: "gh:unbraind/pm-github#35","github_author:unbraind"
+created_at: "2026-09-07T20:43:08.649Z"
+updated_at: "2026-09-07T21:01:23.266Z"
+assignee: codex-pm-ecosystem
+claim_principal: codex-pm-ecosystem
+author: pm-github
+acceptance_criteria: Regression fails before the correction and passes afterward; complete package gate passes; exact-head review and CI resolve before close
+comments[2]{created_at,author,text}:
+ "2026-09-07T20:43:08.861Z","coderabbitai[bot]","\n\n🔗 Related PRs
\n\nunbraind/pm-github#28 - feat(projects): production-grade bidirectional GitHub Projects v2 sync [merged]\nunbraind/pm-github#31 - fix(github): route export dry-run preview to stderr so --format json stdout is valid JSON [merged]\nunbraind/pm-github#5 - Make GitHub issue imports atomic and crash-resumable [merged]\nunbraind/pm-github#9 - feat(import): --link-deps maps GitHub issue-body dependencies to pm edges [merged]\nunbraind/pm-github#28 - fix: route closed-issue imports through pm close for pm-cli 2026.8.3 [merged]\n \n\n---\n\n📝 Issue Planner
\n\nCheck the box below or use the `@coderabbitai plan` command to generate an implementation plan and prompts that you can use with your favorite coding assistant.\n\n- [ ] Create Plan\n \n\n\n---\n\n 🧪 Issue enrichment is currently in open beta.
\n\n\nYou can configure auto-planning by selecting labels in the issue_enrichment configuration.\n\nTo disable automatic issue enrichment, add the following to your `.coderabbit.yaml`:\n```yaml\nissue_enrichment:\n auto_enrich:\n enabled: false\n```\n \n\n💬 Have feedback or questions? Drop into our [discord](https://discord.gg/coderabbit)!\n\n"
+ "2026-09-07T21:01:23.266Z",codex-pm-ecosystem,"Imported from the real GitHub issue using pm-github runImport with atomic commit and native comment mapping. The existing regression assertions fail before the change and pass after it. Final local release:check passed using installed CLI/SDK 2026.9.7 and pm-changelog 2026.9.6. Final top-level test report: tests=299, pass=299, fail=0, skipped=0. ℹ all files | 92.57 | 82.94 | 92.27 | . Coverage is the current reported scope; complete four-dimensional source certification and independent production/privacy/review gates remain open."
+tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}:
+ node --test test/dryrun-preview.test.ts,project,60,codex-pm-ecosystem,"2026-09-07T20:44:45.460Z",local_mutation,chore/certify-pm-cli-2026-9-7
+body: "Found while functionally testing `pm-github` against real data — a `--dry-run` import of `unbraind/pm-cli` (19 open issues).\n\n## What it prints\n\n```\n [dry-run] #982 import pm test --remove reports ok:true and removes nothing when … (open, )\n [dry-run] #860 import pm-beads --preserve-source-ids changes source ID casing (open, bug,reliability)\n```\n\nTwo defects on the same line (`index.ts:2690`):\n\n```ts\nconsole.error(` [dry-run] #${issue.number} ${action} ${title} (${status}, ${labels.join(\",\")})`);\n```\n\n1. **The action verb runs into the title.** `${action} ${title}` with no separator makes `import` read as the first word of the issue title — `#982 import pm test --remove reports…`. For titles that begin with a verb this is genuinely ambiguous, and this is the output an operator reads to decide whether to run the real import.\n2. **A dangling comma when there are no labels.** `labels.join(\",\")` is `\"\"` for an unlabelled issue, so the line ends `(open, )`. Most issues in a real repo are unlabelled, so this is the common case, not the edge case.\n\n## Suggested fix\n\n```ts\nconst meta = labels.length > 0 ? `${status}, ${labels.join(\",\")}` : status;\nconsole.error(` [dry-run] #${issue.number} ${action}: ${title} (${meta})`);\n```\n\nGiving:\n\n```\n [dry-run] #982 import: pm test --remove reports ok:true and removes nothing when … (open)\n [dry-run] #860 import: pm-beads --preserve-source-ids changes source ID casing (open, bug,reliability)\n```\n\nThe atomic variant at `index.ts:2581` has the same verb-adjacency shape (`#${entry.issueNumber} ${action} ${entry.title}`) and should move with it, and `--link-deps` output nearby is worth a glance for the same pattern.\n\n## Context: the rest of the command is sound\n\nEverything else checked out against real data and is worth recording so this issue is not read as a general reliability concern:\n\n- `github validate` resolves the token via the `gh` CLI, reports remaining API quota with its reset time, and confirms repo accessibility with the HTTP status.\n- `github import --dry-run` fetched all 19 open issues correctly, classified each as `import` vs `update` against existing pm items, and wrote nothing.\n\nOnly the rendering of the plan is wrong.\n\n---\n\n### GitHub comments (1)\n\n> **@coderabbitai[bot]** (2026-08-11T19:45:51Z)\n>\n> \n> \n> 🔗 Related PRs
\n> \n> unbraind/pm-github#28 - feat(projects): production-grade bidirectional GitHub Projects v2 sync [merged]\n> unbraind/pm-github#31 - fix(github): route export dry-run preview to stderr so --format json stdout is valid JSON [merged]\n> unbraind/pm-github#5 - Make GitHub issue imports atomic and crash-resumable [merged]\n> unbraind/pm-github#9 - feat(import): --link-deps maps GitHub issue-body dependencies to pm edges [merged]\n> unbraind/pm-github#28 - fix: route closed-issue imports through pm close for pm-cli 2026.8.3 [merged]\n> \n> \n> ---\n> \n> 📝 Issue Planner
\n> \n> Check the box below or use the `@coderabbitai plan` command to generate an implementation plan and prompts that you can use with your favorite coding assistant.\n> \n> - [ ] Create Plan\n> \n> \n> \n> ---\n> \n> 🧪 Issue enrichment is currently in open beta.
\n> \n> \n> You can configure auto-planning by selecting labels in the issue_enrichment configuration.\n> \n> To disable automatic issue enrichment, add the following to your `.coderabbit.yaml`:\n> ```yaml\n> issue_enrichment:\n> auto_enrich:\n> enabled: false\n> ```\n> \n> \n> 💬 Have feedback or questions? Drop into our [discord](https://discord.gg/coderabbit)!"
diff --git a/index.ts b/index.ts
index 5ef5963..d6eb123 100644
--- a/index.ts
+++ b/index.ts
@@ -2852,7 +2852,7 @@ export async function runImport(
updated = prepared.length - imported;
for (const entry of prepared) {
const action = entry.match?.id ? "update" : "import";
- console.error(` [dry-run][atomic] #${entry.issueNumber} ${action} ${entry.title} (${entry.status})`);
+ console.error(` [dry-run][atomic] #${entry.issueNumber} ${action}: ${entry.title} (${entry.status})`);
}
console.error(
`[dry-run] Atomic plan would import ${imported}, update ${updated}, skip ${skipped}.`,
@@ -2961,7 +2961,8 @@ export async function runImport(
if (opts.dryRun) {
const action = match?.id ? "update" : "import";
- console.error(` [dry-run] #${issue.number} ${action} ${title} (${status}, ${labels.join(",")})`);
+ const metadata = labels.length > 0 ? `${status}, ${labels.join(",")}` : status;
+ console.error(` [dry-run] #${issue.number} ${action}: ${title} (${metadata})`);
if (match?.id) updated++;
else imported++;
continue;
diff --git a/package-lock.json b/package-lock.json
index f9db44b..bc10590 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,8 +10,8 @@
"license": "MIT",
"devDependencies": {
"@types/node": "^26.1.1",
- "@unbrained/pm-cli": "2026.9.5",
- "pm-changelog": "2026.9.2",
+ "@unbrained/pm-cli": "2026.9.7",
+ "pm-changelog": "2026.9.6",
"pm-ops": "^2026.9.7",
"typescript": "^7.0.2"
},
@@ -669,9 +669,9 @@
}
},
"node_modules/@unbrained/pm-cli": {
- "version": "2026.9.5",
- "resolved": "https://registry.npmjs.org/@unbrained/pm-cli/-/pm-cli-2026.9.5.tgz",
- "integrity": "sha512-JrOBWBaKBmUATW921AsLL8qmxo9oOFrJp7lIkvOdND47U6v7ifkiWxosdzkY3payog+NIc9jKMNeXurWuzuZqA==",
+ "version": "2026.9.7",
+ "resolved": "https://registry.npmjs.org/@unbrained/pm-cli/-/pm-cli-2026.9.7.tgz",
+ "integrity": "sha512-EbyrHyYVHqk2qyhRNPlu892npbSR7Sgd2C65rIacWKVdDuIdDLv6GqkTq9O23u4zoGwhgvK1mAYCvlpE8skSvw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -982,9 +982,9 @@
}
},
"node_modules/pm-changelog": {
- "version": "2026.9.2",
- "resolved": "https://registry.npmjs.org/pm-changelog/-/pm-changelog-2026.9.2.tgz",
- "integrity": "sha512-pP8lEl5fCg8OKVok2PDw2440msMSX7XQGOL8hZhc0mJKkXuhYuPKP3rFl1h5OpvszqLAN/SX0b0ApYPoRlo2Kw==",
+ "version": "2026.9.6",
+ "resolved": "https://registry.npmjs.org/pm-changelog/-/pm-changelog-2026.9.6.tgz",
+ "integrity": "sha512-T5jltOiV/mxXZAeXynbrkZwAUZtDcsJWyPj/GzB/x1U3NqiSLWJoKjEiwHfZOFeLd8v1+jpCRZvg8UercEZlvQ==",
"dev": true,
"license": "MIT",
"bin": {
diff --git a/package.json b/package.json
index 26ea24b..1851514 100644
--- a/package.json
+++ b/package.json
@@ -45,8 +45,8 @@
},
"devDependencies": {
"@types/node": "^26.1.1",
- "@unbrained/pm-cli": "2026.9.5",
- "pm-changelog": "2026.9.2",
+ "@unbrained/pm-cli": "2026.9.7",
+ "pm-changelog": "2026.9.6",
"pm-ops": "^2026.9.7",
"typescript": "^7.0.2"
},
diff --git a/test/dryrun-preview.test.ts b/test/dryrun-preview.test.ts
index bb8f227..81c3b5b 100644
--- a/test/dryrun-preview.test.ts
+++ b/test/dryrun-preview.test.ts
@@ -15,7 +15,7 @@ const issue = (number: number, title: string): GhIssue => ({
title,
body: "",
state: "open",
- labels: [],
+ labels: number === 2 ? [{ name: "bug" }, { name: "reliability" }] : [],
assignee: null,
milestone: null,
user: { login: "octocat" },
@@ -60,18 +60,21 @@ test("non-atomic --dry-run previews updates for already-linked issues instead of
`summary line missing; saw: ${messages.join(" | ")}`
);
assert.ok(
- messages.some((message) => /#2 update Already linked/.test(message)),
+ messages.some((message) => /#2 update: Already linked \(open, bug,reliability\)/.test(message)),
"per-issue line should label the already-linked issue as an update"
);
assert.ok(
- messages.some((message) => /#1 import Brand new/.test(message)),
+ messages.some((message) => /#1 import: Brand new \(open\)/.test(message)),
"per-issue line should label the unlinked issue as an import"
);
});
test("atomic and non-atomic --dry-run agree on the import/update split", async () => {
const plain = (await previewImport(false)).result as Record;
- const atomic = (await previewImport(true)).result as Record;
+ const { result: atomicResult, messages } = await previewImport(true);
+ const atomic = atomicResult as Record;
+ assert.ok(messages.includes(" [dry-run][atomic] #1 import: Brand new (open)"));
+ assert.ok(messages.includes(" [dry-run][atomic] #2 update: Already linked (open)"));
assert.strictEqual(plain.wouldImport, atomic.wouldImport);
assert.strictEqual(plain.wouldUpdate, atomic.wouldUpdate);
From a56e0f0f366f1225a245b0923e8e8a0f59a388a7 Mon Sep 17 00:00:00 2001
From: SteveBot <1153461+unbraind@users.noreply.github.com>
Date: Mon, 7 Sep 2026 23:19:16 +0200
Subject: [PATCH 2/2] Pin complete import preview lines in regression
assertions
Greptile: tightened both non-atomic preview assertions to exact complete lines, matching the atomic checks, so trailing malformed metadata or punctuation cannot pass. Both preview regressions pass.
---
.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl | 1 +
.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon | 5 +++--
test/dryrun-preview.test.ts | 4 ++--
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl b/.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl
index 82046f5..383ff1d 100644
--- a/.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl
+++ b/.agents/pm/history/pm-github-github-ee4d59c27b67-35.jsonl
@@ -6,3 +6,4 @@
{"ts":"2026-09-07T21:01:21.078Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"claim","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:01:21.078Z"}],"before_hash":"232870d42d5f210e5baf8510de9b861012980df900d2387bf2b4ec4a9e48641f","after_hash":"f164fc53a7d6615d4c31c56cb9748b85d4d7ab2c3a9816280b108191d325e883","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"f0b212ef7d6d305215752d323f89c68bb58c7daedbef3ecd395e347e3c435568"}
{"ts":"2026-09-07T21:01:22.151Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":{"value":"implementer","source":"argv"},"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/acceptance_criteria","value":"Regression fails before the correction and passes afterward; complete package gate passes; exact-head review and CI resolve before close"},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:01:22.151Z"}],"before_hash":"f164fc53a7d6615d4c31c56cb9748b85d4d7ab2c3a9816280b108191d325e883","after_hash":"b441429e78dc934ef8ae54c36edc8db5f91fe9ea25c87e77af3043caa777253f","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"9b982d61646b0df807b8826d18e0ea23db41efeeafc949e61deb3dc05825793a"}
{"ts":"2026-09-07T21:01:23.266Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/1","value":{"created_at":"2026-09-07T21:01:23.266Z","author":"codex-pm-ecosystem","text":"Imported from the real GitHub issue using pm-github runImport with atomic commit and native comment mapping. The existing regression assertions fail before the change and pass after it. Final local release:check passed using installed CLI/SDK 2026.9.7 and pm-changelog 2026.9.6. Final top-level test report: tests=299, pass=299, fail=0, skipped=0. ℹ all files | 92.57 | 82.94 | 92.27 | . Coverage is the current reported scope; complete four-dimensional source certification and independent production/privacy/review gates remain open."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:01:23.266Z"}],"before_hash":"b441429e78dc934ef8ae54c36edc8db5f91fe9ea25c87e77af3043caa777253f","after_hash":"9602589920cb37bae6c53716cd7f68205aea938f98c90cb390e0ccf625c66e78","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"5fc72af1db40ee016c458400aaf93134d0d66baf7f140213f1c91917bc5f22df"}
+{"ts":"2026-09-07T21:19:10.448Z","author":"codex-pm-ecosystem","author_source":"asserted","agent_harness":"codex","agent_model":"gpt-6-astra","agent_model_source":"probe","agent_instance":"9934d297b45b4f81009bf30e","agent_provenance":{"model":{"value":"gpt-6-astra","source":"probe"},"effort":{"value":"xhigh","source":"probe"},"role":null,"topic":null},"op":"comment_add","patch":[{"op":"add","path":"/metadata/comments/2","value":{"created_at":"2026-09-07T21:19:10.448Z","author":"codex-pm-ecosystem","text":"2026-09-07 UTC review correction: Greptile: tightened both non-atomic preview assertions to exact complete lines, matching the atomic checks, so trailing malformed metadata or punctuation cannot pass. Both preview regressions pass."}},{"op":"replace","path":"/metadata/updated_at","value":"2026-09-07T21:19:10.448Z"}],"before_hash":"9602589920cb37bae6c53716cd7f68205aea938f98c90cb390e0ccf625c66e78","after_hash":"227cdcce2e9dd3b57d701e034967c21ed9bc31b321c22eed651ec5cf1b633184","item_hash_version":3,"context":{"agent_provenance_outcomes":{"version":{"status":"unavailable","reason":"harness_unavailable","resolver":"ai_agent_version","rule_version":"v1"}}},"event_class":"substantive","record_hash_version":1,"record_hash":"7dccc3d54aff13be33cf067dd4fd4611775221405babf997364c7fb2401bdf3a"}
diff --git a/.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon b/.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon
index 24c96be..72729ec 100644
--- a/.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon
+++ b/.agents/pm/issues/pm-github-github-ee4d59c27b67-35.toon
@@ -6,14 +6,15 @@ status: in_progress
priority: 2
tags[2]: "gh:unbraind/pm-github#35","github_author:unbraind"
created_at: "2026-09-07T20:43:08.649Z"
-updated_at: "2026-09-07T21:01:23.266Z"
+updated_at: "2026-09-07T21:19:10.448Z"
assignee: codex-pm-ecosystem
claim_principal: codex-pm-ecosystem
author: pm-github
acceptance_criteria: Regression fails before the correction and passes afterward; complete package gate passes; exact-head review and CI resolve before close
-comments[2]{created_at,author,text}:
+comments[3]{created_at,author,text}:
"2026-09-07T20:43:08.861Z","coderabbitai[bot]","\n\n🔗 Related PRs
\n\nunbraind/pm-github#28 - feat(projects): production-grade bidirectional GitHub Projects v2 sync [merged]\nunbraind/pm-github#31 - fix(github): route export dry-run preview to stderr so --format json stdout is valid JSON [merged]\nunbraind/pm-github#5 - Make GitHub issue imports atomic and crash-resumable [merged]\nunbraind/pm-github#9 - feat(import): --link-deps maps GitHub issue-body dependencies to pm edges [merged]\nunbraind/pm-github#28 - fix: route closed-issue imports through pm close for pm-cli 2026.8.3 [merged]\n \n\n---\n\n📝 Issue Planner
\n\nCheck the box below or use the `@coderabbitai plan` command to generate an implementation plan and prompts that you can use with your favorite coding assistant.\n\n- [ ] Create Plan\n \n\n\n---\n\n 🧪 Issue enrichment is currently in open beta.
\n\n\nYou can configure auto-planning by selecting labels in the issue_enrichment configuration.\n\nTo disable automatic issue enrichment, add the following to your `.coderabbit.yaml`:\n```yaml\nissue_enrichment:\n auto_enrich:\n enabled: false\n```\n \n\n💬 Have feedback or questions? Drop into our [discord](https://discord.gg/coderabbit)!\n\n"
"2026-09-07T21:01:23.266Z",codex-pm-ecosystem,"Imported from the real GitHub issue using pm-github runImport with atomic commit and native comment mapping. The existing regression assertions fail before the change and pass after it. Final local release:check passed using installed CLI/SDK 2026.9.7 and pm-changelog 2026.9.6. Final top-level test report: tests=299, pass=299, fail=0, skipped=0. ℹ all files | 92.57 | 82.94 | 92.27 | . Coverage is the current reported scope; complete four-dimensional source certification and independent production/privacy/review gates remain open."
+ "2026-09-07T21:19:10.448Z",codex-pm-ecosystem,"2026-09-07 UTC review correction: Greptile: tightened both non-atomic preview assertions to exact complete lines, matching the atomic checks, so trailing malformed metadata or punctuation cannot pass. Both preview regressions pass."
tests[1]{command,scope,timeout_seconds,provenance{author,created_at,source_kind,source_ref}}:
node --test test/dryrun-preview.test.ts,project,60,codex-pm-ecosystem,"2026-09-07T20:44:45.460Z",local_mutation,chore/certify-pm-cli-2026-9-7
body: "Found while functionally testing `pm-github` against real data — a `--dry-run` import of `unbraind/pm-cli` (19 open issues).\n\n## What it prints\n\n```\n [dry-run] #982 import pm test --remove reports ok:true and removes nothing when … (open, )\n [dry-run] #860 import pm-beads --preserve-source-ids changes source ID casing (open, bug,reliability)\n```\n\nTwo defects on the same line (`index.ts:2690`):\n\n```ts\nconsole.error(` [dry-run] #${issue.number} ${action} ${title} (${status}, ${labels.join(\",\")})`);\n```\n\n1. **The action verb runs into the title.** `${action} ${title}` with no separator makes `import` read as the first word of the issue title — `#982 import pm test --remove reports…`. For titles that begin with a verb this is genuinely ambiguous, and this is the output an operator reads to decide whether to run the real import.\n2. **A dangling comma when there are no labels.** `labels.join(\",\")` is `\"\"` for an unlabelled issue, so the line ends `(open, )`. Most issues in a real repo are unlabelled, so this is the common case, not the edge case.\n\n## Suggested fix\n\n```ts\nconst meta = labels.length > 0 ? `${status}, ${labels.join(\",\")}` : status;\nconsole.error(` [dry-run] #${issue.number} ${action}: ${title} (${meta})`);\n```\n\nGiving:\n\n```\n [dry-run] #982 import: pm test --remove reports ok:true and removes nothing when … (open)\n [dry-run] #860 import: pm-beads --preserve-source-ids changes source ID casing (open, bug,reliability)\n```\n\nThe atomic variant at `index.ts:2581` has the same verb-adjacency shape (`#${entry.issueNumber} ${action} ${entry.title}`) and should move with it, and `--link-deps` output nearby is worth a glance for the same pattern.\n\n## Context: the rest of the command is sound\n\nEverything else checked out against real data and is worth recording so this issue is not read as a general reliability concern:\n\n- `github validate` resolves the token via the `gh` CLI, reports remaining API quota with its reset time, and confirms repo accessibility with the HTTP status.\n- `github import --dry-run` fetched all 19 open issues correctly, classified each as `import` vs `update` against existing pm items, and wrote nothing.\n\nOnly the rendering of the plan is wrong.\n\n---\n\n### GitHub comments (1)\n\n> **@coderabbitai[bot]** (2026-08-11T19:45:51Z)\n>\n> \n> \n> 🔗 Related PRs
\n> \n> unbraind/pm-github#28 - feat(projects): production-grade bidirectional GitHub Projects v2 sync [merged]\n> unbraind/pm-github#31 - fix(github): route export dry-run preview to stderr so --format json stdout is valid JSON [merged]\n> unbraind/pm-github#5 - Make GitHub issue imports atomic and crash-resumable [merged]\n> unbraind/pm-github#9 - feat(import): --link-deps maps GitHub issue-body dependencies to pm edges [merged]\n> unbraind/pm-github#28 - fix: route closed-issue imports through pm close for pm-cli 2026.8.3 [merged]\n> \n> \n> ---\n> \n> 📝 Issue Planner
\n> \n> Check the box below or use the `@coderabbitai plan` command to generate an implementation plan and prompts that you can use with your favorite coding assistant.\n> \n> - [ ] Create Plan\n> \n> \n> \n> ---\n> \n> 🧪 Issue enrichment is currently in open beta.
\n> \n> \n> You can configure auto-planning by selecting labels in the issue_enrichment configuration.\n> \n> To disable automatic issue enrichment, add the following to your `.coderabbit.yaml`:\n> ```yaml\n> issue_enrichment:\n> auto_enrich:\n> enabled: false\n> ```\n> \n> \n> 💬 Have feedback or questions? Drop into our [discord](https://discord.gg/coderabbit)!"
diff --git a/test/dryrun-preview.test.ts b/test/dryrun-preview.test.ts
index 81c3b5b..3175604 100644
--- a/test/dryrun-preview.test.ts
+++ b/test/dryrun-preview.test.ts
@@ -60,11 +60,11 @@ test("non-atomic --dry-run previews updates for already-linked issues instead of
`summary line missing; saw: ${messages.join(" | ")}`
);
assert.ok(
- messages.some((message) => /#2 update: Already linked \(open, bug,reliability\)/.test(message)),
+ messages.includes(" [dry-run] #2 update: Already linked (open, bug,reliability)"),
"per-issue line should label the already-linked issue as an update"
);
assert.ok(
- messages.some((message) => /#1 import: Brand new \(open\)/.test(message)),
+ messages.includes(" [dry-run] #1 import: Brand new (open)"),
"per-issue line should label the unlinked issue as an import"
);
});