Skip to content

Refuse incomplete pm corpora before GitHub sync - #46

Merged
unbraind merged 4 commits into
mainfrom
require-complete-pm-corpus
Aug 17, 2026
Merged

Refuse incomplete pm corpora before GitHub sync#46
unbraind merged 4 commits into
mainfrom
require-complete-pm-corpus

Conversation

@unbraind

@unbraind unbraind commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • remove the arbitrary 10,000-row ceiling from pm-github's whole-workspace reader
  • request strict, full, unbounded pm output and validate every independent completeness, omission, pagination, budget, count, identity, and consumed-row contract
  • add adversarial decoder coverage plus a fresh-tracker installed-CLI acceptance containing both closed and open work
  • document fail-closed behavior and the 64 MiB transport guard
  • pin the development CLI/SDK toolchain to 2026.8.17

Why

Import idempotency, export, state sync, Projects v2 sync, and search fallback all rely on seeing every item. The previous reader accepted arrays and loosely shaped envelopes, defaulted missing rows to an empty array, ignored truthfulness receipts, and silently capped the corpus at 10,000 rows. A partial read could therefore recreate a closed imported issue or omit remote updates.

Project-management evidence

Verification

  • npm ci --ignore-scripts: 73 packages, 0 vulnerabilities
  • npm run release:check: pass (typecheck, build, 85/85 docstrings, behavioral coverage gate, production audit, package dry-run, changelog check)
  • focused compatibility + complete-corpus suite: 9/9
  • real fresh tracker: one closed GitHub-linked issue plus one open task returned intact
  • host latest CLI acceptance: 2026.8.17
  • bun install --no-save: pass
  • pm validate: storage/history integrity pass; pre-existing metadata/file warnings remain
  • pm health --strict-exit: pass with three pre-existing legacy provenance advisories
  • current tracked and all-object high-confidence credential scan: 0 findings

Independent remaining gates

This PR does not claim the separate exact-coverage mandate: current measured production coverage is 91.83% lines, 82.14% branches, and 91.75% functions, and operational scripts remain outside measurement. pm-github-9cjx owns that work.

The current tree contains no absolute host paths, but historical Git objects contain pre-existing path metadata and one machine-local commit identity. pm-github-zqad records the required forward gate and the maintainer decision needed before any destructive rewrite of already-published history/tags.

Summary by Sourcery

Enforce strict, complete whole-workspace pm reads before performing any GitHub import, export, or sync, failing closed on partial or unverifiable corpora.

Bug Fixes:

  • Ensure pm item reads use a strict, unbounded pm list-all contract and reject truncated, paginated, compacted, or otherwise incomplete outputs instead of treating them as partial workspaces.
  • Fix Windows invocation of the npm-installed pm CLI by enabling shell execution only on that platform while preserving direct execution elsewhere.

Enhancements:

  • Add comprehensive validation of pm list-all truthfulness envelopes, including completeness, omission, budget, counts, identities, and consumed fields, to guard idempotency and sync flows.
  • Document whole-workspace read safety guarantees and the PM_JSON_MAX_BUFFER transport cap in the README.
  • Introduce an installed-CLI acceptance test and adversarial decoder tests to verify complete open-and-closed corpus handling and robustness against malformed responses.

Build:

  • Pin the development pm CLI/SDK toolchain to @unbrained/pm-cli 2026.8.17.

Documentation:

  • Document the strict, fail-closed whole-workspace read behavior and guidance for raising the JSON buffer cap for large trackers.

Tests:

  • Add a complete-corpus test suite covering argv construction, cross-platform spawn options, strict decoder contracts, and real installed-CLI behavior on a fresh tracker.

Chores:

  • Record pm-github project-management history and tasks for completeness and coverage mandates in new .agents/pm files.

Summary by cubic

Refuses incomplete pm item corpora before any GitHub import, export, or sync, and hardens Windows execution to preserve argument boundaries. Previously we accepted loose arrays/envelopes and capped reads at 10,000 rows; now we require a strict, full, unbounded pm list-all contract and fail closed on any unverifiable output.

  • Validates pagination, completeness, omission, projection, budget, counts vs totals, unique identities, and consumed fields, then decodes rows; incomplete or contradictory receipts throw. Retains the 64 MiB transport guard.
  • Windows: relaunches the CLI via process.execPath using host PM_CLI_PACKAGE_ROOT to locate @unbrained/pm-cli’s declared bin.pm, verifies it resides inside the host package root, and never uses a shell; acceptance covers metacharacter paths and packed installs.
  • Adds completePmListArgs and decodeCompletePmItems and routes readPmItems through them; documents behavior in README; pins the development toolchain to @unbrained/pm-cli 2026.8.17; expands adversarial/installed-CLI tests.

Required actions

  • Ensure the host pm CLI is >= 2026.8.3.
  • For very large trackers, set PM_JSON_MAX_BUFFER to a larger value.
  • On Windows, ensure the host sets PM_CLI_PACKAGE_ROOT for the installed @unbrained/pm-cli; reads will fail if it is missing.

Written for commit 45759bd. Summary will update on new commits.

Review in cubic

Replace the 10,000-row ceiling and permissive cast with a strict full unbounded list-all contract. Validate source, omission, pagination, budget, count, identity, and consumed row fields before import, export, sync, project, or search planning.

Add adversarial and fresh-tracker installed-CLI acceptance, document the safety behavior, pin the development CLI to 2026.8.17, regenerate the changelog, and record separate exact-coverage and historical-privacy follow-ups in the package tracker.
@sourcery-ai

sourcery-ai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Reviewer's Guide

Replaces the permissive pm workspace reader with a strict, fully-validated pm list-all pipeline, adds adversarial and acceptance tests plus Windows spawn fixes, documents fail-closed behavior and buffer limits, and pins the dev pm CLI toolchain version.

Sequence diagram for strict complete pm list-all workspace reads

sequenceDiagram
  actor GitHubSync
  participant pmGithub as pm-github
  participant pmCli as pm CLI process

  GitHubSync->>pmGithub: readPmItems(pmRoot)
  pmGithub->>pmGithub: pmJsonMaxBuffer()
  pmGithub->>pmGithub: completePmListArgs(pmRoot)
  pmGithub->>pmGithub: pmListSpawnOptions(maxBuffer)
  pmGithub->>pmCli: spawnSync("pm", args, options)
  pmCli-->>pmGithub: stdout (pm list-all JSON envelope)
  pmGithub->>pmGithub: JSON.parse(stdout)
  pmGithub->>pmGithub: decodeCompletePmItems(parsed)
  pmGithub-->>GitHubSync: PmItem[]

  alt [incomplete or unverifiable corpus]
    pmGithub->>pmGithub: requireCompletePmField(...)
    pmGithub-->>GitHubSync: CommandError("Refusing unverifiable pm list-all output")
  end
Loading

File-Level Changes

