Skip to content

chore: single-source CLI normalization helpers - #389

Merged
unbraind merged 4 commits into
mainfrom
chore/reduce-static-duplicate-chunks
Jun 27, 2026
Merged

unbraind merged 4 commits into
mainfrom
chore/reduce-static-duplicate-chunks

Conversation

@unbraind

@unbraind unbraind commented Jun 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Single-source create/update legacy none/null collection normalization in legacy-none-tokens.
  • Single-source runtime schema --unset field resolution in shared-unset-fields while preserving existing test-only aliases.
  • Export the canonical ConfigKey union from the positional config router and reuse it in config.ts.
  • Record implementation and verification evidence on pm-92if.

Verification

  • node scripts/run-tests.mjs coverage -> 256 files, 5137 tests, 100/100/100/100 coverage.
  • pm test pm-92if --run --progress -> 7/7 linked checks passed, including Sentry/telemetry best-effort.
  • pnpm lint
  • pnpm typecheck
  • pnpm quality:static -> duplicate_chunks=0, docstring coverage 100%.
  • pnpm contracts:check
  • pnpm security:scan
  • pnpm changelog:pm:check -> pm-changelog 2026.6.27, CHANGELOG.md unchanged.
  • pnpm audit --audit-level moderate
  • pm health --check-only --summary --json
  • pm validate --check-resolution --check-history-drift --json -> history drift ok; existing closed-item resolution metadata warning backlog remains.
  • node dist/cli.js package doctor --project --detail deep --json
  • Manual temp-dir smoke in isolated PM_PATH/PM_GLOBAL_PATH passed init/config/create/update/files/docs/test/comments/close/release/health/validate.

GitHub sweep

  • No open PRs before this branch.
  • No open Dependabot alerts.
  • No open code-scanning alerts.
  • pnpm audit --audit-level moderate clean.
  • Issue Auto Release blocked: scheduled run failed #374 remains externally blocked by missing release RELEASE_PAT configuration, not by code in this branch.

Summary by cubic

Centralized CLI helpers for legacy "none"/"null" collection handling and runtime --unset field resolution, shared by create and update. Exported canonical ConfigKey, normalized unset matching, and hardened guards to reduce duplicates while keeping behavior strict.

  • Refactors

    • Single-sourced legacy none/null normalizer; maps tokens to clear flags, disables conflicting replace flags, and blocks mixing with entries.
    • Single-sourced runtime --unset resolver; create/update reuse it and test-only aliases still work.
    • Exported ConfigKey from the positional router and reused it in config.
  • Bug Fixes

    • Scoped runtime --unset to command_to_fields.
    • Normalized unset tokens and candidate forms (case and dash/underscore) so camelCase dynamic fields still match lowercased input.
    • Rejected non-string collection entries and ignored malformed/missing runtime field metadata (key/metadata_key/cli_flag/cli_aliases) when matching unset tokens.

Written for commit a800b3f. Summary will update on new commits.

Review in cubic

Share create/update legacy none collection normalization and runtime unset field resolution so the SDK-backed command primitives have one canonical implementation.

Export ConfigKey from the positional config router and reuse it in config command handling to remove duplicated config surface metadata.

PM evidence: pm-92if records GitHub/dependency checks, temp-dir smoke, pm-changelog verification, Sentry/telemetry, static quality, and full 100/100/100/100 coverage.

@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, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@unbraind

Copy link
Copy Markdown
Owner Author

@codex review
/gemini review
@gemini-code-assist
@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 27, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Extracts two shared helpers—applyLegacyNoneCollectionNormalizers and resolveRuntimeUnsetFieldDefinition—into legacy-none-tokens.ts and shared-unset-fields.ts. The create and update commands delegate to these helpers, removing duplicated logic. ConfigKey is exported from positional-value.ts and re-used in config.ts. Tests are updated to match the new registry shape.

Changes

Shared CLI helpers and command consolidation

