Skip to content

Content-field & governance introspection: utilization stats + field-existence & missing-metadata filters (GH-241/242/236) - #251

Merged
unbraind merged 3 commits into
mainfrom
feat/content-field-governance-introspection
Jun 16, 2026
Merged

unbraind merged 3 commits into
mainfrom
feat/content-field-governance-introspection

Conversation

@unbraind

Copy link
Copy Markdown
Owner

Summary

Makes under-contextualized items queryable — directly serving the project philosophy project management = context management. Combines three GitHub issues behind one shared, pure core primitive so an auditor/agent can answer "which items lack notes/body/tests?" and "which open work is missing governance metadata?" without external scripting.

Issue Item Delivered
#242 pm-hntj Content-field existence filters
#236 pm-mfl1 Governance metadata missing-field selectors
#241 pm-7snq pm stats --field-utilization

What's new

Content-field filters (#242) — pm list / list-open / list-all / search

--has-notes/--no-notes, --has-learnings/--no-learnings, --has-files/--no-files, --has-docs/--no-docs, --has-tests/--no-tests, --has-comments/--no-comments, --has-deps/--no-deps, --has-body/--empty-body, --has-linked-command/--no-linked-command. Bulk --filter-* variants land on update-many/close-many for deterministic backfill targeting. list/list-all force the heavy / with-body reader only when a content filter needs collections/body — the light hot path is untouched otherwise.

Governance missing-field selectors (#236) — same commands

--filter-reviewer-missing, --filter-risk-missing, --filter-confidence-missing, --filter-sprint-missing, --filter-release-missing. Wired as independent AND predicates and deliberately not folded into the existing --filter-metadata-missing union (so that long-standing flag keeps its meaning and stays useful).

pm stats --field-utilization (#241)

Per-field present/total/percent (+ body_populated/empty_body) so dormant features (e.g. notes/learnings at 0%) become immediately discoverable. Switches to the with-body reader only when requested.

pm stats --field-utilization        # find dormant context features
pm list --type Feature --empty-body # features needing a body
pm list --no-tests --filter-reviewer-missing  # untested + unreviewed
pm update-many --filter-no-notes --add-tags needs-notes

Architecture

  • New pure, IO-free primitive src/core/governance/content-fields.ts (ContentFieldFilters, isContentFieldPresent, computeContentFieldUtilization) + governance-missing predicates extending core/governance/metadata-coverage.ts.
  • MCP param names align 1:1 to ListOptions fields (the MCP path forwards params verbatim, unlike the CLI which normalizes first); pm_list/pm_search/pm_update/pm_stats expose the new options. Contracts golden regenerated.

Security (Dependabot #39/#40/#41)

  • vite override 7.3.2 → 7.3.5 (server.fs.deny bypass on Windows / launch-editor NTLMv2 disclosure — dev scope).
  • add @opentelemetry/core >=2.8.0 override (W3C Baggage unbounded memory allocation — runtime scope, via Sentry).

Testing

  • Coverage 100/100/100/100 (statements/branches/functions/lines), 4310 tests.
  • Static quality gate, secret scan, contracts snapshot, changelog drift check: all green.
  • Manual end-to-end smoke in a temp tracker: content/governance filters, both-set usage error, search, bulk update-many, and --field-utilization all verified.

Tracking

Closes #241, #242, #236. Orchestrated via plan pm-php4; each sub-agent recorded its work on the corresponding pm item. This PR also regenerates CHANGELOG.md, clearing pre-existing drift on main.

🤖 Generated with Claude Code

…2/236)

Make under-contextualized items queryable — "project management = context
management". Combines three GitHub issues behind one shared, pure primitive.

GH-242 (pm-hntj): content-field existence filters on list/list-open/list-all
and search, plus --filter-* bulk variants on update-many/close-many:
  --has-notes/--no-notes, --has-learnings/--no-learnings, --has-files/--no-files,
  --has-docs/--no-docs, --has-tests/--no-tests, --has-comments/--no-comments,
  --has-deps/--no-deps, --has-body/--empty-body, --has-linked-command/--no-linked-command.
  list/list-all force the heavy/with-body reader only when a content filter
  needs collections/body, keeping the light hot path otherwise.

GH-236 (pm-mfl1): governance metadata missing-field selectors on the same
commands: --filter-reviewer-missing/--filter-risk-missing/
  --filter-confidence-missing/--filter-sprint-missing/--filter-release-missing.
  Wired as independent AND predicates; deliberately NOT folded into the existing
  --filter-metadata-missing union so that long-standing flag keeps its meaning.

GH-241 (pm-7snq): pm stats --field-utilization — content-field utilization
  report (present/total/percent per field + body_populated/empty_body) so
  dormant features (e.g. notes/learnings at 0%) become discoverable. Stats
  switches to the with-body reader only when requested.

New pure primitive: src/core/governance/content-fields.ts (ContentFieldFilters,
isContentFieldPresent, computeContentFieldUtilization) + governance-missing
predicates extending core/governance/metadata-coverage.ts. MCP param names align
to ListOptions fields (the MCP path forwards params verbatim); pm_list/pm_search/
pm_update/pm_stats expose the new options. Full contracts/MCP/docs/completion
surface; contracts golden regenerated.

Security: bump vite override 7.3.2 -> 7.3.5 (GHSA fs.deny bypass / NTLMv2) and
add @opentelemetry/core >=2.8.0 override (W3C Baggage unbounded allocation,
runtime). Resolves Dependabot alerts #39/#40/#41.

Coverage 100/100/100/100 (4310 tests). Closes #241 #242 #236.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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.

Sorry @unbraind, your pull request is larger than the review limit of 150000 diff characters

@unbraind

Copy link
Copy Markdown
Owner Author

@codex review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@unbraind, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 29 minutes and 51 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: e3478dd9-bb90-4f6c-8ef7-d373e101b58b

📥 Commits

Reviewing files that changed from the base of the PR and between b893c67 and 0cc5e8c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (38)
  • .agents/pm/features/pm-7snq.toon
  • .agents/pm/features/pm-hntj.toon
  • .agents/pm/features/pm-mfl1.toon
  • .agents/pm/history/pm-7snq.jsonl
  • .agents/pm/history/pm-hntj.jsonl
  • .agents/pm/history/pm-mfl1.jsonl
  • .agents/pm/history/pm-php4.jsonl
  • .agents/pm/plans/pm-php4.toon
  • CHANGELOG.md
  • docs/COMMANDS.md
  • package.json
  • src/cli/commands/completion.ts
  • src/cli/commands/list-filter-shared.ts
  • src/cli/commands/list.ts
  • src/cli/commands/search.ts
  • src/cli/commands/stats.ts
  • src/cli/register-list-query.ts
  • src/cli/register-mutation.ts
  • src/cli/register-operations.ts
  • src/cli/registration-helpers.ts
  • src/core/governance/content-fields.ts
  • src/core/governance/metadata-coverage.ts
  • src/mcp/server.ts
  • src/sdk/cli-contracts.ts
  • src/sdk/cli-contracts/tool-option-contracts.ts
  • src/sdk/cli-contracts/tool-parameter-tables.ts
  • tests/fixtures/contracts/full.json
  • tests/unit/cli/registration-helpers.spec.ts
  • tests/unit/commands/completion-command.spec.ts
  • tests/unit/commands/content-governance-filter-resolvers.spec.ts
  • tests/unit/commands/contracts-command.spec.ts
  • tests/unit/commands/list-command.spec.ts
  • tests/unit/commands/metadata-governance-commands.spec.ts
  • tests/unit/commands/register-commands.spec.ts
  • tests/unit/commands/search-command.spec.ts
  • tests/unit/commands/stats-command.spec.ts
  • tests/unit/core/governance/content-fields.spec.ts
  • tests/unit/core/governance/metadata-coverage.spec.ts
📝 Walkthrough

Walkthrough

Three governance features are added: (1) pm stats --field-utilization computes per-content-field presence rates using a new with-body reader path; (2) --has-*/--no-*/--empty-body content-field existence filters for pm list, pm search, pm update-many, and pm close-many; (3) --filter-*-missing governance metadata filters for the same command surface. A new src/core/governance/content-fields.ts module provides the shared primitives. All three features are wired through CLI registration, SDK contracts, MCP, shell completion, and docs.

Changes

Content-field and governance filter features (GH-241/242/236)

Layer / File(s) Summary
Core governance and content-field primitives
src/core/governance/content-fields.ts, src/core/governance/metadata-coverage.ts, tests/unit/core/governance/content-fields.spec.ts, tests/unit/core/governance/metadata-coverage.spec.ts
New content-fields.ts introduces ContentField, ContentFieldItem, per-field presence predicates, AND-semantic filter matching, heavy-load capability flags, and computeContentFieldUtilization. metadata-coverage.ts gains five governance fields on CoverageItem, five new missing predicates, and five new MissingMetadataFilters flags ANDed into itemMatchesMissingMetadata.
List/Search filter implementation
src/cli/commands/list.ts, src/cli/commands/search.ts, src/cli/commands/list-filter-shared.ts, tests/unit/commands/content-governance-filter-resolvers.spec.ts, tests/unit/commands/list-command.spec.ts, tests/unit/commands/search-command.spec.ts, tests/unit/commands/metadata-governance-commands.spec.ts
ListOptions/SearchOptions gain all new presence/absence and governance-missing flags. list.ts adds resolver helpers (resolveContentFieldFilters, echo builders), content-field matching in applyFilters, heavy-path detection in runList, and verbose echo. search.ts mirrors the filter pipeline with lifecycleClassifier threading. hasListFilters recognizes all new flags.
Stats --field-utilization feature
src/cli/commands/stats.ts, src/cli/register-operations.ts, src/mcp/server.ts, tests/unit/commands/stats-command.spec.ts
StatsCommandOptions gains fieldUtilization and StatsResult gains field_utilization. runStats conditionally switches to the with-body reader and calls computeContentFieldUtilization. CLI and MCP forward the new flag.
CLI registration wiring
src/cli/register-list-query.ts, src/cli/register-mutation.ts, src/cli/registration-helpers.ts, tests/unit/cli/registration-helpers.spec.ts, tests/unit/commands/register-commands.spec.ts
registerContentAndGovernanceFilters wires all new flags into list/search commands. registerBulkContentAndGovernanceFilters and its mapper do the same for update-many/close-many. normalizeListOptions/normalizeSearchOptions map Commander negation booleans to no* fields. UPDATE_MANY_CONTROL_OPTION_KEYS excludes new filter keys from mutation payloads.
SDK contracts and shell completion surface
src/sdk/cli-contracts.ts, src/sdk/cli-contracts/tool-option-contracts.ts, src/sdk/cli-contracts/tool-parameter-tables.ts, src/cli/commands/completion.ts, tests/unit/commands/contracts-command.spec.ts, tests/fixtures/contracts/full.json
All four filter contract arrays and STATS_FLAG_CONTRACTS are extended. PM_TOOL_PARAMETER_PROPERTIES gains ~40 new boolean entries. Bash/zsh/fish completions cover all new flags. Contract fixture snapshot updated across all 11 affected commands and 9 action schemas.
Docs, changelog, package overrides, and agent PM records
CHANGELOG.md, docs/COMMANDS.md, package.json, .agents/pm/...
CHANGELOG adds GH-236/242/241 entries. docs/COMMANDS.md documents governance-missing filters, bulk filter options, and --field-utilization. package.json bumps vite to 7.3.5 and pins @opentelemetry/core >=2.8.0. Agent PM feature/plan/history records closed for all three child features.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #241 — This PR directly implements pm stats --field-utilization as requested, including computeContentFieldUtilization, with-body reader switching, and the field_utilization result field.
  • #236 — This PR directly implements the governance metadata missing-field filters (--filter-reviewer-missing, --filter-risk-missing, --filter-confidence-missing, --filter-sprint-missing, --filter-release-missing) across list/search and bulk commands.
  • #242 — This PR directly implements the content-field existence filters (--has-*/--no-*/--empty-body) for list, search, and bulk operations as described in the retrieved pm search / pm list field-existence content filters (--has-notes, --has-learnings, --has-files, --has-comments, --empty-body) #242 issue.

Possibly related PRs

  • unbraind/pm-cli#238: Both PRs extend SearchOptions and the applyFilters pipeline in src/cli/commands/search.ts, sharing direct code-level overlap in search filter/option processing.
  • unbraind/pm-cli#231: Both PRs extend src/core/governance/metadata-coverage.ts and integrate its missing-metadata filter logic into list and stats command paths.
  • unbraind/pm-cli#246: Both PRs modify hasListFilters in src/cli/commands/list-filter-shared.ts to recognize additional active filter fields.

@unbraind

Copy link
Copy Markdown
Owner Author

/gemini review

@unbraind

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements content-field existence filters (GH-242) and governance metadata missing-field filters (GH-236) across the list, search, update-many, and close-many commands, as well as content-field utilization metrics (GH-241) for the stats command. These features are supported by new core primitives, CLI options, completion scripts, and MCP server integrations. Feedback was provided to replace the default switch case in isContentFieldPresent with an exhaustive compile-time check to ensure TypeScript safety when handling the ContentField union type.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/core/governance/content-fields.ts Outdated
Comment on lines +106 to +107
default:
return hasLinkedCommand(item.tests);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using a default case in the switch statement for a union type like ContentField bypasses TypeScript's exhaustiveness checking. If a new content field is added to the ContentField type in the future, the compiler won't warn us that it is unhandled here. It is safer to explicitly handle linked_command and use the default block to perform a compile-time exhaustiveness check.

    case "linked_command":
      return hasLinkedCommand(item.tests);
    default: {
      const _exhaustiveCheck: never = field;
      return false;
    }

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements content-field presence/absence filters and governance metadata missing-field filters across the list, search, update-many, and close-many commands, alongside content-field utilization metrics in the stats command. It introduces new core primitives for content-field and metadata-coverage governance, updates CLI contracts, shell completions, and documentation, and adds comprehensive unit tests. The review feedback suggests improving the switch statement in isContentFieldPresent by explicitly handling all union cases and adding a TypeScript exhaustiveness check to catch unhandled fields at compile time.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +88 to +109
export function isContentFieldPresent(item: ContentFieldItem, field: ContentField): boolean {
switch (field) {
case "notes":
return hasEntries(item.notes);
case "learnings":
return hasEntries(item.learnings);
case "files":
return hasEntries(item.files);
case "docs":
return hasEntries(item.docs);
case "tests":
return hasEntries(item.tests);
case "comments":
return hasEntries(item.comments);
case "deps":
return hasEntries(item.dependencies);
case "body":
return hasBody(item.body);
default:
return hasLinkedCommand(item.tests);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using a default branch in the switch statement bypasses TypeScript's exhaustiveness checks. If a new ContentField is added to the union type in the future, the compiler won't warn about the missing case. It is highly recommended to explicitly handle "linked_command" and use an exhaustive check in the default branch to catch unhandled cases at compile time.

export function isContentFieldPresent(item: ContentFieldItem, field: ContentField): boolean {
  switch (field) {
    case "notes":
      return hasEntries(item.notes);
    case "learnings":
      return hasEntries(item.learnings);
    case "files":
      return hasEntries(item.files);
    case "docs":
      return hasEntries(item.docs);
    case "tests":
      return hasEntries(item.tests);
    case "comments":
      return hasEntries(item.comments);
    case "deps":
      return hasEntries(item.dependencies);
    case "body":
      return hasBody(item.body);
    case "linked_command":
      return hasLinkedCommand(item.tests);
    default: {
      const _exhaustiveCheck: never = field;
      return false;
    }
  }
}

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Around line 148-154: The pnpm.overrides field in package.json is deprecated in
pnpm v9+ and is being completely ignored, meaning version constraints for vite
and `@opentelemetry/core` are not enforced. Create a new pnpm-workspace.yaml file
in the repository root and move the overrides for vite (7.3.5) and
`@opentelemetry/core` (>=2.8.0) into it under an overrides section. Then remove
the entire pnpm.overrides block from the package.json file to complete the
migration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 66045e06-f258-44f9-a7a6-f8874a10426e

📥 Commits

Reviewing files that changed from the base of the PR and between b893c67 and 3d3fcd2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (38)
  • .agents/pm/features/pm-7snq.toon
  • .agents/pm/features/pm-hntj.toon
  • .agents/pm/features/pm-mfl1.toon
  • .agents/pm/history/pm-7snq.jsonl
  • .agents/pm/history/pm-hntj.jsonl
  • .agents/pm/history/pm-mfl1.jsonl
  • .agents/pm/history/pm-php4.jsonl
  • .agents/pm/plans/pm-php4.toon
  • CHANGELOG.md
  • docs/COMMANDS.md
  • package.json
  • src/cli/commands/completion.ts
  • src/cli/commands/list-filter-shared.ts
  • src/cli/commands/list.ts
  • src/cli/commands/search.ts
  • src/cli/commands/stats.ts
  • src/cli/register-list-query.ts
  • src/cli/register-mutation.ts
  • src/cli/register-operations.ts
  • src/cli/registration-helpers.ts
  • src/core/governance/content-fields.ts
  • src/core/governance/metadata-coverage.ts
  • src/mcp/server.ts
  • src/sdk/cli-contracts.ts
  • src/sdk/cli-contracts/tool-option-contracts.ts
  • src/sdk/cli-contracts/tool-parameter-tables.ts
  • tests/fixtures/contracts/full.json
  • tests/unit/cli/registration-helpers.spec.ts
  • tests/unit/commands/completion-command.spec.ts
  • tests/unit/commands/content-governance-filter-resolvers.spec.ts
  • tests/unit/commands/contracts-command.spec.ts
  • tests/unit/commands/list-command.spec.ts
  • tests/unit/commands/metadata-governance-commands.spec.ts
  • tests/unit/commands/register-commands.spec.ts
  • tests/unit/commands/search-command.spec.ts
  • tests/unit/commands/stats-command.spec.ts
  • tests/unit/core/governance/content-fields.spec.ts
  • tests/unit/core/governance/metadata-coverage.spec.ts
👮 Files not reviewed due to content moderation or server errors (1)
  • tests/fixtures/contracts/full.json

Comment thread package.json
Comment on lines 148 to 154
"overrides": {
"rollup": ">=4.59.0",
"brace-expansion": ">=5.0.6",
"vite": "7.3.2",
"esbuild": "0.28.1"
"vite": "7.3.5",
"esbuild": "0.28.1",
"@opentelemetry/core": ">=2.8.0"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
jq -r '.pnpm.overrides.vite, .pnpm.overrides["`@opentelemetry/core`"]' package.json
pnpm why vite `@opentelemetry/core`
pnpm list vite `@opentelemetry/core` --depth Infinity

Repository: unbraind/pm-cli

Length of output: 2235


🌐 Web query:

pnpm overrides configuration 2024 2025 new format package.json

💡 Result:

As of pnpm version 11 and later, configuration for overrides has moved from the package.json file to the pnpm-workspace.yaml file [1][2]. pnpm no longer reads overrides or other settings from the pnpm field in package.json [1][2]. The current standard format for defining overrides is within the root pnpm-workspace.yaml file using the following YAML structure [3][4]: overrides: "package-name": "version" "package-a@version-range>package-b": "new-version" Key changes and usage details: - Relocation: All overrides must now be defined at the root of the project in pnpm-workspace.yaml [3][2]. Definitions inside package.json are ignored and will not take effect [2]. - Syntax: The syntax allows for direct overrides, nested dependency overrides (using the > separator), and removing dependencies entirely using the - character [3][5][4]. - Catalogs: You can maintain version consistency by defining versions in a catalog and referencing them in your overrides using the catalog: protocol [3][4]. - Yarn Resolutions: While pnpm historically supported the Yarn-style resolutions field in package.json, this is now deprecated. Recent updates to pnpm (v11+) have moved to strictly enforcing overrides in pnpm-workspace.yaml [6][7]. Using resolutions at the root may trigger errors or deprecation warnings, and pnpm recommends migrating entirely to the overrides field [7]. If you are upgrading from an older version of pnpm, you must manually move these configurations from your package.json (under the pnpm field) to your pnpm-workspace.yaml file to ensure they are applied [2].

Citations:


🏁 Script executed:

#!/bin/bash
# Check for pnpm-workspace.yaml and its overrides configuration
if [ -f pnpm-workspace.yaml ]; then
  echo "=== pnpm-workspace.yaml found ==="
  cat pnpm-workspace.yaml
else
  echo "=== pnpm-workspace.yaml NOT found ==="
fi

# Also check for .pnpmfile.cjs as an alternative override location
if [ -f .pnpmfile.cjs ]; then
  echo "=== .pnpmfile.cjs found ==="
  cat .pnpmfile.cjs
fi

Repository: unbraind/pm-cli

Length of output: 97


Migrate dependency overrides to pnpm-workspace.yaml — current configuration is non-functional.

The pnpm.overrides field in package.json is deprecated and no longer read by pnpm (v9+). The warning in your pnpm output confirms this: "The 'pnpm' field in package.json is no longer read by pnpm... pnpm.overrides." These overrides are being ignored entirely, meaning vite@7.3.5 and @opentelemetry/core@>=2.8.0 constraints won't be enforced. When the lockfile is regenerated, transitive dependencies can drift to incompatible releases without protection.

Move overrides to a pnpm-workspace.yaml file in the repository root:

Migration required
overrides:
  "vite": "7.3.5"
  "`@opentelemetry/core`": ">=2.8.0"

Remove the pnpm.overrides block from package.json after migrating.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 148 - 154, The pnpm.overrides field in
package.json is deprecated in pnpm v9+ and is being completely ignored, meaning
version constraints for vite and `@opentelemetry/core` are not enforced. Create a
new pnpm-workspace.yaml file in the repository root and move the overrides for
vite (7.3.5) and `@opentelemetry/core` (>=2.8.0) into it under an overrides
section. Then remove the entire pnpm.overrides block from the package.json file
to complete the migration.

…emini review)

Handle "linked_command" as an explicit case and drop the default branch so
TypeScript enforces ContentField exhaustiveness at compile time (adding a field
without a case becomes a compile error). No unreachable branch is introduced, so
the literal-100% coverage gate is preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@unbraind

Copy link
Copy Markdown
Owner Author

Review responses

@gemini-code-assist (medium — exhaustiveness in isContentFieldPresent): Applied in 5689229. Rather than a never-default (which would add an unreachable branch that breaks our literal-100% coverage gate), I handled linked_command as an explicit case and dropped the default entirely — TypeScript now enforces ContentField exhaustiveness at compile time (adding a field without a case is a compile error) with no unreachable code.

@coderabbitai (critical — "overrides non-functional, migrate to pnpm-workspace.yaml"): This is a false positive for this repo's pnpm version (10.x). Verified empirically:

  • pnpm install emits no "pnpm field is no longer read" warning.
  • The overrides demonstrably took effect: @opentelemetry/core deduped to a single version 2.8.0 (impossible without the override — @sentry/node-core pins 2.7.1), and vite resolved to 7.3.5 (natural resolution is 7.3.2). Both are visible in the regenerated pnpm-lock.yaml.

The package.json pnpm.overrides field is the repo's established, working pattern (rollup/brace-expansion/vite/esbuild already live there). The migration to pnpm-workspace.yaml only becomes mandatory on pnpm v11+, which this repo does not use. Declining the migration; happy to revisit when the toolchain bumps to v11.

Re-triggering reviews on the updated branch.

@codex review
/gemini review
@coderabbitai full review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

@unbraind, thanks for the detailed follow-up on both items.

isContentFieldPresent exhaustiveness (Gemini): Good call — the explicit linked_command case with no default is the right approach. TypeScript's exhaustiveness check over the ContentField union will catch any future additions at compile time with zero unreachable branches, so the coverage gate stays clean.

pnpm.overrides (my critical comment): You're right — I was wrong here. pnpm.overrides in package.json remains fully supported in pnpm 10.x; the mandatory migration to pnpm-workspace.yaml is a pnpm v11+ concern. The empirical evidence (no warning emitted, @opentelemetry/core and vite resolving to the pinned versions in the lockfile) conclusively demonstrates the overrides are functional. I'm withdrawing that comment.


✏️ Learnings added
Learnt from: unbraind
URL: https://github.com/unbraind/pm-cli/pull/251

Timestamp: 2026-06-16T13:58:36.115Z
Learning: In the `unbraind/pm-cli` repository, `pnpm.overrides` in `package.json` is the established, working pattern for version overrides (used for `rollup`, `brace-expansion`, `vite`, `esbuild`, `opentelemetry/core`, etc.). The repo uses pnpm 10.x, where `pnpm.overrides` in `package.json` is still fully supported. Do not flag this as needing migration to `pnpm-workspace.yaml`; that migration is only mandatory on pnpm v11+.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Full review finished.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements content-field presence filters, governance metadata missing-field filters, and stats content-field utilization metrics across list, search, stats, and bulk mutation commands. It introduces new core primitives for content-field tracking and extends metadata coverage predicates, along with updating CLI completions, documentation, and test suites. The review feedback suggests validating mutually exclusive bulk filters in mapBulkContentAndGovernanceFilters to provide clearer error messages, and correcting a signature mismatch in the hasListFilters test call.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +112 to +114
function mapBulkContentAndGovernanceFilters(options: Record<string, unknown>): Record<string, unknown> {
const presence = (key: string): true | undefined => (options[key] === true ? true : undefined);
return {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When mutually exclusive bulk filters (e.g., --filter-has-notes and --filter-no-notes) are both passed, the conflict is currently caught downstream in resolveContentFieldFilters which throws an error referencing the non-bulk flags (e.g., --has-notes and --no-notes). This can be confusing to users. Consider adding a validation check inside mapBulkContentAndGovernanceFilters to throw a more accurate error message with the correct --filter- prefix.

function mapBulkContentAndGovernanceFilters(options: Record<string, unknown>): Record<string, unknown> {
  const presence = (key: string): true | undefined => (options[key] === true ? true : undefined);
  const conflicts: [string, string, string][] = [
    ["filterHasNotes", "filterNoNotes", "notes"],
    ["filterHasLearnings", "filterNoLearnings", "learnings"],
    ["filterHasFiles", "filterNoFiles", "files"],
    ["filterHasDocs", "filterNoDocs", "docs"],
    ["filterHasTests", "filterNoTests", "tests"],
    ["filterHasComments", "filterNoComments", "comments"],
    ["filterHasDeps", "filterNoDeps", "deps"],
    ["filterHasBody", "filterEmptyBody", "body"],
    ["filterHasLinkedCommand", "filterNoLinkedCommand", "linked-command"],
  ];
  for (const [hasKey, noKey, label] of conflicts) {
    if (options[hasKey] === true && options[noKey] === true) {
      const hasFlag = hasKey === "filterEmptyBody" ? "--filter-empty-body" : `--filter-has-${label}`;
      const noFlag = noKey === "filterEmptyBody" ? "--filter-empty-body" : `--filter-no-${label}`;
      throw new PmCliError(
        `Cannot combine ${hasFlag} with ${noFlag} for the same field.`,
        EXIT_CODE.USAGE,
      );
    }
  }
  return {

});

it("returns false when no filter is active", () => {
expect(hasListFilters({}, undefined, { includePagination: false })).toBe(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The hasListFilters function only accepts two arguments: list and includePagination?: boolean. Passing a third argument { includePagination: false } is a signature mismatch and completely redundant. Consider correcting it to hasListFilters({}, false).

Suggested change
expect(hasListFilters({}, undefined, { includePagination: false })).toBe(false);
expect(hasListFilters({}, false)).toBe(false);

…emini review)

When --filter-has-X and --filter-no-X are both passed to update-many/close-many,
catch the conflict in mapBulkContentAndGovernanceFilters so the error names the
bulk --filter-* flags the user typed, instead of the downstream list-level
--has-/--no- flags that resolveContentFieldFilters would otherwise report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@unbraind

Copy link
Copy Markdown
Owner Author

Round 2 review responses

@gemini-code-assist (medium — bulk conflict error names the wrong flags, register-mutation.ts): Applied in 0cc5e8c. mapBulkContentAndGovernanceFilters now pre-checks each has/no pair and throws naming the actual --filter-* bulk flags (e.g. Cannot combine --filter-has-notes with --filter-no-notes), so update-many/close-many no longer surface the downstream list-level --has-/--no- names. Covered by a new conflict test.

@gemini-code-assist (medium — hasListFilters({}, undefined, { includePagination: false }) signature mismatch): Declining — this is based on a misread of the signature. hasListFilters(list, status, options) takes three parameters (src/cli/commands/list-filter-shared.ts): list, status, and options: HasListFilterOptions. The test correctly passes list={}, status=undefined, options={includePagination:false}. The suggested hasListFilters({}, false) would pass false as the status string argument, which is incorrect.

Re-triggering reviews.

@codex review
/gemini review
@coderabbitai full review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

@unbraind, thanks for the detailed round 2 responses.

Bulk conflict error naming (applied in 0cc5e8c): That's the right fix — surfacing --filter-has-notes / --filter-no-notes in the error message instead of the downstream --has- / --no- names is exactly what users of update-many / close-many need to see. Good that it's covered by a dedicated conflict test.

hasListFilters signature (declined): Agreed — your reading is correct. hasListFilters(list, status, options) takes three positional parameters, so passing {includePagination: false} as the third argument is accurate. Passing false as the second argument would incorrectly type it as the status string, which is wrong.

Triggering the full review now.

✅ Action performed

Full review finished.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request implements three major governance and content-field introspection features: content-field utilization metrics for stats (GH-241), field-existence filters for list and search (GH-242), and missing-field filters for governance metadata (GH-236). It introduces a new pure primitive module core/governance/content-fields.ts to handle presence checks and compute utilization, and extends metadata-coverage.ts with predicates for missing reviewer, risk, confidence, sprint, and release fields. These filters are fully integrated into the CLI commands (list, search, update-many, close-many, and stats), shell completions, and MCP contracts. Extensive unit and integration tests have been added to verify the new filtering and stats behavior. There are no review comments to address.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

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.

pm stats should report content-field utilization (notes, learnings, files, docs, tests, comments, empty-body) for governance audits

1 participant