Change Details Files
Harden pm workspace reading by enforcing a strict, complete pm list-all contract and structured decoding.
  • Introduce JSON helpers and requireCompletePmField to validate specific envelope fields in pm CLI responses.
  • Add decodeCompletePmItems to verify completeness, omission, pagination, projection, budget, counts, and item identities, rejecting any partial or malformed corpus.
  • Refactor readPmItems to call completePmListArgs, use guarded spawn options, parse JSON once, and route results through the strict decoder instead of loosely accepting arrays or fallback fields.
index.ts
Standardize the CLI invocation and process options for whole-workspace reads, including Windows npm shim handling.
  • Add completePmListArgs(pmRoot) to build a canonical, strict, full, unbounded pm list-all argument vector with no arbitrary --limit.
  • Add pmListSpawnOptions(maxBuffer, platform) to configure UTF-8 encoding, buffer size, and conditional shell: true on Windows so npm pm shims execute correctly.
  • Keep the existing JSON max-buffer guard while ensuring the child process invocation is consistent across platforms.
index.ts
test/complete-corpus.test.ts
Extend documentation and changelog to describe whole-workspace safety guarantees and the new failure mode.
  • Document whole-workspace read safety, fail-closed behavior, and the 64 MiB transport guard in the README with guidance on PM_JSON_MAX_BUFFER.
  • Add an unreleased changelog entry noting that incomplete pm corpora are now refused before GitHub imports, exports, and syncs.
README.md
CHANGELOG.md
Pin the development pm CLI toolchain to a newer version that supports the stricter contract.
  • Update the @unbrained/pm-cli devDependency from 2026.8.15 to 2026.8.17 in package.json.
  • Regenerate or update the package lockfile to reflect the new CLI version.
package.json
package-lock.json
Add high-coverage tests for strict decoding, Windows spawn behavior, and real CLI integration, plus PM history artifacts.
  • Create complete-corpus.test.ts with unit tests for completePmListArgs, pmListSpawnOptions, and decodeCompletePmItems, including malformed envelopes, omissions, pagination, compaction, count mismatches, and duplicate IDs.
  • Add an installed-CLI acceptance test that initializes a fresh workspace, creates a closed imported issue and an open task, and verifies readPmItems returns both with preserved fields.
  • Introduce pm history/issue/task artifacts for completeness and identity gates (pm-github-ep0u, pm-github-9cjx, pm-github-zqad).
test/complete-corpus.test.ts
.agents/pm/history/pm-github-9cjx.jsonl
.agents/pm/history/pm-github-ep0u.jsonl
.agents/pm/history/pm-github-zqad.jsonl
.agents/pm/issues/pm-github-ep0u.toon
.agents/pm/issues/pm-github-zqad.toon
.agents/pm/tasks/pm-github-9cjx.toon

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review available on request

  • 🔍 Trigger review

Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment @coderabbitai review to review the latest changes. For a full review, comment @coderabbitai full review.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6b7c9a73-f449-446c-89af-0c9c08aee672


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@unbraind

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

Please review exact head 6c88b6e, with particular attention to fail-closed receipt validation, runtime row narrowing, Windows process behavior, and whether any legitimate complete pm list-all envelope can be rejected.

@unbraind

Copy link
Copy Markdown
Owner Author

@greptileai please review exact head 6c88b6e. Please focus on whole-corpus correctness, data-loss/duplication risks, and test gaps.

@unbraind

Copy link
Copy Markdown
Owner Author

/gemini review

@unbraind

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

Please review exact head 6c88b6e; the local release gate and fresh-tracker acceptance pass, and the separate non-exact coverage gap is explicitly tracked rather than claimed complete.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Repository and requested commit =='
git rev-parse HEAD
git show -s --format='%H%n%P%n%s' HEAD
git rev-parse --verify 6c88b6e^{commit}

printf '%s\n' '== Changed files against first parent =='
base="$(git rev-parse HEAD^)"
git diff --name-status "$base" HEAD
printf '%s\n' '== Diff stat =='
git diff --stat "$base" HEAD

printf '%s\n' '== Candidate symbols =='
rg -n -C 3 --glob '!node_modules/**' \
  'completePmListArgs|decodeCompletePmItems|readPmItems|PM_JSON_MAX_BUFFER|list-all|spawn|execFile|child_process' .

printf '%s\n' '== TypeScript outlines for changed source/tests =='
git diff --name-only --diff-filter=AM "$base" HEAD |
  rg '\.(ts|tsx|js|mjs|cjs)$' |
  while IFS= read -r f; do
    printf '\n-- %s --\n' "$f"
    ast-grep outline "$f" --items all --type function,class,interface,type --view expanded || true
  done

Length of output: 53110


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="$(git rev-parse HEAD^)"

printf '%s\n' '== index.ts change diff =='
git diff --unified=80 "$base" HEAD -- index.ts

printf '%s\n' '== Whole-corpus implementation =='
sed -n '790,1098p' index.ts

printf '%s\n' '== PmItem field consumers and constructors =='
rg -n -C 5 --glob 'index.ts' \
  '\bPmItem\b|\.body\b|\.description\b|\.tags\b|\.title\b|\.status\b|\.id\b' index.ts

printf '%s\n' '== Complete-corpus tests =='
cat -n test/complete-corpus.test.ts

printf '%s\n' '== Package/toolchain metadata =='
cat package.json
printf '\n-- installed pm packages --\n'
fd -HI -t f 'package.json' node_modules 2>/dev/null |
  rg '/(`@unbrained`|unbrained)/|/pm/' |
  head -80 |
  while IFS= read -r f; do
    printf '\n-- %s --\n' "$f"
    sed -n '1,160p' "$f"
  done

printf '%s\n' '== Installed CLI contract candidates =='
rg -n -C 4 -i --hidden \
  'omission_receipt|read_output|pagination|strict-read|output_budget|requested_dimensions' \
  node_modules 2>/dev/null | head -500 || true

Length of output: 100687


🏁 Script executed:

#!/bin/bash
set -u -o pipefail

printf '%s\n' '== Changed non-index files =='
base="$(git rev-parse HEAD^)"
git diff --unified=30 "$base" HEAD -- CHANGELOG.md README.md package.json package-lock.json test/complete-corpus.test.ts || true

printf '%s\n' '== Lockfile pm package resolution =='
rg -n -C 5 '"node_modules/@unbrained/pm-cli"|`@unbrained/pm-cli`|2026\.8\.17' package-lock.json package.json || true

printf '%s\n' '== Public npm package metadata =='
curl --fail --silent --show-error --location \
  'https://registry.npmjs.org/@unbrained%2fpm-cli/2026.8.17' \
  -o /tmp/pm-cli-package.json &&
python3 - <<'PY'
import json
p=json.load(open('/tmp/pm-cli-package.json'))
for k in ('name','version','dist','repository','bin','types','main'):
    print(f'{k}: {p.get(k)!r}')
PY