Layer / File(s) Summary
Shared interfaces and ConfigKey export
src/core/config/positional-value.ts, src/cli/commands/legacy-none-tokens.ts, src/cli/commands/shared-unset-fields.ts
CanonicalConfigKey renamed to exported ConfigKey; LegacyNoneCollectionNormalizer<T> interface and RuntimeUnsetFieldDefinition + resolveRuntimeUnsetFieldDefinition added as shared contracts.
applyLegacyNoneCollectionNormalizers implementation
src/cli/commands/legacy-none-tokens.ts
New exported function validates collection entries are strings, detects legacy "none"/"null" tokens, throws on mixing with concrete entries, and mutates the options object to set clear/disable flags.
create.ts and update.ts delegation
src/cli/commands/create.ts, src/cli/commands/update.ts
Both commands import and use the shared normalizer and resolver, removing duplicated in-file loop/validation logic; test-only exports updated to wrap shared resolver.
ConfigKey import consolidation
src/cli/commands/config.ts
Imports ConfigKey from positional-value module and replaces the manually-declared string-literal union.
Unit tests
tests/unit/commands/legacy-none-tokens.spec.ts, tests/unit/commands/create-command.spec.ts, tests/unit/commands/update-command.spec.ts
Adds malformed-entry rejection test for applyLegacyNoneCollectionNormalizers; updates create/update test registries to include command_to_fields map and allow_unset.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • unbraind/pm-cli#380: Both PRs modify src/cli/commands/shared-unset-fields.ts and refactor how create.ts/update.ts resolve --unset field definitions, making the runtime unset-field resolver work directly related.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: centralizing CLI normalization helpers.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description accurately matches the centralized CLI helper refactor, ConfigKey export, and pm-92if evidence updates.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@chatgpt-codex-connector

Copy link
Copy Markdown

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

@coderabbitai

coderabbitai Bot commented Jun 27, 2026 •

Copy link
Copy Markdown

@unbraind I'll redo the full review of this PR 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 refactors several CLI commands to eliminate duplicate code and types. It consolidates legacy none/null collection normalization and runtime unset field resolution into shared utility functions, which are now reused across the create and update commands. Additionally, it exports the canonical ConfigKey type from the positional config router to remove a duplicate type definition in config.ts. There are no review comments on these changes, and I have no additional feedback to provide.

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.

@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 refactors and single-sources several shared utilities across CLI commands to reduce duplication. Specifically, it consolidates legacy none/null collection normalization into legacy-none-tokens.ts and runtime --unset field resolution into shared-unset-fields.ts, reusing these helpers in both create.ts and update.ts. Additionally, it exports ConfigKey from positional-value.ts to eliminate duplicate type definitions in config.ts. There are no review comments, so I have no feedback to provide.

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.

@cubic-dev-ai cubic-dev-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.

2 issues found across 8 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread src/cli/commands/shared-unset-fields.ts Resolved
Comment thread src/cli/commands/legacy-none-tokens.ts Resolved
@codecov

codecov Bot commented Jun 27, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jun 27, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR centralizes two previously duplicated CLI helpers: the legacy none/null collection normalization loop (extracted into applyLegacyNoneCollectionNormalizers) and the runtime --unset field resolver (extracted into resolveRuntimeUnsetFieldDefinition). It also single-sources the ConfigKey union type from positional-value.ts, removing the copy in config.ts.

  • Legacy normalizer (legacy-none-tokens.ts): The shared function gains a disableFlagKey property (replacing the update-only disableReplaceFlagKey) and adds a non-string entry guard that throws a USAGE error, which is new behavior the old per-command loops did not have.
  • Runtime unset resolver (shared-unset-fields.ts): Scopes lookup to command_to_fields.get(command) instead of iterating all definitions, normalizes the token and all candidate forms to lowercase before matching (preserving camelCase keys registered by plugins), and guards against malformed registry fields.
  • ConfigKey export (positional-value.ts): Renames the private CanonicalConfigKey to the exported ConfigKey, eliminating the 23-line duplicate union in config.ts.

Confidence Score: 5/5

Safe to merge — pure extraction of duplicate logic into shared helpers with no behavioral regressions in the happy path and improved defensive handling of malformed registry data.

Both extracted helpers faithfully replicate the old per-command behavior: the legacy-none normalizer is functionally identical to the removed loops (with an added non-string entry guard), and the runtime unset resolver correctly scopes to per-command field lists and normalizes tokens to lowercase. Test-only aliases preserve the existing test surface, fixtures were updated to supply command_to_fields, and new regression cases cover the malformed-registry edge cases. The ConfigKey rename is a rename-and-export with no logic change.

