Repository navigation
chore(release): public launch hardening for @unbrained/pm-cli - #1
Conversation
Switch the package to scoped publishing with calendar-based SemVer-compatible versioning and add release guardrails for secret scanning, npx smoke checks, and tag/version enforcement. This hardens CI/CD and contributor release docs so today’s public npm release is reproducible and safe.
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Free Run ID: 📒 Files selected for processing (24)
Summary by CodeRabbitRelease Notes
WalkthroughThis pull request prepares version 2026.3.9 for public release by rebranding the package from pm-cli to Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Git as Git / Tag
participant GH as GitHub Actions
participant NPM as npm Registry
participant GHR as GitHub Releases
Dev->>Dev: pnpm version:check
activate Dev
Note over Dev: Validate tag & version policy
deactivate Dev
Dev->>Dev: pnpm security:scan
activate Dev
Note over Dev: Scan for credential leaks
deactivate Dev
Dev->>Dev: pnpm smoke:npx
activate Dev
Note over Dev: Test npx tarball locally
deactivate Dev
Dev->>Git: Commit & tag (v2026.3.9)
Git->>GH: Trigger release workflow
GH->>GH: Verify version tag & policy
GH->>GH: Run security scan
GH->>GH: Test npx smoke
GH->>NPM: npm publish `@unbrained/pm-cli`
NPM-->>GH: Success
GH->>GHR: Create GitHub release w/ notes
GHR-->>GH: Release published
GH-->>Dev: Workflow complete
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly hardens the release process for the Highlights
Changelog
Ignored Files
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request implements significant release hardening and prepares the project for a public production launch. Key changes include switching the npm package name from pm-cli to @unbrained/pm-cli and updating the versioning scheme to a calendar-based SemVer (YYYY.M.D or YYYY.M.D-N). New scripts were added to enforce this version policy (release-version.mjs), scan for credential leaks (check-secrets.mjs), and perform npx smoke tests (smoke-npx-from-pack.mjs). The CI/CD workflows, particularly the release pipeline, have been updated to integrate these new checks, enforce the version policy, use a dedicated GitHub release environment, and automate GitHub Release creation. Documentation, including CHANGELOG.md, README.md, CONTRIBUTING.md, AGENTS.md, and PRD.md, has been updated to reflect the new package name, versioning policy, and release procedures. Installer scripts (install.sh, install.ps1) now default to the scoped package. Additionally, a new chore (pm-1h88) was created to track the final public release execution, and a previous chore (pm-1hm2) detailing the release hardening tasks was marked as closed, indicating the completion of these preparatory steps.
… validate guards, idempotent materialize no-op Followups to first-pass review on PR #78. CodeRabbit's 7 ".agents/pm files" comments were a false-positive (pm IS the system of record AND the files ARE tracked in git — that's the entire pm-cli design). Codex P2 #1: src/cli/commands/extension/scaffold.ts now emits the typed `defineExtension({...})` shape ONLY in package mode (peerDependencies on @unbrained/pm-cli make the import resolvable from the package's own node_modules). In extension-only mode there is no package.json with that dep, so the dynamic loader's `import(<file-url>)` would have raised ERR_MODULE_NOT_FOUND when the extension was actually loaded. The import-free shape from before is restored for that mode, decorated with a JSDoc @PARAM hint so editors that follow JSDoc still narrow the api parameter (matches the SDK type contract without a runtime dep). Codex P2 #2: src/sdk/cli-contracts.ts CLOSE_FLAG_CONTRACTS now lists `--resolution`, `--expected-result` (alias `--expected_result`), and `--actual-result` (alias `--actual_result`). `pm contracts --command close --json` now surfaces them; bootstrap flag normalization and the JSON help payload pick them up automatically. Gemini high #1+#2: src/cli/commands/validate.ts buildMetadataCheck adds optional-chain guards so a future settings shape with an unsupported field in `required_fields` falls back to 0 instead of throwing TypeError, and skips writing when the count-key / item-ids-key / truncated-key mapping is undefined. Gemini medium (idempotency): `pm plan materialize --steps all` now returns a successful no-op (exit 0) with `materialize_skipped` populated when every selected step is already completed or already materialized. Previously this threw NOT_FOUND, breaking agent/CI pipelines that re-invoke the same script. Explicit step-ref path keeps processing pending refs in the same call and recording skipped reasons. Test updated to assert the new exit-0 + materialize_skipped contract. Gemini medium (style): dropped the redundant `trimmed.length === 0` short-circuit in `normalizeDependencyKindInput` (update.ts) — the alias lookup with the trimmed key already returns the canonical value (or the trimmed input) safely. 1866 tests, 100% coverage (stmt/branch/func/line), typecheck, quality:static, quality:docs-skills, security:scan, changelog:check all green. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…gv, schema
Gemini (case-insensitivity):
- mergeAdditiveTags / applyTagRemovals now normalize the existing baseTags to
canonical lowercase before merging/removing, so mixed-case legacy or
hand-edited tags ("Beta") dedupe against additions ("beta") and are removed
by lowercase selectors. Output stays sorted + deduped + lowercased.
- pm update skips a no-op tag write: an additive/subtractive-only mutation that
resolves to the existing set no longer pushes "tags" to changed_fields or
churns history/updated_at. Comparison uses the RAW original so a canonical
case normalization still counts as a real change (consistent with
update-many's buildTagMutationPlan).
Codex P2 #1 (JSON-array argv corruption):
- Dropped list:true from the --add-tags/--remove-tags flag contracts. These use
Commander's repeatable collector, so the bootstrap list-coalescer must not
comma-join repeated occurrences — it was turning
`--add-tags '["a","b"]' --add-tags c` into `["a","b"],c` and breaking the
documented JSON-array form. Each occurrence is now parsed independently.
Codex P2 #2 (published tool schema):
- Added addTags/removeTags to TOOL_CREATE/UPDATE_OPTION_CONTRACTS and
PM_TOOL_PARAMETER_PROPERTIES (+ descriptions) so the additionalProperties:false
MCP/SDK tool schema accepts them instead of rejecting schema-validated clients.
(--expected/--actual remain CLI-only sugar; MCP clients use expectedResult.)
New regression tests for case-insensitive merge/removal and the no-op skip.
1973 tests, 100% coverage, all gates green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…config-driven status enum, calendar timezones (#87) * perf+config+calendar: instant mutations, per-command code-splitting, config-driven status enum, calendar timezones Dogfood audit session targeting the #1 user complaint (CLI feels slow), validated against live telemetry (mutations had the worst real-world p95: update 15.3s, create 5.4s). Performance — mutations now feel instant (pm-3ju0, dev repo 893 items): - create 2581ms -> 239ms, update 1693ms -> 265ms, close -> 374ms. - Non-blocking background semantic refresh: mutations invalidate the keyword cache synchronously (search stays correct) and dispatch the slow Ollama embed + vector-store rewrite to a detached worker (src/core/search/background-refresh.ts + src/cli/search-refresh.ts), mirroring the telemetry-flush spawn pattern. The worker drains a pending-refresh queue under the existing reindex lock (corruption-safe; reindex now imports the shared REINDEX_LOCK_ID). shouldRunSearchRefreshInForeground() keeps tests/CI inline (deterministic). - Fixed a second dominant cost found via CPU profile: the pre-mutation item-format sync (run on every mutation by default) re-parsed/re-serialized the ENTIRE corpus; now skips items already in the target format with no legacy alternate. Performance — startup / read commands (pm-t57d, pm-3mal): - Per-command code-splitting: the 4 register-* families lazily imported the whole commands/index.js barrel inside every handler, so esbuild emitted one 943KB command chunk and every command loaded all command code + deps (incl. fast-glob's ~15-package tree, used only by linked-artifacts). Changed all 44 handler call sites to import the specific command module; a command now loads only its own chunk. Verified: fast-glob 0 resolutions during `pm list`; monolith gone. Dev repo (best of 7): search 673->292ms, show 370->204ms. - MCP tool-parameter schemas (PM_TOOL_PARAMETERS_SCHEMA / provider variant) are now a memoized lazy Proxy — built only on first access (contracts/MCP/SDK), never during CLI commands. Verified the builder fires 0x for `pm list`, 1x for `pm contracts`. Config-driven schema (pm-jtdc): - Removed the hardcoded enum on the MCP `status` tool parameter; it now matches `type`/`filterStatus` (open string) so project-defined custom statuses are accepted via MCP. Runtime validation already rejects unknown statuses with a helpful allowed-list hint (never-block preserved). Verified a custom `in_review` status round-trips end to end. Description now points agents at pm_contracts. Calendar timezone + all-day (pm-0l88): - Added toLocalDayKey() via Intl (no new dependency): timed events bucket by their local day in event.timezone (a 23:30Z Asia/Tokyo instant now lands on the next local day) and render the local wall-clock; all-day events are timezone-agnostic (literal date, never shifted). UTC events unchanged. Window filtering still uses the absolute instant. Health (pm-yf31): - Removed the legacy unused `index` required subdir from PM_CORE_REQUIRED_SUBDIRS so `pm health` reports ok:true on a healthy repo (it was the sole false warning). gc still cleans legacy index/ for old repos. Tests/governance: +14 tests (background-refresh worker/queue/gating merged into search-cache.spec to respect the 120-file cap; calendar tz + all-day cases). Updated the static-quality orphan allowlist for the new spawn-only entry, the coverage include-alignment list, and the gc/integration/constants tests for the removed `index` dir. Full suite green (2029 tests), 100% coverage, all release gates pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * review: address AI-bot feedback on background refresh + add calendar tz tests Addresses CodeRabbit/Sourcery/Gemini/Codex review on PR #87. src/core/search/background-refresh.ts: - Attach a no-op 'error' handler to the detached refresh child so an async spawn error (EAGAIN/ENOENT/resource limits) can never surface as an unhandled error that crashes the parent CLI (Gemini). - enqueuePendingRefreshIds no longer silently drops a mutation's refresh work when the queue gate stays contended past its retry budget: it falls back to a best-effort unguarded merge (writeFileAtomic keeps the file valid) instead of returning without enqueuing (Sourcery bug_risk). - The worker now retries the reindex lock with bounded backoff (8 x 400ms) instead of exiting on the first conflict, so a sibling refresh holding the lock briefly no longer strands queued ids until the next mutation (Codex P2). tests/unit/calendar-command.spec.ts: - Add coverage for invalid/explicit-UTC timezone fallback (graceful UTC day+clock) and for timezone-aware event end-times that stay on the local day (Sourcery). Note: CodeRabbit's "do not edit .agents/pm/**" comments are false positives — pm is the git-tracked system of record and the item changes were produced via pm_close/ pm_create workflows, not hand-edited. Codex's "events excluded from local-day views" is also not a defect: views filter by the absolute instant (calendar.ts:710-713), so tz bucketing only changes the day-header grouping, never inclusion. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ierarchy cycle detection (GH-281, GH-280) (#282) * chore(pm): finalize pm-6bz1 merge record for PR #279 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019JDymqzh8VsuJzFqtit5PD * fix(search,validate): guarantee exact-ID search rank across all modes + detect parent-hierarchy cycles Two agent-trust correctness fixes for pm's core read/validate primitives, surfaced from dogfooding the large 2d-bots tracker (pm-rkie). GH-281 (pm-oqgf): exact-ID search was only guaranteed #1 in keyword mode. combineHybridHits() normalizes keyword scores to [0,1] and blends them under hybrid_semantic_weight, so an exact full/short-ID match's keyword boost (EXACT_ID_MATCH_SCORE=1000 / SHORT_ID_MATCH_SCORE=900) was capped at the keyword weight and could be out-ranked by a high-semantic body mention; in pure semantic mode an exact-ID item with no vector hit vanished entirely. Added an exact_id_match flag on SearchHit and forceExactIdHitsToTop(), applied at both the semantic and hybrid return points (after rerank), which reattaches exact-ID hits in a reserved band above every blended hit (full-ID above short-ID) and exempts them from the score threshold, so the exact target is always #1 and is never dropped — in every mode. GH-280 (pm-8vul): pm validate ran Tarjan SCC only over the dependency graph (blocked_by/definition_of_ready) and never traversed the parent composition graph, so a parent cycle (A<->B, or any-length ring) reported dependency_cycle_count:0 with no warning while silently breaking pm list --tree, orphan detection, and rollups. Added buildLifecycleParentGraph (child->parent over ALL items) + detectLifecycleParentCycles reusing the generic findLifecycleDependencyCycleComponents helper. Folds parent_cycle_* diagnostics into the lifecycle check, emits validate_hierarchy_parent_cycle[_error] warnings, adds --parent-cycle-severity off|warn|error (default warn, mirrors dependency-cycle policy) with full contract/help/completion wiring, and a remediation registry entry (pm update <id> --parent <other-id>). Coverage 100/100/100/100 (4536 tests); quality:static + contracts:check green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019JDymqzh8VsuJzFqtit5PD * fix(search,validate): address Gemini review — case-insensitive parent resolution + Math.max stack safety - validate.ts (HIGH): buildLifecycleParentGraph resolves parent refs via a lowercase->canonical id map so a casing mismatch (PR #279 made parent matching case-insensitive) can never silently drop a cycle edge and hide a parent cycle. Added a regression assertion (uppercase PM-PA resolves to pm-pa). - search.ts (MEDIUM): forceExactIdHitsToTop uses reduce instead of Math.max(0, ...spread) to avoid a RangeError on a very large pre-truncation candidate set, and promoted hits spread the original keyword hit so matched_all_terms / exact_id_match are preserved. Coverage 100/100/100/100 (4536 tests); static + contracts green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019JDymqzh8VsuJzFqtit5PD * refactor(search): drop redundant defensive copy before sorting exact-ID hits Gemini review: exactIdHits is already a fresh array returned by keywordHits.filter(...), so the [...exactIdHits] spread before .sort() is an unnecessary allocation — sort in place. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019JDymqzh8VsuJzFqtit5PD --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
PM context: pm-1hm2. Historical linkage verified during the 2026-08-04 ecosystem review. This comment records canonical context only; it does not reopen or change this artifact. |
Summary
@unbrained/pm-cliwith calendar SemVer-compatible versioning (YYYY.M.D[-N])version:check,security:scan, packagednpxsmoke) plus release workflow tag/sequence enforcement and GitHub release automationTest plan
node scripts/release-version.mjs check --tag v2026.3.9 --verify-nextpnpm security:scanpnpm buildpnpm typecheckpnpm testnode scripts/run-tests.mjs coveragepnpm smoke:npx