printf '%s\n' '== Download and inspect the pinned package archive =='
tarball="$(python3 - <<'PY'
import json
print(json.load(open('/tmp/pm-cli-package.json'))['dist']['tarball'])
PY
)"
curl --fail --silent --show-error --location "$tarball" -o /tmp/pm-cli.tgz
rm -rf /tmp/pm-cli-package
mkdir -p /tmp/pm-cli-package
tar -xzf /tmp/pm-cli.tgz -C /tmp/pm-cli-package
fd -HI -t f . /tmp/pm-cli-package | head -100
printf '%s\n' '== Authoritative receipt/schema occurrences =='
rg -n -C 8 -i \
  'output_budget_truncation|output_budget_exceeded|omission_receipt|read_output|requested_dimensions|strict-read' \
  /tmp/pm-cli-package || true

printf '%s\n' '== Windows executable entries =='
find /tmp/pm-cli-package -maxdepth 4 -type f \( -name 'package.json' -o -name '*.cmd' -o -name 'pm' \) -print | sort

Length of output: 202102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

pkg=/tmp/pm-cli-package/package

printf '%s\n' '== Pinned CLI package launcher metadata =='
python3 - <<'PY'
import json
p=json.load(open('/tmp/pm-cli-package/package/package.json'))
print('name:', p.get('name'))
print('version:', p.get('version'))
print('bin:', json.dumps(p.get('bin'), indent=2, sort_keys=True))
PY

printf '%s\n' '== List result declarations and implementation =='
rg -n -C 12 --glob '*.d.ts' \
  'interface ListResult|type ListResult|ListResult' "$pkg/dist" "$pkg/sdk" | head -300 || true
rg -n -C 12 --glob '*.js' \
  'strictRead|strict_read|unreadable_item_count|unreadable_directory_count|list-all|listAll' \
  "$pkg/dist" | head -500 || true

printf '%s\n' '== Exact changed production process calls and line numbers =='
nl -ba index.ts | sed -n '850,1095p'
printf '\n== Existing Windows-aware process examples ==\n'
nl -ba test/smoke.test.ts | sed -n '1156,1175p'
nl -ba test/complete-corpus.test.ts | sed -n '59,67p'

printf '%s\n' '== Search for exact `readPmItems` injection/use coverage =='
rg -n -C 4 '\breadPmItems\s*\(|readItems\s*\?\?' index.ts test --glob '*.ts'

Length of output: 201834


@unbraind I reviewed exact head 6c88b6e0a84781fdf5cf469805b96f7a577d580d.

Finding

  • High — Run the pm shim through a Windows-compatible process path.
    readPmItems calls spawnSync("pm", ...) without shell: process.platform === "win32" at Line 1065. On Windows, an npm-installed CLI resolves to pm.cmd. Node cannot execute that batch shim directly in this configuration. The existing acceptance helper and other test helpers already use shell: process.platform === "win32" for this reason.

    This change routes import, export, state sync, Projects v2 sync, and search fallback through readPmItems. Therefore, these operations now fail before they read the corpus on Windows. Add the Windows shell option, or resolve and execute a platform-specific launcher. Add a Windows-targeted regression test for the production readPmItems invocation.

Verified areas

  • The receipt checks fail closed for pagination, unreadable rows, omission, compacting, budget handling, count reconciliation, and duplicate identities.
  • The runtime row narrowing validates every field that the integration consumes: id, title, status, body, description, and tags.
  • The pinned @unbrained/pm-cli@2026.8.17 contract emits output_budget_truncation and output_budget_exceeded only for incomplete output. The presence-based rejection does not reject a complete envelope from that CLI version.
  • I found no additional valid-complete-envelope rejection in the requested scope.

You are interacting with an AI system.

@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown

Greptile Summary

The PR replaces permissive, capped workspace reads with a strict complete-corpus contract and repairs Windows execution by relaunching the host CLI directly without shell parsing.

  • Requests full, strict, unbounded pm list-all output and validates completeness, omission, pagination, budget, counts, identities, and consumed fields.
  • Uses the host-owned CLI package root on Windows and preserves workspace paths as literal argv entries.
  • Adds adversarial decoder and installed-CLI acceptance coverage, updates documentation, and pins the development CLI to 2026.8.17.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
index.ts Introduces strict complete-corpus decoding and a shell-free Windows host-CLI launcher; the previously reported path-rewriting issue is resolved.
test/complete-corpus.test.ts Adds adversarial envelope validation and installed-CLI coverage, including workspace paths containing shell metacharacters.
package.json Pins the development pm CLI toolchain to 2026.8.17 while retaining the existing runtime compatibility floor.
package-lock.json Synchronizes the resolved CLI development dependency with the new exact manifest pin.
README.md Documents fail-closed whole-workspace reads and the configurable 64 MiB subprocess transport guard.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[GitHub import, export, sync, project, or search] --> B[readPmItems]
  B --> C{Windows?}
  C -->|No| D[Execute pm with argv]
  C -->|Yes| E[Resolve host package bin.pm]
  E --> F[Execute with Node and literal argv]
  D --> G[Strict unbounded list-all]
  F --> G
  G --> H[Decode JSON envelope]
  H --> I{All completeness contracts valid?}
  I -->|No| J[Fail closed]
  I -->|Yes| K[Return complete workspace corpus]
Loading

Reviews (4): Last reviewed commit: "fix: relaunch through the pm host packag..." | Re-trigger Greptile

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The decoder currently hardcodes a large number of individual field checks via requireCompletePmField; consider grouping related envelope contracts into small helper functions or typed structures to make the completeness logic easier to evolve and reason about as the CLI contract changes.
  • Tests assert against specific error message fragments for many failure modes, which tightly couples behavior to exact wording; you might want to centralize error codes or identifiers to keep the negative-path coverage robust while allowing more flexible user-facing text changes.
  • decodeCompletePmItems intentionally whitelists a minimal set of item fields and drops everything else; if future integration paths need additional pm fields, it may be helpful to document this truncation or provide a clear extension point so those new fields are not accidentally lost.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The decoder currently hardcodes a large number of individual field checks via `requireCompletePmField`; consider grouping related envelope contracts into small helper functions or typed structures to make the completeness logic easier to evolve and reason about as the CLI contract changes.
- Tests assert against specific error message fragments for many failure modes, which tightly couples behavior to exact wording; you might want to centralize error codes or identifiers to keep the negative-path coverage robust while allowing more flexible user-facing text changes.
- `decodeCompletePmItems` intentionally whitelists a minimal set of item fields and drops everything else; if future integration paths need additional pm fields, it may be helpful to document this truncation or provide a clear extension point so those new fields are not accidentally lost.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Enable the Windows command shell only when invoking the npm-installed pm shim, preserving direct execution on other platforms. Add a platform-contract regression test and record the exact-head bot review evidence in the package tracker.
@unbraind

Copy link
Copy Markdown
Owner Author