No files require special attention.

Important Files Changed

Filename Overview
src/cli/commands/shared-unset-fields.ts New shared resolver for runtime --unset tokens; correctly scopes to command_to_fields, normalizes to lowercase, and skips malformed/empty registry fields before matching.
src/cli/commands/legacy-none-tokens.ts New applyLegacyNoneCollectionNormalizers helper; interface generalized with disableFlagKey, non-string entry guard added, logic otherwise identical to the removed per-command loops.
src/core/config/positional-value.ts ConfigKey exported by renaming private CanonicalConfigKey; internal Set generics and toCanonicalKey updated consistently, no behavioral change.
src/cli/commands/create.ts Removes local LegacyNoneCollectionNormalizationDefinition and resolveRuntimeCreateUnsetDefinition; delegates to shared helpers with test-only alias preserved for backward compatibility.
src/cli/commands/update.ts Mirrors create.ts: removes local normalization interface and resolver; disableReplaceFlagKey renamed to disableFlagKey in normalizer array; test alias preserved.
src/cli/commands/config.ts Local ConfigKey union removed; now imports the canonical type from positional-value.ts; otherwise unchanged.
tests/unit/commands/create-command.spec.ts Registry fixture updated to add command_to_fields; new regression cases cover malformed aliases, non-string alias entries, and lowercased camelCase key lookup (githuburl).
tests/unit/commands/update-command.spec.ts Registry fixture updated to add command_to_fields; existing assertions unchanged and still pass through the new test alias wrapper.
tests/unit/commands/legacy-none-tokens.spec.ts New describe block for applyLegacyNoneCollectionNormalizers; adds one regression test for the non-string entry guard added in this PR.

Reviews (4): Last reviewed commit: "fix: normalize runtime unset candidates" | Re-trigger Greptile

Address current-head Cubic review feedback on PR #389 by resolving runtime --unset fields only from the active command_to_fields registry and by rejecting malformed non-string legacy none/null collection payloads with usage errors.

Update command helper fixtures and legacy none-token tests so the new guard branch remains covered under the repo-wide 100/100/100/100 coverage gate.

Verification: pm test pm-92if --run --progress passed 8/8; node scripts/run-tests.mjs coverage passed 256 files, 5138 tests, 100/100/100/100; pnpm lint, typecheck, quality:static, and git diff --check passed.
@unbraind

Copy link
Copy Markdown
Owner Author

Review fix pushed in f1252cb. Re-running review agents on the current head.

@codex review
/gemini review
@gemini-code-assist
@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 27, 2026 •

Copy link
Copy Markdown

@unbraind Re-running the full review on the current head, including the fix from f1252cbe.

✅ 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 refactors the CLI commands to eliminate duplicate logic by introducing shared helpers. Specifically, it extracts legacy none/null collection token normalization and runtime schema unset field resolution into shared modules, and single-sources the config key type definition. Unit tests have been updated to align with these changes. The feedback suggests adding defensive checks to the shared unset field resolution helper to prevent potential runtime TypeErrors if dynamically loaded registry fields are missing expected properties.

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/cli/commands/shared-unset-fields.ts Outdated
Comment on lines +160 to +167
const candidates = new Set<string>([
definition.key,
definition.metadata_key,
definition.cli_flag.replaceAll("-", "_"),
definition.cli_flag,
...definition.cli_aliases.map((alias) => alias.replaceAll("-", "_")),
...definition.cli_aliases,
]);

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

Defensive checks should be added when resolving runtime unset field definitions. If cli_flag or cli_aliases are missing or undefined in the registry (which is loaded dynamically), calling replaceAll or map directly on them will throw a runtime TypeError. Adding safe guards ensures robustness at dynamic boundaries.

    const candidates = new Set<string>();
    if (definition.key) candidates.add(definition.key);
    if (definition.metadata_key) candidates.add(definition.metadata_key);
    if (definition.cli_flag) {
      candidates.add(definition.cli_flag.replaceAll("-", "_"));
      candidates.add(definition.cli_flag);
    }
    if (Array.isArray(definition.cli_aliases)) {
      for (const alias of definition.cli_aliases) {
        candidates.add(alias.replaceAll("-", "_"));
        candidates.add(alias);
      }
    }
References
  1. Do not remove runtime type checks or defensive guards at dynamic import boundaries (such as loading an SDK or module from a configured path), even if the expected module is statically typed. Statically typed shapes do not guarantee actual runtime exports for dynamically imported modules, and runtime validation prevents failures further down the execution path.

@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 `@src/cli/commands/shared-unset-fields.ts`:
- Around line 160-168: The candidate matching in shared-unset-fields.ts is
inconsistent because `definition.key` is compared without the same normalization
applied to `parseCreateUnsetTargets()` and `parseUpdateUnsetTargets()`. Update
the candidate set construction in the helper that builds `candidates` so
`definition.key` is normalized the same way as the lower-cased `--unset` token
(for example, matching the existing lowercase/underscore handling used for
`metadata_key`, `cli_flag`, and `cli_aliases`), ensuring runtime keys like
`githubUrl` still match their `--unset` input.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 66d83cf4-0b19-4793-a6d4-2b640cdfb4a3

📥 Commits

Reviewing files that changed from the base of the PR and between 82dbbe8 and f1252cb.

📒 Files selected for processing (11)
  • .agents/pm/epics/pm-92if.toon
  • .agents/pm/history/pm-92if.jsonl
  • src/cli/commands/config.ts
  • src/cli/commands/create.ts
  • src/cli/commands/legacy-none-tokens.ts
  • src/cli/commands/shared-unset-fields.ts
  • src/cli/commands/update.ts
  • src/core/config/positional-value.ts
  • tests/unit/commands/create-command.spec.ts
  • tests/unit/commands/legacy-none-tokens.spec.ts
  • tests/unit/commands/update-command.spec.ts

Comment thread src/cli/commands/shared-unset-fields.ts Resolved

@cubic-dev-ai cubic-dev-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.

1 issue found across 9 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread src/cli/commands/shared-unset-fields.ts Resolved
Harden shared runtime unset field resolution against malformed dynamic registry metadata before matching create/update unset tokens.

Document Gemini review evidence on pm-92if and add malformed alias coverage to preserve full branch coverage.
@unbraind

Copy link
Copy Markdown
Owner Author

Gemini review fix pushed in 86899ee. Re-running review agents on current head.

@codex review
/gemini review
@gemini-code-assist
@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 27, 2026 •

Copy link
Copy Markdown

@unbraind Re-running the full review on the current head, including the fix from 86899ee1.

✅ 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 refactors and centralizes shared CLI command helpers to improve code quality and reduce duplication. Specifically, it exports the canonical ConfigKey type from positional-value.ts to eliminate duplicate definitions in config.ts. It also extracts and centralizes legacy none/null collection normalization into legacy-none-tokens.ts and runtime unset field resolution into shared-unset-fields.ts, reusing these helpers across both the create and update commands. Unit tests have been updated and expanded to cover these centralized helpers, including handling of malformed payloads. 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.

Normalize runtime unset lookup tokens and candidate forms so camelCase dynamic field keys still match lowercased --unset input.

Record current-head review evidence on pm-92if and cover the lowercased key lookup path.
@unbraind

Copy link
Copy Markdown
Owner Author

Runtime unset normalization fix pushed in a800b3f. Re-running review agents on current head.

@codex review
/gemini review
@gemini-code-assist
@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 27, 2026 •

Copy link
Copy Markdown

@unbraind I'll rerun the full review on the current head, including the runtime unset normalization fix from a800b3f6.

✅ 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 refactors and consolidates duplicate code across the create and update commands by introducing shared helpers for normalizing legacy none/null collection tokens and resolving runtime --unset field definitions. Additionally, it single-sources the canonical ConfigKey type definition from positional-value.ts to be reused in config.ts, and updates unit tests to cover these shared behaviors. There are no review comments, so no further feedback is provided.

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.

@unbraind
unbraind merged commit a4ba57b into main Jun 27, 2026
19 checks passed
@unbraind
unbraind deleted the chore/reduce-static-duplicate-chunks branch June 27, 2026 18:52
@unbraind

unbraind commented Aug 4, 2026

Copy link
Copy Markdown
Owner Author

PM context: pm-92if. Historical linkage verified during the 2026-08-04 ecosystem review. This comment records canonical context only; it does not reopen or change this artifact.

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