@coderabbitai Fixed the Windows launcher finding in exact head 3bb84d7. Production readPmItems now gets shell: true only on win32, where npm exposes pm.cmd; Linux/macOS retain direct execution. The new regression test binds both platform branches to the options used by the production call. Full local release:check passes (261/261 tests, docstrings, coverage gate, audit, package, changelog). Please run a full review of this new exact head.

@unbraind

Copy link
Copy Markdown
Owner Author

@greptileai Thank you for verifying the completeness and data-safety boundary. The only subsequent code change is the Windows npm-shim correction in exact head 3bb84d7, with a focused regression test. Please re-review that exact head for any cross-platform or corpus-safety regression.

@unbraind

Copy link
Copy Markdown
Owner Author

@sourcery-ai Thank you. Exact head 3bb84d7 addresses the concrete Windows review finding and records your maintainability points. The decoder already documents that it returns the deliberately narrow PmItem projection and validates every consumed field; extending PmItem plus this decoder is the explicit extension point. I retained adjacent receipt checks because each contract is independently auditable, and extra one-use grouping wrappers would conflict with this repository’s helper rule. The tests intentionally match stable diagnostic fragments rather than full wording so user-facing context can still evolve while each fail-closed reason remains observable. Please re-review the new exact head.

@unbraind

Copy link
Copy Markdown
Owner Author

/gemini review

Please review exact head 3bb84d7, including the Windows npm-shim fix and its production-options regression test.

@sourcery-ai

sourcery-ai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Sure! I'm generating a new review now.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

requested="$(git rev-parse --verify 3bb84d7^{commit})"
head="$(git rev-parse HEAD)"
base="$(git rev-parse "${requested}^")"

printf 'requested=%s\nhead=%s\nbase=%s\n' "$requested" "$head" "$base"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$requested"

printf '\n== Changed files ==\n'
git diff --name-status "$base" "$requested"
printf '\n== Diff stat ==\n'
git diff --stat "$base" "$requested"

printf '\n== Changed TypeScript outlines ==\n'
git diff --name-only --diff-filter=AM "$base" "$requested" |
  rg '\.(ts|tsx|js|mjs|cjs)$' |
  while IFS= read -r file; do
    printf '\n-- %s --\n' "$file"
    ast-grep outline "$file" --items all --type function,class,interface,type --view expanded || true
  done

printf '\n== Process, corpus-reader, and decoder diffs ==\n'
git diff --unified=100 "$base" "$requested" -- index.ts test/complete-corpus.test.ts

printf '\n== Relevant production call sites ==\n'
rg -n -C 6 --glob '!node_modules/**' \
  '\b(readPmItems|decodeCompletePmItems|completePmListArgs|spawnSync|spawn|execFile|PM_JSON_MAX_BUFFER)\b' \
  index.ts test || true

Length of output: 80630


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="$(git rev-parse --verify 3bb84d7^{commit})"
printf '== PR metadata and comparison base ==\n'
gh pr view 46 --repo unbraind/pm-github --json baseRefOid,headRefOid,commits,files \
  --jq '{baseRefOid, headRefOid, commits: [.commits[] | {oid: .oid, messageHeadline}], files: [.files[] | {path, additions, deletions}]}' || true

base="$(git merge-base "$head" "$(gh pr view 46 --repo unbraind/pm-github --json baseRefOid --jq .baseRefOid 2>/dev/null || git rev-parse "$head^")")"
printf '\nreview_base=%s\nhead=%s\n' "$base" "$head"
printf '\n== Complete PR file list ==\n'
git diff --name-status "$base" "$head"
printf '\n== Complete PR stat ==\n'
git diff --stat "$base" "$head"

printf '\n== Process-option and reader source ==\n'
nl -ba index.ts | sed -n '860,1120p'

printf '\n== pmRoot origins and complete-reader callers ==\n'
rg -n -C 5 --glob 'index.ts' \
  '(ctx\.pm_root|pmRoot|pm_root|readPmItems\s*\(|completePmListArgs\s*\()' index.ts

printf '\n== Existing Windows shell use and argument safety comments ==\n'
rg -n -C 5 --glob '*.{ts,js,mjs,cjs}' \
  '(shell:\s*process\.platform\s*===\s*["'\'']win32["'\'']|shell:\s*true|cmd\.exe|\.cmd|CVE-2024-27980)' \
  . -g '!node_modules/**'

printf '\n== Node child_process declarations and documentation candidates ==\n'
node_types="$(npm root 2>/dev/null)/@types/node"
if [ -d "$node_types" ]; then
  rg -n -C 8 'shell.*[Ss]pawn|[Ss]pawn.*shell|metacharacter|unsanitized user input' \
    "$node_types/child_process.d.ts" "$node_types"/*.md 2>/dev/null || true
fi

printf '\n== PM history artifact diff ==\n'
git diff --unified=30 "$base" "$head" -- .agents/pm/history/pm-github-ep0u.jsonl .agents/pm/issues/pm-github-ep0u.toon

Length of output: 4535


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="$(git rev-parse --verify 3bb84d7^{commit})"
base="f7315e2521d28558a2db3b38e89724ad98875a8c"

printf '%s\n' '== Reader and Windows process path =='
sed -n '860,1120p' index.ts

printf '%s\n' '== pmRoot sources and reader callers =='
rg -n -C 5 --glob 'index.ts' \
  '(ctx\.pm_root|pmRoot|pm_root|readPmItems\s*\(|completePmListArgs\s*\()' index.ts

printf '%s\n' '== Existing Windows shell calls and safety notes =='
rg -n -C 5 --glob '*.{ts,js,mjs,cjs}' \
  '(shell:\s*process\.platform\s*===\s*["'\'']win32["'\'']|shell:\s*true|cmd\.exe|\.cmd|CVE-2024-27980)' \
  . -g '!node_modules/**' || true

printf '%s\n' '== Local Node child_process API declarations =='
node_types="$(npm root 2>/dev/null)/@types/node"
if [ -f "$node_types/child_process.d.ts" ]; then
  rg -n -C 10 'shell.*[Ss]pawn|[Ss]pawn.*shell|metacharacter|unsanitized user input' \
    "$node_types/child_process.d.ts" || true
fi

printf '%s\n' '== Complete production diff =='
git diff --unified=40 "$base" "$head" -- index.ts

printf '%s\n' '== Tests, docs, dependencies, and tracker artifacts =='
git diff --unified=30 "$base" "$head" -- \
  test/complete-corpus.test.ts README.md CHANGELOG.md package.json package-lock.json \
  .agents/pm/history/pm-github-9cjx.jsonl \
  .agents/pm/history/pm-github-ep0u.jsonl \
  .agents/pm/history/pm-github-zqad.jsonl \
  .agents/pm/issues/pm-github-ep0u.toon \
  .agents/pm/issues/pm-github-zqad.toon \
  .agents/pm/tasks/pm-github-9cjx.toon

Length of output: 142799


🤖 web_search executed:

site:nodejs.org/api child_process spawn shell option unsanitized user input command shell metacharacters Windows

Length of output: 14020


@unbraind I reviewed exact head 3bb84d72366bf062a1386b17b08c18bb0cd0a7bf against PR base f7315e2521d28558a2db3b38e89724ad98875a8c.

The original Windows npm-shim finding is addressed. readPmItems() now uses the Windows shell only on win32.

Finding

  • High — Do not pass pmRoot through cmd.exe. At Line 920, pmListSpawnOptions() enables shell: true on Windows. At Line 888, completePmListArgs() inserts pmRoot into that process invocation. pmRoot comes from the extension host and can be a user-selected workspace path. A path that contains cmd.exe metacharacters, such as &, |, %, ^, or parentheses, can alter the command line. It can execute an additional command or cause pm to receive a different path.

    Node explicitly warns against unsanitized input with spawn() and shell: true, because shell metacharacters can cause arbitrary command execution. The current test only asserts { shell: true }. It does not prove safe argument transport.

    Resolve the installed CLI’s JavaScript entry point and execute it with process.execPath on Windows. This retains argument-vector semantics and avoids cmd.exe. Add a Windows regression test with a workspace path that contains cmd.exe metacharacters.

Verified areas

  • The decoder still fails closed for pagination, completeness, omission, projection, budget, count, duplicate identity, and consumed-field contracts.
  • The new reader retains the unbounded full-corpus request.
  • The CLI development pin and lockfile consistently use @unbrained/pm-cli@2026.8.17.
  • I found no other complete-corpus regression in this PR.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

Comment thread index.ts Outdated
Resolve the installed pm package entry point and execute it directly with Node on Windows, avoiding cmd.exe interpretation of user-selected workspace paths. Exercise the production Windows strategy against a real tracker path containing shell metacharacters and record the review correction.
@unbraind

Copy link
Copy Markdown
Owner Author

@coderabbitai Fixed the command-injection finding in exact head 496cb11. Windows no longer uses a shell: readPmItems resolves the installed @unbrained/pm-cli package metadata, reads its declared bin.pm JavaScript entry, and launches that entry with process.execPath plus a direct argument vector. The regression test exercises this production Windows strategy against a real tracker path containing &() and reads the expected item. Full local release:check passes (261/261). Please perform a full exact-head review.

@unbraind

Copy link
Copy Markdown
Owner Author

@greptileai The previous re-review check failed without an explanatory summary or review thread, so there was no actionable detail to address. Exact head 496cb11 now removes the Windows shell entirely and proves argument preservation with a real metacharacter-path tracker. Please re-review this head and report any concrete finding in the PR.

@unbraind

Copy link
Copy Markdown
Owner Author

@sourcery-ai The head advanced to 496cb11 while your prior review was pending. This head replaces the temporary Windows shell strategy with direct process.execPath execution of the installed package-declared pm entry point and includes real metacharacter-path acceptance. Please review this exact head.

@unbraind

Copy link
Copy Markdown
Owner Author

/gemini review

Please review exact head 496cb11, particularly the shell-free Windows launcher and real metacharacter-path acceptance.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • The decodeCompletePmItems envelope validation is tightly coupled to the current CLI schema (e.g., contract_version === 1, exact read_output.command, specific completeness fields); consider centralizing these constants and adding a small compatibility layer so future CLI contract additions or version bumps don’t break all GitHub operations by default.
  • To avoid accidental future misuse, you might constrain requireCompletePmField’s expected/actual types to primitives (e.g., string | number | boolean | null) rather than unknown, which would make it harder to call it with structured objects where === comparison would be fragile.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `decodeCompletePmItems` envelope validation is tightly coupled to the current CLI schema (e.g., `contract_version === 1`, exact `read_output.command`, specific completeness fields); consider centralizing these constants and adding a small compatibility layer so future CLI contract additions or version bumps don’t break all GitHub operations by default.
- To avoid accidental future misuse, you might constrain `requireCompletePmField`’s `expected`/`actual` types to primitives (e.g., `string | number | boolean | null`) rather than `unknown`, which would make it harder to call it with structured objects where `===` comparison would be fragile.

## Individual Comments

### Comment 1
<location path="test/complete-corpus.test.ts" line_range="123-132" />
<code_context>
+test("decoder refuses every independent incomplete, omitted, paginated, compacted, or contradictory receipt", () => {
</code_context>
<issue_to_address>
**suggestion (testing):** Add a few more adversarial envelope cases, especially around type/contract drift in receipts

To further harden the decoder against future CLI/protocol drift, consider adding a few targeted cases:

- A `read_output.contract_version` mismatch (e.g. `contract_version: 2`) to confirm we reject unexpected protocol versions, not just missing contracts.
- Non-object receipts currently normalized via `isJsonRecord`, e.g. `completeness: null` or `omission_receipt: []`, to assert we fail closed on structurally invalid shapes.
- A non-array `omitted_field_groups` (e.g. `omitted_field_groups: "body"`) to ensure the explicit empty-array guard is exercised, not just the length check.

These cases would strengthen the guarantee that `decodeCompletePmItems` rejects structural or version drift in the JSON envelope, not only value-level changes.

Suggested implementation:

```typescript
test("decoder refuses every independent incomplete, omitted, paginated, compacted, or contradictory receipt", () => {
  const completeReadOutput = completeEnvelope().read_output as Record<string, unknown>;
  const cases: Array<[string, unknown, RegExp]> = [
    ["bare array", [], /top-level object/],
    ["missing items", withoutField("items"), /items must be an array/],
    ["truncated", completeEnvelope({ truncated: true }), /truncated must be false/],
    ["has more", completeEnvelope({ has_more: true }), /has_more must be false/],
    ["cursor", completeEnvelope({ next_cursor: "next" }), /next_cursor must be null/],
    ["missing completeness", withoutField("completeness"), /completeness.status must be "complete"/],
    ["partial completeness", completeEnvelope({ completeness: { status: "partial", unreadable_item_count: 1, unreadable_directory_count: 0 } }), /completeness.status must be "complete"/],
    ["unreadable item", completeEnvelope({ completeness: { status: "complete", unreadable_item_count: 1, unreadable_directory_count: 0 } }), /unreadable_item_count must be 0/],
    [
      "contract version mismatch",
      completeEnvelope({ read_output: { ...completeReadOutput, contract_version: 2 } }),
      /contract_version must be 1/,
    ],
    [
      "null completeness",
      completeEnvelope({ completeness: null }),
      /completeness must be an object/,
    ],
    [
      "array omission_receipt",
      completeEnvelope({ omission_receipt: [] }),
      /omission_receipt must be an object/,
    ],
    [
      "string omitted_field_groups",
      completeEnvelope({ omitted_field_groups: "body" }),
      /omitted_field_groups must be an array/,
    ],

```

If the actual decoder error messages differ from the regexes used here, you will need to update the `/contract_version must be 1/`, `/completeness must be an object/`, `/omission_receipt must be an object/`, and `/omitted_field_groups must be an array/` patterns to match the real error text emitted by `decodeCompletePmItems`. Otherwise, the structural test cases will work as written.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread test/complete-corpus.test.ts
@sourcery-ai

sourcery-ai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Hey @unbraind, I've posted a new review for you!

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

requested="$(git rev-parse --verify 496cb11^{commit})"
head="$(git rev-parse HEAD)"
printf 'requested=%s\nhead=%s\n' "$requested" "$head"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$requested"

printf '\n== PR metadata and review base ==\n'
gh pr view 46 --repo unbraind/pm-github --json baseRefOid,headRefOid,commits,files \
  --jq '{baseRefOid,headRefOid,commits:[.commits[]|{oid,messageHeadline}],files:[.files[]|{path,additions,deletions}]}' || true
base="$(gh pr view 46 --repo unbraind/pm-github --json baseRefOid --jq .baseRefOid 2>/dev/null || git rev-parse "${requested}^")"
printf 'base=%s\n' "$base"

printf '\n== Full PR file changes ==\n'
git diff --name-status "$base" "$requested"
printf '\n== Full PR stat ==\n'
git diff --stat "$base" "$requested"

printf '\n== Changed TypeScript structure ==\n'
git diff --name-only --diff-filter=AM "$base" "$requested" |
  rg '\.(ts|tsx|js|mjs|cjs)$' |
  while IFS= read -r file; do
    printf '\n-- %s --\n' "$file"
    ast-grep outline "$file" --items all --type function,class,interface,type --view expanded || true
  done

printf '\n== Reader, launcher, and decoder source ==\n'
rg -n -C 8 --glob 'index.ts' \
  'readPmItems|decodeCompletePmItems|completePmListArgs|pmListSpawnOptions|process\.execPath|require\.resolve|createRequire|`@unbrained/pm-cli`|spawnSync|execFileSync' .

printf '\n== Focused production source ranges ==\n'
nl -ba index.ts | sed -n '830,1160p'

printf '\n== Reader callers and pmRoot origins ==\n'
rg -n -C 6 --glob 'index.ts' \
  'readPmItems\s*\(|pmRoot|pm_root|workspace.*root|ctx\.' index.ts

printf '\n== Complete-corpus tests and Windows tests ==\n'
fd -t f . test | sort | while IFS= read -r file; do
  if rg -q 'completePmListArgs|decodeCompletePmItems|readPmItems|process\.execPath|win32|metacharacter|spawnSync|pm-cli' "$file"; then
    printf '\n-- %s --\n' "$file"
    cat -n "$file"
  fi
done

printf '\n== Package metadata and lock resolution ==\n'
cat package.json
rg -n -C 5 '`@unbrained/pm-cli`|2026\.8\.17' package-lock.json package.json || true

printf '\n== Full relevant diff ==\n'
git diff --unified=60 "$base" "$requested" -- index.ts test README.md CHANGELOG.md package.json package-lock.json

Length of output: 28357


🏁 Script executed:

#!/bin/bash
set -euo pipefail

requested="$(git rev-parse --verify 496cb11^{commit})"
base="$(gh pr view 46 --repo unbraind/pm-github --json baseRefOid --jq .baseRefOid 2>/dev/null || git merge-base "$requested" "$(git rev-parse "$requested^")")"

printf '%s\n' '== Imports, complete-reader implementation, and buffer guard =='
sed -n '1,70p' index.ts
sed -n '850,1130p' index.ts
sed -n '1168,1208p' index.ts

printf '%s\n' '== Exact complete-corpus test structure =='
ast-grep outline test/complete-corpus.test.ts --items all --view expanded || true
printf '%s\n' '== Complete-corpus test source =='
cat test/complete-corpus.test.ts

printf '%s\n' '== Full code diff for this PR =='
git diff --unified=80 "$base" "$requested" -- index.ts test/complete-corpus.test.ts

printf '%s\n' '== Dependency declarations =='
cat package.json
printf '%s\n' '== Lockfile package resolution context =='
rg -n -C 8 '"node_modules/@unbrained/pm-cli"|"`@unbrained/pm-cli`"|2026\.8\.17' package-lock.json

printf '%s\n' '== Runtime package-resolution assumptions =='
rg -n -C 5 --glob '!node_modules/**' \
  'createRequire\(|require\.resolve|`@unbrained/pm-cli/package`\.json|peerDependencies|dependencies|devDependencies|bundl|standalone-installed' \
  README.md index.ts package.json test

printf '%s\n' '== All modified non-code files =='
git diff --unified=40 "$base" "$requested" -- README.md CHANGELOG.md package.json package-lock.json \
  .agents/pm/history/pm-github-9cjx.jsonl \
  .agents/pm/history/pm-github-ep0u.jsonl \
  .agents/pm/history/pm-github-zqad.jsonl \
  .agents/pm/issues/pm-github-ep0u.toon \
  .agents/pm/issues/pm-github-zqad.toon \
  .agents/pm/tasks/pm-github-9cjx.toon

Length of output: 157819


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Extension manifest and runtime packaging references =='
fd -t f 'manifest.json' . -E node_modules -E .git -x sh -c 'echo "-- $1"; cat "$1"' sh {} || true
rg -n -C 8 --glob '!node_modules/**' \
  'pm install|extensions|extension.*load|extension.*path|runtime|dist/index|peerDependencies|pm_min_version' \
  README.md manifest.json index.ts test package.json || true

printf '%s\n' '== Pinned pm-cli package metadata from npm registry =='
curl --fail --silent --show-error --location \
  'https://registry.npmjs.org/@unbrained%2fpm-cli/2026.8.17' \
  -o /tmp/pm-cli-package.json
python3 - <<'PY'
import json
p=json.load(open('/tmp/pm-cli-package.json'))
for key in ('name','version','bin','main','files','dependencies','peerDependencies'):
    print(f'{key}: {p.get(key)!r}')
print('tarball:', p['dist']['tarball'])
PY

printf '%s\n' '== Pinned pm-cli package extension installation references =='
tarball="$(python3 - <<'PY'
import json
print(json.load(open('/tmp/pm-cli-package.json'))['dist']['tarball'])
PY
)"
rm -rf /tmp/pm-cli-package
mkdir -p /tmp/pm-cli-package
curl --fail --silent --show-error --location "$tarball" -o /tmp/pm-cli.tgz
tar -xzf /tmp/pm-cli.tgz -C /tmp/pm-cli-package
rg -n -C 8 --glob '*.{js,mjs,cjs,json}' \
  'npm:|install.*extension|extensionsDir|extension.*install|node_modules|copy.*dist|import\(.*index|loadExtension' \
  /tmp/pm-cli-package/package/dist | head -1000 || true

printf '%s\n' '== Pinned CLI bin declaration =='
cat /tmp/pm-cli-package/package/package.json | sed -n '1,180p'

Length of output: 202100


@unbraind I reviewed exact head 496cb11b3d5f45d30307a827ac80461174a81feb.

The command-injection finding is addressed. The Windows reader now uses process.execPath and a direct argument vector. The metacharacter-path regression covers the prior cmd.exe risk.

Finding

  • High — Do not resolve @unbrained/pm-cli from the installed extension. At Line 1075, the Windows branch calls createRequire(import.meta.url).resolve("@unbrained/pm-cli/package.json"). However, Line 724 states that standalone-installed extensions load only their own dist/, where @unbrained/pm-cli is not resolvable. The published package also does not include node_modules; it declares the CLI as a peer dependency.

    Therefore, a normal installed extension on Windows can throw MODULE_NOT_FOUND before spawnSync() runs. Import, export, sync, Projects v2 sync, and search fallback then fail on Windows.

    The new test runs from the repository checkout. The development node_modules/@unbrained/pm-cli directory makes the resolution succeed. The test does not reproduce the installed extension layout.

    Launch the JavaScript entry point of the already-running host CLI instead. For example, use the host process entry path when it is available, such as process.argv[1], with process.execPath and the direct argument vector. Do not resolve the peer dependency from the extension package. Add an installed-package Windows regression that verifies the extension can read a workspace with &() in its path.

Verified areas

  • The complete-corpus decoder still fails closed for pagination, source completeness, omissions, projection, budget receipts, count reconciliation, duplicate IDs, and consumed fields.
  • The reader still requests strict, full, unbounded output.
  • The 10,000-row limit remains removed.
  • The development CLI pin and lockfile use @unbrained/pm-cli@2026.8.17.

You are interacting with an AI system.

Use the host-owned PM_CLI_PACKAGE_ROOT contract for shell-free Windows corpus reads instead of resolving the CLI peer from a standalone extension. Validate the declared entry stays inside the host package, harden receipt drift tests and primitive contracts, and record packed-install acceptance evidence.
@unbraind

Copy link
Copy Markdown
Owner Author

@coderabbitai Fixed the standalone-layout finding in exact head 45759bd. The Windows route no longer resolves any peer from the extension. It uses the host-owned PM_CLI_PACKAGE_ROOT contract set by both pm CLI and pm MCP, reads that host package’s declared bin.pm, rejects entries outside the host root, and executes it directly with Node. The metacharacter test injects this host root. I also packed the 13-file npm artifact, installed it into a fresh tracker with no extension node_modules, and successfully ran installed pm github export --dry-run --json. Full local release gate passes. Please perform a full exact-head review.

@unbraind

Copy link
Copy Markdown
Owner Author

@sourcery-ai Applied the concrete hardening from your latest review in exact head 45759bd: contract version and command are centralized, requireCompletePmField only accepts primitive expected values, and adversarial coverage now includes contract version 2, null/array receipt shapes, and non-array omission groups. Version mismatch remains deliberately fail-closed: a new protocol must be explicitly validated before GitHub operations trust it. Please re-review this exact head.

@unbraind

Copy link
Copy Markdown
Owner Author

@greptileai Exact head 45759bd fixes the installed-extension Windows concern by using the host-owned package-root contract, adds protocol-drift cases, and passed a real packed-artifact fresh install/export acceptance. Please re-review this exact head for correctness, security, and standalone-install behavior.

@unbraind

Copy link
Copy Markdown
Owner Author

/gemini review

Please review exact head 45759bd, with focus on host-package resolution, Windows argument safety, and receipt protocol drift.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

requested="$(git rev-parse --verify 45759bd^{commit})"
head="$(git rev-parse HEAD)"
base="$(gh pr view 46 --repo unbraind/pm-github --json baseRefOid --jq .baseRefOid 2>/dev/null || git rev-parse "${requested}^")"

printf 'requested=%s\nhead=%s\nbase=%s\n' "$requested" "$head" "$base"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$requested"

printf '\n== Complete PR changes ==\n'
git diff --name-status "$base" "$requested"
git diff --stat "$base" "$requested"

printf '\n== Changed source and test outlines ==\n'
git diff --name-only --diff-filter=AM "$base" "$requested" |
  rg '\.(ts|tsx|js|mjs|cjs)$' |
  while IFS= read -r file; do
    printf '\n-- %s --\n' "$file"
    ast-grep outline "$file" --items all --type function,class,interface,type --view expanded || true
  done

printf '\n== Launcher, reader, decoder, and environment contract ==\n'
rg -n -C 8 --glob '!node_modules/**' \
  'PM_CLI_PACKAGE_ROOT|readPmItems|decodeCompletePmItems|completePmListArgs|pmListSpawnOptions|process\.execPath|spawnSync|createRequire|requireCompletePmField|list-all' \
  index.ts test package.json README.md

printf '\n== Focused production ranges ==\n'
nl -ba index.ts | sed -n '840,1180p'

printf '\n== Complete-corpus test file ==\n'
cat -n test/complete-corpus.test.ts 2>/dev/null || true

printf '\n== PR diff for source, tests, package metadata, and docs ==\n'
git diff --unified=70 "$base" "$requested" -- \
  index.ts test/complete-corpus.test.ts package.json package-lock.json README.md CHANGELOG.md

Length of output: 76023


🏁 Script executed:

#!/bin/bash
set -euo pipefail

requested="$(git rev-parse --verify 45759bd^{commit})"
base="f7315e2521d28558a2db3b38e89724ad98875a8c"

printf '%s\n' '== Exact Windows launcher and error path =='
sed -n '1070,1160p' index.ts

printf '%s\n' '== Full complete-corpus tests =='
sed -n '1,320p' test/complete-corpus.test.ts

printf '%s\n' '== Exact changes introduced by requested commit =='
git diff --unified=80 "${requested}^" "$requested" -- index.ts test/complete-corpus.test.ts README.md package.json package-lock.json

printf '%s\n' '== Package contents and pack configuration =='
sed -n '1,220p' package.json
git ls-tree -r --name-only "$requested" | sed -n '1,240p'
git diff --unified=30 "$base" "$requested" -- package.json package-lock.json

printf '%s\n' '== Pinned `@unbrained/pm-cli` registry metadata =='
curl --fail --silent --show-error --location \
  'https://registry.npmjs.org/@unbrained%2fpm-cli/2026.8.17' \
  -o /tmp/pm-cli-package.json
python3 - <<'PY'
import json
p = json.load(open('/tmp/pm-cli-package.json'))
for key in ('name', 'version', 'bin', 'main', 'files', 'dependencies', 'peerDependencies'):
    print(f'{key}: {p.get(key)!r}')
print('tarball:', p['dist']['tarball'])
PY

printf '%s\n' '== Pinned pm CLI implementation of host package root propagation =='
tarball="$(python3 - <<'PY'
import json
print(json.load(open('/tmp/pm-cli-package.json'))['dist']['tarball'])
PY
)"
rm -rf /tmp/pm-cli-package
mkdir -p /tmp/pm-cli-package
curl --fail --silent --show-error --location "$tarball" -o /tmp/pm-cli.tgz
tar -xzf /tmp/pm-cli.tgz -C /tmp/pm-cli-package
rg -n -C 10 'PM_CLI_PACKAGE_ROOT|packageRoot|package_root|spawnSync|execFile|extension' /tmp/pm-cli-package/package \
  -g '*.{js,mjs,cjs,json,d.ts}' || true

Length of output: 202275


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== PM_CLI_PACKAGE_ROOT occurrences in the pinned host package =='
rg -n -l -F 'PM_CLI_PACKAGE_ROOT' /tmp/pm-cli-package/package -g '*.{js,mjs,cjs,json,d.ts}' | sort
rg -n -F -C 2 'PM_CLI_PACKAGE_ROOT' /tmp/pm-cli-package/package \
  -g '!dist/cli-bundle/sdk-core.js' -g '*.{js,mjs,cjs,json,d.ts}' || true

printf '%s\n' '== Short context for minified bundle occurrences =='
python3 - <<'PY'
from pathlib import Path
for p in Path('/tmp/pm-cli-package/package').rglob('*'):
    if not p.is_file():
        continue
    try:
        text = p.read_text(errors='ignore')
    except OSError:
        continue
    needle = 'PM_CLI_PACKAGE_ROOT'
    start = 0
    while True:
        pos = text.find(needle, start)
        if pos < 0:
            break
        print(f'\n-- {p} offset={pos} --')
        print(text[max(0, pos-700):pos+1000])
        start = pos + len(needle)
PY

printf '%s\n' '== Exact new Windows and installed-artifact tests =='
sed -n '70,280p' test/complete-corpus.test.ts

printf '%s\n' '== Test references to package archives, isolated installs, and PM_CLI_PACKAGE_ROOT =='
rg -n -C 10 \
  'npm pack|pack |fresh|install|node_modules|PM_CLI_PACKAGE_ROOT|readPmItems\(' \
  test package.json README.md -g '*.{ts,js,mjs,cjs,json,md}'

printf '%s\n' '== Complete requested-commit diff, limited to the new launcher and tests =='
git diff --unified=50 496cb11b3d5f45d30307a827ac80461174a81feb 45759bd277a73b18234a7490ae2bb03733f67894 \
  -- index.ts test/complete-corpus.test.ts

printf '%s\n' '== Host package bin declaration and package-root entries =='
python3 - <<'PY'
import json
p=json.load(open('/tmp/pm-cli-package/package/package.json'))
print('bin =', repr(p.get('bin')))
print('files =', repr(p.get('files')))
PY
find /tmp/pm-cli-package/package -maxdepth 3 -type f | sed -n '1,120p'

Length of output: 151481


🏁 Script executed:

#!/bin/bash
set -euo pipefail

pkg=/tmp/pm-cli-package/package

printf '%s\n' '== Exact host-root assignments in normal host entrypoints =='
for file in \
  "$pkg/dist/cli.js" \
  "$pkg/dist/cli/main.js" \
  "$pkg/dist/mcp/server.js" \
  "$pkg/dist/sdk/extension.js" \
  "$pkg/dist/sdk/extension/loader.js"
do
  if [ -f "$file" ]; then
    echo "-- $file --"
    rg -n -C 6 -F 'PM_CLI_PACKAGE_ROOT' "$file" || true
  fi
done

printf '%s\n' '== All non-minified PM_CLI_PACKAGE_ROOT occurrence locations =='
rg -n -l -F 'PM_CLI_PACKAGE_ROOT' "$pkg" \
  -g '!dist/cli-bundle/sdk-core.js' -g '*.{js,mjs,cjs}' | sort

printf '%s\n' '== Host root resolver implementation =='
rg -n -l 'resolvePmPackageRootFromModule' "$pkg/dist" -g '*.{js,mjs,cjs}' |
  while IFS= read -r file; do
    echo "-- $file --"
    rg -n -C 5 'resolvePmPackageRootFromModule' "$file" || true
  done

printf '%s\n' '== Path containment behavior for relevant edge cases =='
python3 - <<'PY'
import posixpath
root = '/opt/pm'
for target in ['/opt/pm/dist/cli.js', '/opt/pm-other/cli.js', '/opt/pm/../outside/cli.js']:
    resolved = posixpath.normpath(target)
    rel = posixpath.relpath(resolved, root)
    print(f'root={root!r} target={target!r} resolved={resolved!r} relative={rel!r} rejected={rel.startswith("..") or posixpath.isabs(rel)}')
PY

printf '%s\n' '== Full PR changed production regions and regression-test names =='
git diff --unified=20 f7315e2521d28558a2db3b38e89724ad98875a8c 45759bd277a73b18234a7490ae2bb03733f67894 -- index.ts |
  sed -n '1,2200p'
rg -n '^test\(' test/complete-corpus.test.ts

Length of output: 140451


@unbraind I reviewed exact head 45759bd277a73b18234a7490ae2bb03733f67894.

I found no new actionable findings.

The prior standalone-layout finding is addressed.

  • The pinned pm CLI entrypoint sets PM_CLI_PACKAGE_ROOT before it loads extensions.
  • The pinned pm MCP entrypoint sets the same host-owned value.
  • The Windows path reads the host package bin.pm, rejects an entry outside the host package root, and invokes Node with a direct argument vector.
  • The Windows path does not use shell: true or cmd.exe.
  • The metacharacter workspace test covers &() through the production Windows strategy.
  • The complete-reader decoder still fails closed for pagination, completeness, omissions, projection mode, output budgets, count reconciliation, duplicate IDs, and all consumed item fields.
  • The complete reader still requests strict, full, unbounded pm list-all output.

I did not run the test suite in this review environment. The environment permits static inspection only.

You are interacting with an AI system.

@unbraind

Copy link
Copy Markdown
Owner Author

@coderabbitai Acknowledged and verified against the same exact head: the full local release gate, real packed-install acceptance, and both Node CI jobs pass. Thank you for confirming that the host-root fix closes the Windows security and standalone-layout findings with no new actionable issue.

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@unbraind Thank you for the verification. The reported checks support the reviewed Windows launcher and standalone-install behavior at exact head 45759bd.

🐇 ✅

You are interacting with an AI system.

@unbraind

Copy link
Copy Markdown
Owner Author

Acknowledged. This closes the exact-head CodeRabbit loop: three successive Windows findings were fixed, the final static re-review found no actionable issue, local and GitHub gates pass, and the packed standalone artifact was exercised successfully. Greptile, Sourcery, Semgrep, and both Node jobs are green; Cubic skipped and Gemini produced no response after repeated exact-head triggers.

@unbraind
unbraind merged commit e0798b7 into main Aug 17, 2026
7 checks passed
@unbraind
unbraind deleted the require-complete-pm-corpus branch August 17, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant