Skip to content

Agent self-repair: pm health remediation_map + pm validate --fix-hints - #111

Merged
unbraind merged 3 commits into
mainfrom
agent-self-repair-remediation-registry-20260604
Jun 5, 2026
Merged

unbraind merged 3 commits into
mainfrom
agent-self-repair-remediation-registry-20260604

Conversation

@unbraind

@unbraind unbraind commented Jun 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds machine-executable remediation so coding agents gating on pm health / pm validate can auto-repair findings without hardcoding warning-code → fix-command mappings. A single shared registry (src/core/diagnostics/remediation.ts) is the source of truth, consumed by both commands.

This closes a tightly-related cluster under the Observability/health epic (pm-5oj5) and Schema/config epic (pm-8jdc):

Item Type What shipped
pm-sz6w Story (umbrella) Agent self-repair via machine-executable remediation
pm-0hnu Feature pm health --json per-check remediation_map
pm-6m3y Feature pm validate --fix-hints
pm-7tcw Chore Surface settings_read_* warnings proactively

What changed

1. Shared remediation registry (keystone)

src/core/diagnostics/remediation.ts maps each stable warning-code prefix → an executable pm fix command (e.g. history_drift_missing_stream → pm history-repair <id>, vectorization_stale_items_remaining → pm health --refresh-vectors). First-match resolution (codes are disjoint under colon-boundary matching). Coverage-gated at 100%.

2. pm health --json → per-check remediation_map (pm-0hnu)

Every non-extension check whose warnings have a known code gains details.remediation_map (code → fix command). Present in default/--full, omitted in --brief/--summary (token-efficient). Extension checks keep their richer details.triage.remediation.

3. pm validate --fix-hints (pm-6m3y)

New read-only flag adds a uniform details.fix_hints[] of executable pm commands to each failing check. The resolution check aliases its existing per-row commands; other checks derive one generic command per warning code from the registry. Never mutates items.

4. Settings-read warning surfacing (pm-7tcw)

readSettings() silently fell back to defaults on a corrupt settings.json. Now main.ts threads settings_read_* warnings through the discovery snapshot (zero extra reads) and prints them once per command to stderr — create/list/search/… all report settings_read_invalid_schema / settings_read_invalid_json instead of silently using defaults. stdout/--json output is unaffected.

Wiring

register-operations · cli-contracts (VALIDATE_FLAG_CONTRACTS + validate schema contract) · tool-parameter-tables (MCP) · bash/zsh/fish completions · docs (COMMANDS / AGENT_GUIDE / CONFIGURATION).

Testing

  • Registry unit spec (tests/integration/remediation-registry.spec.ts) — 100% lines/branches/functions.
  • Command-level tests merged into existing health-command / validate-command / cli-main-errors specs (unit dir is at its 120-file cap).
  • Full suite: 2301 passed, coverage 100%.
  • Manually verified end-to-end in a temp tracker: health remediation_map (history drift), brief-mode omission, validate --fix-hints (generic + resolution aliasing), and stderr surfacing for both corrupt-settings cases with clean stdout.

🤖 Generated with Claude Code

Summary by Sourcery

Introduce a shared remediation registry to provide machine-executable fix commands for health and validate diagnostics, expose them via new CLI surfaces, and surface settings-read issues proactively while keeping JSON output clean.

New Features:

  • Add a centralized remediation registry that maps diagnostic warning codes to executable pm fix commands for use across commands.
  • Expose per-check remediation maps in pm health --json output for non-extension checks to support automated self-repair workflows.
  • Add a pm validate --fix-hints flag that attaches executable fix_hints commands to failing checks without mutating any items.

Enhancements:

  • Thread settings-read warnings through the CLI runtime and emit actionable settings_read_* messages once per command on stderr while preserving clean stdout/JSON output.
  • Extend CLI flag registration, shell completion scripts, and SDK contracts to support the new validate --fix-hints option and remediation-aware tooling.
  • Document self-repair remediation behavior and usage in the command reference, agent guide, configuration docs, and changelog.

Tests:

  • Add integration coverage for the remediation registry and new health remediation_map behavior, ensuring 100% coverage on the registry module.
  • Extend validate, health, completion, and CLI main error tests to cover fix_hints, remediation_map emission/omission in different modes, and surfaced settings-read warnings.

…--fix-hints

Introduce a shared, machine-executable remediation registry
(src/core/diagnostics/remediation.ts) as the single source of truth mapping each
pm health / pm validate warning-code prefix to the executable pm command that
resolves it, so agents gating on these commands no longer hardcode warning-code
-> fix mappings.

- pm health --json (pm-0hnu): every non-extension check whose warnings have a
  known code gains details.remediation_map (code -> fix command). Default/--full
  include it; --brief/--summary omit it; extension checks keep their richer
  details.triage.remediation.
- pm validate --fix-hints (pm-6m3y): new read-only flag adds a uniform
  details.fix_hints[] of executable pm commands per failing check. The resolution
  check aliases its existing per-row commands; other checks derive one generic
  command per warning code from the registry. Never mutates items.
- Settings-read warning surfacing (pm-7tcw): main.ts threads settings_read_*
  warnings through the discovery snapshot (zero extra reads) and prints them once
  per command to stderr, so create/list/search no longer silently fall back to
  defaults on a corrupt settings.json (stdout/--json stays clean).

Closes pm-sz6w (umbrella story), pm-0hnu, pm-6m3y, pm-7tcw.

Wiring: register-operations, cli-contracts (VALIDATE_FLAG_CONTRACTS + validate
schema contract), tool-parameter-tables, bash/zsh/fish completions, and docs
(COMMANDS/AGENT_GUIDE/CONFIGURATION). The registry module is coverage-gated at
100% (tests/integration/remediation-registry.spec.ts); command-level tests merged
into existing health/validate/cli-main specs. Full suite 2301 passed, coverage 100%.

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

sourcery-ai Bot commented Jun 4, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Adds a shared remediation registry powering machine-executable fix commands for pm health/validate, exposes them via health.remediation_map and validate --fix-hints, and surfaces settings-read warnings on stderr while keeping JSON/stdout clean, with full wiring through CLI options, contracts, completions, docs, tests, and coverage.

Sequence diagram for pm validate --fix-hints flow

sequenceDiagram
  actor User
  participant CLI as pm validate
  participant Run as runValidate
  participant Fix as attachValidateFixHints
  participant Reg as buildRemediationCommands
  participant RegData as REMEDIATION_REGISTRY

  User->>CLI: pm validate --fix-hints --json
  CLI->>Run: runValidate({ fixHints: true, ... })
  loop per built check
    Run->>Fix: attachValidateFixHints(check, warnings)
    alt resolution check with per-row hints
      Fix-->>Run: details.fix_hints from missing_resolution_remediation_hints
    else other checks
      Fix->>Reg: buildRemediationCommands(warnings)
      Reg->>RegData: resolveRemediation(warning)
      Reg-->>Fix: ["pm ...", ...]
      Fix-->>Run: details.fix_hints updated
    end
  end
  Run-->>CLI: checks with details.fix_hints
  CLI-->>User: JSON result
Loading

Sequence diagram for surfacing settings_read warnings on stderr

sequenceDiagram
  actor User
  participant CLI as pm <command>
  participant Snap as loadRuntimeExtensionDiscoverySnapshot
  participant Read as readSettingsWithMetadata
  participant Maybe as maybeLoadRuntimeExtensions
  participant Emit as emitSettingsReadWarnings
  participant Err as printError

  User->>CLI: pm create / list / search ...
  CLI->>Snap: loadRuntimeExtensionDiscoverySnapshot(pmRoot)
  Snap->>Read: readSettingsWithMetadata(pmRoot)
  Read-->>Snap: { settings, warnings }
  Snap-->>CLI: discoverySnapshot{ settingsReadWarnings }
  CLI->>Maybe: maybeLoadRuntimeExtensions(command, globalOptions)
  Maybe->>Emit: emitSettingsReadWarnings(settingsReadWarnings)
  loop each warning starting with settings_read_
    Emit->>Err: printError("[pm] warning: ...")
  end
  Maybe-->>CLI: runtime extension result
  CLI-->>User: stdout / --json output (unchanged)
Loading

File-Level Changes

Change Details Files
Introduce shared remediation registry and helpers for mapping warning codes to fix commands.
  • Add RemediationEntry model and immutable REMEDIATION_REGISTRY for non-extension health/validate warning codes.
  • Implement resolveRemediation, buildRemediationMap, and buildRemediationCommands utilities for warning→command resolution and deduping.
  • Include remediation module in coverage configuration and add integration spec for registry behavior.
src/core/diagnostics/remediation.ts
vitest.config.ts
tests/integration/remediation-registry.spec.ts
Enhance pm health JSON output with per-check remediation_map derived from registry.
  • Collect per-check warning sources for core health checks (settings, directories, telemetry, integrity, history_drift, vectorization).
  • Build remediation_map from normalized warnings using registry and attach to check.details only when non-empty.
  • Keep remediation_map only in full/default projections; maintain existing triage.remediation for extension checks.
  • Expand tests to cover remediation_map presence/omission for relevant checks and brief mode.
src/cli/commands/health.ts
tests/unit/health-command.spec.ts
Add pm validate --fix-hints to expose executable remediation commands per failing check.
  • Extend ValidateCommandOptions and CLI wiring to accept boolean fixHints and pass into runValidate.
  • Add attachValidateFixHints helper to enrich ValidateCheck.details.fix_hints from existing resolution hints or registry-derived commands.
  • Refactor check construction in runValidate to funnel through a record helper that conditionally attaches fix_hints and aggregates warnings.
  • Update tool contracts, parameter metadata, and shell completions to include the new flag.
  • Add unit tests validating fix_hints behavior when enabled, aliasing of resolution remediation hints, and absence when not requested.
src/cli/commands/validate.ts
src/cli/register-operations.ts
src/sdk/cli-contracts.ts
src/sdk/cli-contracts/tool-parameter-tables.ts
src/cli/commands/completion.ts
tests/unit/validate-command.spec.ts
tests/unit/completion-command.spec.ts
Surface settings.json read/parse warnings once per command on stderr without affecting stdout/JSON.
  • Extend runtime discovery snapshot to carry settingsReadWarnings from readSettingsWithMetadata instead of readSettings.
  • Introduce emitSettingsReadWarnings to print actionable settings_read_* warnings to stderr with remediation guidance.
  • Invoke emitSettingsReadWarnings during extension loading so all commands (list/create/search/etc.) surface corrupt settings.json warnings.
  • Document behavior and add CLI tests ensuring stderr surfacing and clean stdout JSON for list --json.
src/cli/main.ts
docs/CONFIGURATION.md
tests/unit/cli-main-errors.spec.ts
Update docs, changelog, completions, and contracts to reflect new remediation capabilities.
  • Document validate --fix-hints semantics and Self-Repair Remediation behavior for pm health/validate in COMMANDS and AGENT_GUIDE.
  • Record new features and chores in CHANGELOG with relevant plan/feature links.
  • Wire fix-hints flag into bash/zsh/fish shell completion scripts with descriptions.
  • Update internal .toon/plan/history artifacts associated with remediation and settings warnings work items.
docs/COMMANDS.md
docs/AGENT_GUIDE.md
CHANGELOG.md
src/cli/commands/completion.ts
.agents/pm/chores/pm-7tcw.toon
.agents/pm/features/pm-0hnu.toon
.agents/pm/features/pm-6m3y.toon
.agents/pm/history/pm-0hnu.jsonl
.agents/pm/history/pm-6m3y.jsonl
.agents/pm/history/pm-7tcw.jsonl
.agents/pm/history/pm-kcba.jsonl
.agents/pm/history/pm-sz6w.jsonl
.agents/pm/issues/pm-kcba.toon
.agents/pm/stories/pm-sz6w.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

@unbraind

unbraind commented Jun 4, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@unbraind

unbraind commented Jun 4, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@coderabbitai

coderabbitai Bot commented Jun 4, 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 19 minutes and 35 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ 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: 0b29756f-eb8a-4ab1-bf0f-309339942485

📥 Commits

Reviewing files that changed from the base of the PR and between c651b26 and e80d49a.

📒 Files selected for processing (8)
  • .agents/pm/features/pm-6m3y.toon
  • .agents/pm/history/pm-6m3y.jsonl
  • docs/COMMANDS.md
  • src/cli/commands/validate.ts
  • src/cli/main.ts
  • src/core/diagnostics/remediation.ts
  • tests/integration/remediation-registry.spec.ts
  • tests/unit/cli-main-errors.spec.ts
📝 Walkthrough

Walkthrough

This PR delivers machine-executable remediation commands across pm health --json (via details.remediation_map) and pm validate --fix-hints (via details.fix_hints[]), sourced from a shared remediation registry. It also surfaces settings_read_* warnings to stderr once per command and closes related implementation tasks.

Changes

Remediation Delivery Feature

Layer / File(s) Summary
Shared remediation registry and helpers
src/core/diagnostics/remediation.ts, tests/integration/remediation-registry.spec.ts
New module exports RemediationEntry interface, frozen REMEDIATION_REGISTRY mapping warning-code prefixes to executable pm commands with summaries, and helper functions: resolveRemediation (exact/prefix match), buildRemediationMap (deduped code-to-command map), buildRemediationCommands (ordered command list). Integration tests validate registry consistency, resolver edge cases, and map/command construction.
Health command remediation integration
src/cli/commands/health.ts, tests/unit/health-command.spec.ts
pm health augments non-extension checks (settings, directories, settings_values, telemetry, integrity, history_drift, vectorization) with per-check details.remediation_map in full mode; remediation map is omitted from brief/summary projections. Unit tests assert remediation maps for settings_values and history_drift checks.
Validate command --fix-hints support
src/cli/commands/validate.ts, tests/unit/validate-command.spec.ts
ValidateCommandOptions adds optional fixHints?: boolean flag. runValidate refactored through shared record helper to conditionally attach per-check fix_hints[] arrays: reuses existing resolution remediation hints or derives commands from warning codes via buildRemediationCommands. Unit tests verify fix_hints presence/content for metadata and resolution checks, and absence when flag not requested.
Validate CLI wiring and contracts
src/cli/register-operations.ts, src/sdk/cli-contracts.ts, src/sdk/cli-contracts/tool-parameter-tables.ts, src/cli/commands/completion.ts
Wire --fix-hints flag through register-operations into runValidate; add flag contract, validate action schema parameter, and tool-parameter properties/metadata; update bash, zsh, fish completion to advertise --fix-hints for validate command.
Settings-read warnings to stderr
src/cli/main.ts, tests/unit/cli-main-errors.spec.ts
CLI startup surfaces settings_read_* warnings (invalid JSON/schema) to stderr once per command before fast-path returns. Integration: read settings via readSettingsWithMetadata() during discovery, extend RuntimeExtensionDiscoverySnapshot with settingsReadWarnings, call emitSettingsReadWarnings() early in maybeLoadRuntimeExtensions(). Tests verify stderr warning output while keeping stdout clean and exit code successful.
Documentation and CHANGELOG
CHANGELOG.md, docs/AGENT_GUIDE.md, docs/COMMANDS.md, docs/CONFIGURATION.md, vitest.config.ts
CHANGELOG records new fix-hints and remediation-map features plus settings-read warning surfacing; AGENT_GUIDE explains retrieval from pm health --json and pm validate --fix-hints; COMMANDS documents --fix-hints behavior and details.remediation_map with example JSON; CONFIGURATION notes fallback and warning behavior; vitest config adds remediation.ts to coverage include.

Project Management Closure Records

Layer / File(s) Summary
Chore and feature closures
.agents/pm/chores/pm-7tcw.toon, .agents/pm/features/pm-0hnu.toon, .agents/pm/features/pm-6m3y.toon
Update pm-7tcw (settings-read warnings), pm-0hnu (health remediation_map), pm-6m3y (validate fix-hints) to closed status with resolution and expected/actual results reflecting shipped behavior.
Lifecycle events and commentary
.agents/pm/history/pm-0hnu.jsonl, .agents/pm/history/pm-6m3y.jsonl, .agents/pm/history/pm-7tcw.jsonl, .agents/pm/history/pm-sz6w.jsonl, .agents/pm/history/pm-kcba.jsonl
Append claim/update/close lifecycle events to pm-0hnu, pm-6m3y, pm-7tcw history; add multiple commentary entries to pm-sz6w describing registry implementation, test coverage, and documentation updates; add maintainer comment to pm-kcba about Sentry token blocker.
Issue and story updates
.agents/pm/issues/pm-kcba.toon, .agents/pm/stories/pm-sz6w.toon
Update pm-kcba with Sentry token blocker comment noting operational secret configuration required; close pm-sz6w story with resolution and expanded documentation/testing notes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • unbraind/pm-cli#37: Refactors pm health --brief/--summary projection; this PR builds on that work to omit details.remediation_map from brief/summary modes while including it in full output.
  • unbraind/pm-cli#54: Factors history_drift detection into scanHistoryDrift; this PR extends that to attach details.remediation_map for history_drift warning codes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% 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 directly and clearly summarizes the main changes: introducing machine-executable remediation for agent self-repair via pm health remediation_map and pm validate --fix-hints.
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 PR description clearly outlines the changes: shared remediation registry, pm health --json remediation_map, pm validate --fix-hints flag, and settings-read warning surfacing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

@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 3 issues, and left some high level feedback:

  • In emitSettingsReadWarnings you currently surface every settings_read_ warning, but the comment says only actionable ones should be shown while benign informational warnings stay quiet; consider explicitly whitelisting the actionable codes (e.g. settings_read_invalid_json, settings_read_invalid_schema, settings_read_merge_failed) instead of using a broad startsWith("settings_read_") filter.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `emitSettingsReadWarnings` you currently surface every `settings_read_` warning, but the comment says only actionable ones should be shown while benign informational warnings stay quiet; consider explicitly whitelisting the actionable codes (e.g. `settings_read_invalid_json`, `settings_read_invalid_schema`, `settings_read_merge_failed`) instead of using a broad `startsWith("settings_read_")` filter.

## Individual Comments

### Comment 1
<location path="src/cli/commands/validate.ts" line_range="646-653" />
<code_context>
+ * registry. Read-only: this only enriches the diagnostic output, never mutates
+ * any item.
+ */
+function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
+  const existingResolutionHints = check.details.missing_resolution_remediation_hints;
+  const fixHints =
+    Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
+      ? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
+      : buildRemediationCommands(checkWarnings);
+  if (fixHints.length > 0) {
+    check.details = { ...check.details, fix_hints: fixHints };
+  }
+}
</code_context>
<issue_to_address>
**issue:** Clarify that fix_hints entries may contain placeholders and are not always directly executable as-is.

The current JSDoc suggests `fix_hints` is a uniform, directly executable list of `pm` commands, but some values produced by `buildRemediationCommands` can contain `<id>` placeholders (as per `RemediationEntry.command`). This means they require substitution before execution. Please either adjust the wording to avoid "machine-executable" or ensure `fix_hints` only contains fully concrete commands so the documented contract matches the actual values.
</issue_to_address>

### Comment 2
<location path="src/cli/main.ts" line_range="1055-1058" />
<code_context>
+ */
+function emitSettingsReadWarnings(warnings: readonly string[]): void {
+  for (const warning of warnings) {
+    if (warning.startsWith("settings_read_")) {
+      printError(
+        `[pm] warning: ${warning} — settings.json could not be loaded and pm fell back to defaults; run pm health for remediation.`,
+      );
+    }
+  }
</code_context>
<issue_to_address>
**issue (bug_risk):** The implementation may surface non-actionable settings_read_* warnings despite the docstring promise.

The docstring promises that only actionable `settings_read_*` warnings are surfaced, but this code logs every `settings_read_*` warning. If upstream emits benign `settings_read_*` warnings, they will now appear too. Please either restrict the check to the specific actionable codes, or update the docstring to match this broader behavior so callers aren’t misled about what is suppressed.
</issue_to_address>

### Comment 3
<location path="src/core/diagnostics/remediation.ts" line_range="1" />
<code_context>
   return `pm update ${row.id} ${fieldArguments} --message \"Backfill resolution metadata\"`;
 }

+/**
+ * Attach a uniform, machine-executable `fix_hints` array (executable `pm`
+ * commands) to a validate check's details when `--fix-hints` is requested. The
</code_context>
<issue_to_address>
**suggestion (bug_risk):** The documented "longest-prefix precedence" is not enforced by resolveRemediation and relies on registry ordering.

The type comment for `code` promises longest-prefix precedence (e.g., `settings` vs `settings:id_prefix_empty`), but `resolveRemediation` just walks `REMEDIATION_REGISTRY` in order and returns the first match on equality or `<code>:`. Precedence is therefore determined by registry order, not prefix length. To avoid surprises when adding broader codes, either implement actual longest-prefix selection in `resolveRemediation` or update the comment to state that registry order defines precedence.
</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 src/cli/commands/validate.ts Resolved
Comment thread src/cli/main.ts
Comment on lines +1055 to +1058
if (warning.startsWith("settings_read_")) {
printError(
`[pm] warning: ${warning} — settings.json could not be loaded and pm fell back to defaults; run pm health for remediation.`,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): The implementation may surface non-actionable settings_read_* warnings despite the docstring promise.

The docstring promises that only actionable settings_read_* warnings are surfaced, but this code logs every settings_read_* warning. If upstream emits benign settings_read_* warnings, they will now appear too. Please either restrict the check to the specific actionable codes, or update the docstring to match this broader behavior so callers aren’t misled about what is suppressed.

Comment thread src/core/diagnostics/remediation.ts Resolved

@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 introduces a shared machine-executable remediation registry that maps warning codes to actionable pm commands, enabling automated self-repair. This registry is integrated into pm health --json as a remediation_map and into pm validate under a new --fix-hints flag. Additionally, the PR surfaces settings-read warnings proactively to stderr instead of silently falling back to defaults. The review feedback highlights two important improvements: ensuring that resolveRemediation correctly respects longest-prefix precedence rather than returning the first linear match, and using optional chaining on check.details in attachValidateFixHints to avoid potential runtime errors.

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 +297 to +305
export function resolveRemediation(warning: string): RemediationEntry | undefined {
const normalized = warning.trim();
for (const entry of REMEDIATION_REGISTRY) {
if (normalized === entry.code || normalized.startsWith(`${entry.code}:`)) {
return entry;
}
}
return undefined;
}

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 documented behavior of resolveRemediation specifies 'longest-prefix precedence so settings:id_prefix_empty can be more specific than a hypothetical bare settings entry.' However, the current implementation performs a simple linear scan and returns the first match. If a less specific prefix entry is placed earlier in the registry, it will shadow more specific entries. To guarantee longest-prefix matching regardless of registry order, we should find the matching entry with the longest code length.

export function resolveRemediation(warning: string): RemediationEntry | undefined {
  const normalized = warning.trim();
  let bestMatch: RemediationEntry | undefined = undefined;
  for (const entry of REMEDIATION_REGISTRY) {
    if (normalized === entry.code || normalized.startsWith(`${entry.code}:`)) {
      if (!bestMatch || entry.code.length > bestMatch.code.length) {
        bestMatch = entry;
      }
    }
  }
  return bestMatch;
}

Comment on lines +646 to +655
function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
const existingResolutionHints = check.details.missing_resolution_remediation_hints;
const fixHints =
Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
: buildRemediationCommands(checkWarnings);
if (fixHints.length > 0) {
check.details = { ...check.details, fix_hints: fixHints };
}
}

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

To prevent potential runtime errors, use optional chaining when accessing check.details since details can be undefined or null depending on the specific validation check implementation.

Suggested change
function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
const existingResolutionHints = check.details.missing_resolution_remediation_hints;
const fixHints =
Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
: buildRemediationCommands(checkWarnings);
if (fixHints.length > 0) {
check.details = { ...check.details, fix_hints: fixHints };
}
}
function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
const existingResolutionHints = check.details?.missing_resolution_remediation_hints;
const fixHints =
Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
: buildRemediationCommands(checkWarnings);
if (fixHints.length > 0) {
check.details = { ...check.details, fix_hints: fixHints };
}
}

@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 introduces a shared machine-executable remediation registry to provide uniform fix commands across pm health (via remediation_map) and pm validate (via the new --fix-hints flag), alongside proactive surfacing of settings-read warnings to stderr. The review feedback highlights three key areas for improvement: ensuring the resolveRemediation helper correctly implements the documented longest-prefix precedence rather than relying on array order, using optional chaining when accessing check.details to prevent potential runtime errors, and safely defaulting settingsReadWarnings to an empty array during destructuring.

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 +297 to +305
export function resolveRemediation(warning: string): RemediationEntry | undefined {
const normalized = warning.trim();
for (const entry of REMEDIATION_REGISTRY) {
if (normalized === entry.code || normalized.startsWith(`${entry.code}:`)) {
return entry;
}
}
return undefined;
}

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 comment above REMEDIATION_REGISTRY states that matching is resolved with longest-prefix precedence so that more specific codes (e.g., settings:id_prefix_empty) take precedence over hypothetical shorter prefix codes (e.g., settings). However, the current implementation simply returns the first match encountered in the array order. If a shorter prefix code is ever added earlier in the registry, it will incorrectly shadow the more specific one.

To guarantee the documented longest-prefix precedence regardless of array order, we should find the match with the longest code length.

export function resolveRemediation(warning: string): RemediationEntry | undefined {
  const normalized = warning.trim();
  let bestMatch: RemediationEntry | undefined = undefined;
  for (const entry of REMEDIATION_REGISTRY) {
    if (normalized === entry.code || normalized.startsWith(`${entry.code}:`)) {
      if (!bestMatch || entry.code.length > bestMatch.code.length) {
        bestMatch = entry;
      }
    }
  }
  return bestMatch;
}

Comment on lines +646 to +655
function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
const existingResolutionHints = check.details.missing_resolution_remediation_hints;
const fixHints =
Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
: buildRemediationCommands(checkWarnings);
if (fixHints.length > 0) {
check.details = { ...check.details, fix_hints: fixHints };
}
}

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

To ensure robust defensive programming, we should use optional chaining when accessing check.details as it could potentially be undefined or null in some contexts (e.g., custom or extension checks, or future refactors). This prevents a potential runtime TypeError when trying to read missing_resolution_remediation_hints.

Suggested change
function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
const existingResolutionHints = check.details.missing_resolution_remediation_hints;
const fixHints =
Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
: buildRemediationCommands(checkWarnings);
if (fixHints.length > 0) {
check.details = { ...check.details, fix_hints: fixHints };
}
}
function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
const existingResolutionHints = check.details?.missing_resolution_remediation_hints;
const fixHints =
Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
: buildRemediationCommands(checkWarnings);
if (fixHints.length > 0) {
check.details = { ...check.details, fix_hints: fixHints };
}
}

Comment thread src/cli/main.ts
try {
const startedAt = Date.now();
const settings = await readSettings(pmRoot);
const { settings, warnings: settingsReadWarnings } = await readSettingsWithMetadata(pmRoot);

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

If readSettingsWithMetadata ever returns an object where warnings is undefined or optional, spreading settingsReadWarnings at line 1118 will throw a runtime TypeError (since undefined is not iterable). Defaulting it to an empty array ensures safe execution.

Suggested change
const { settings, warnings: settingsReadWarnings } = await readSettingsWithMetadata(pmRoot);
const { settings, warnings: settingsReadWarnings = [] } = await readSettingsWithMetadata(pmRoot);

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c651b26875

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/core/diagnostics/remediation.ts Outdated
// --- pm validate: lifecycle ---
{
code: "validate_lifecycle_active_closure_like_metadata",
command: 'pm update <id> --resolution "none"',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Use a real clear command for resolution hints

When pm validate --fix-hints reports validate_lifecycle_active_closure_like_metadata for an active item with resolution set, this hint cannot be executed to fix it: pm update treats --resolution as a scalar setter and rejects the legacy clear token "none" via assertNoLegacyScalarToken, so the suggested self-repair command fails instead of clearing the field. The hint should use the supported clear path such as --unset resolution or otherwise point to a valid close/update flow.

Useful? React with 👍 / 👎.

Comment thread src/core/diagnostics/remediation.ts Outdated
},
{
code: "validate_lifecycle_stale_blockers",
command: 'pm update <id> --unblock-note "<resolution>"',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Clear blocker fields instead of setting unblock_note

For stale-blocker findings, this fix hint does not resolve the condition that buildLifecycleCheck reports: the validator only checks status, blocked_by, and blocked_reason, while --unblock-note writes the separate unblock_note metadata field. In cases like an active non-blocked item that still has blocked_by/blocked_reason, running the suggested command leaves the same warning on the next pm validate --check-stale-blockers --fix-hints run; the hint needs to clear or update the blocker fields/status that the validator actually inspects.

Useful? React with 👍 / 👎.

Comment thread src/core/diagnostics/remediation.ts Outdated
// --- pm validate: metadata ---
{
code: "validate_metadata_missing_author",
command: 'pm update <id> --author "<name>"',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not use audit author to backfill item author

For validate_metadata_missing_author, this fix hint does not update the missing item metadata: pm update --author is the mutation/audit author used for history entries, and the update path never writes document.metadata.author. When an item file is missing its author field, running the suggested command records a new audit entry but leaves the same validation warning, so the remediation needs a command that actually sets or restores the item author field.

Useful? React with 👍 / 👎.

Comment thread src/core/diagnostics/remediation.ts Outdated
// --- pm validate: command_references ---
{
code: "validate_command_references_stale_pm_ids",
command: "pm update <id> --body <corrected-body>",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Remediate stale linked-test references, not item bodies

validate_command_references_stale_pm_ids is raised from buildCommandReferencesCheck by scanning item.tests[].command, but this hint edits the item's body instead of the linked test entry. For an item whose linked test command references a deleted pm-* id, running pm update <id> --body ... leaves the stale test command unchanged, so pm validate --check-command-references --fix-hints reports the same warning again; the hint should route through the linked-test mutation path instead.

Useful? React with 👍 / 👎.

Comment thread src/core/diagnostics/remediation.ts Outdated
// --- pm validate: files ---
{
code: "validate_files_missing_linked_paths",
command: "pm files <id> --remove <path>",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Account for missing docs in linked-path remediation

The files validation check combines both item.files and item.docs before emitting validate_files_missing_linked_paths, but this hint only removes entries through pm files. When the missing path is a linked doc, pm files <id> --remove <path> cannot remove it from metadata.docs, so the warning remains on the next validation run; the remediation needs to either mention pm docs <id> --remove <path> or choose the command based on which artifact list contains the missing path.

Useful? React with 👍 / 👎.

Codex (P2) correctly found several remediation commands did not actually resolve
their findings; corrected each to the real fix path:

- validate_metadata_missing_author: removed — `pm update --author` sets the
  audit/history author, not metadata.author, and no CLI flag writes that field.
- validate_lifecycle_active_closure_like_metadata: --resolution "none" ->
  `pm update <id> --unset resolution` (the explicit clear path; the legacy
  "none" sentinel is deprecated in favor of --unset).
- validate_lifecycle_stale_blockers: --unblock-note ->
  `pm update <id> --unset blocked-by --unset blocked-reason` (the fields the
  lifecycle validator actually inspects).
- validate_command_references_stale_pm_ids: --body ->
  `pm update <id> --replace-tests --test ...` (the check scans tests[].command).
- validate_files_missing/orphaned_paths: summaries now cover the pm docs path
  (the files check spans both item.files and item.docs).

Doc/impl drift (Sourcery + Gemini):
- RemediationEntry.code JSDoc reworded: resolver is first-match over disjoint
  colon-boundary codes, not longest-prefix (no ordering dependency).
- attachValidateFixHints JSDoc notes generic hints are <id>/<field>/<path>
  templates, not always directly executable.
- emitSettingsReadWarnings docstring clarified (surfaces settings_read_* codes).

All corrected commands re-verified executable in a sandbox; full suite 2301
passed; remediation registry still 100% covered.

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

unbraind commented Jun 4, 2026

Copy link
Copy Markdown
Owner Author

Thanks for the thorough reviews — addressed in 37a7d85.

Codex (P2 — all valid; these commands didn't actually resolve their findings):

  • validate_metadata_missing_author: removed the entry. pm update --author is the audit/history author and no CLI flag writes metadata.author, so it couldn't backfill the field; a missing item author must be restored at the source.
  • validate_lifecycle_active_closure_like_metadata: --resolution "none" → pm update <id> --unset resolution (explicit clear path; legacy-none-tokens.ts deprecates the "none" sentinel in favor of --unset).
  • validate_lifecycle_stale_blockers: --unblock-note → pm update <id> --unset blocked-by --unset blocked-reason (the fields buildLifecycleCheck actually inspects).
  • validate_command_references_stale_pm_ids: --body → pm update <id> --replace-tests --test ... (the check scans tests[].command, not the body).
  • validate_files_missing/orphaned_paths: summaries now cover the pm docs path (the check spans both item.files and item.docs).

Sourcery / Gemini (doc-impl drift — valid):

  • RemediationEntry.code JSDoc reworded: the resolver is first-match over disjoint colon-boundary codes (no ordering dependency), not longest-prefix.
  • fix_hints JSDoc now notes generic hints are <id>/<field>/<path> templates, not always directly executable.
  • emitSettingsReadWarnings docstring clarified.

Gemini (defensive nits — no change, by design):

  • check.details optional chaining: ValidateCheck.details is typed Record<string, unknown> and is always constructed as an object literal by every check builder, so it is never null/undefined.
  • spreading settingsReadWarnings: readSettingsWithMetadata returns SettingsReadResult.warnings: string[] (non-optional), so the spread cannot throw.

All corrected commands re-verified executable in a sandbox; full suite 2301 passed; registry module still 100% covered.

@codex review
/gemini review

@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 introduces a shared machine-executable remediation registry to map diagnostic warning codes to actionable pm fix commands. This registry is integrated into pm health --json via a new remediation_map and into pm validate via a new --fix-hints flag. Additionally, settings-read warnings are now proactively surfaced to stderr once per command instead of silently falling back to defaults. The changes are supported by updated documentation, shell completions, SDK contracts, and new integration and unit tests. Feedback on the code changes suggests adding optional chaining when accessing check.details in validate.ts and implementing a defensive type check for the warning parameter in resolveRemediation to prevent potential runtime crashes.

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/validate.ts Outdated
* output, never mutates any item.
*/
function attachValidateFixHints(check: ValidateCheck, checkWarnings: string[]): void {
const existingResolutionHints = check.details.missing_resolution_remediation_hints;

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

To prevent potential runtime TypeError crashes if check.details is undefined or null, use optional chaining when accessing missing_resolution_remediation_hints.

Suggested change
const existingResolutionHints = check.details.missing_resolution_remediation_hints;
const existingResolutionHints = check.details?.missing_resolution_remediation_hints;

Comment on lines +304 to +305
export function resolveRemediation(warning: string): RemediationEntry | undefined {
const normalized = warning.trim();

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

To ensure robustness and prevent potential runtime crashes if warning is null, undefined, or not a string, add a defensive type check at the beginning of resolveRemediation.

export function resolveRemediation(warning: string): RemediationEntry | undefined {
  if (typeof warning !== "string") {
    return undefined;
  }
  const normalized = warning.trim();

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37a7d85976

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/core/diagnostics/remediation.ts Outdated
Comment on lines +210 to +212
code: "validate_metadata_custom_profile_missing_required_fields",
command: 'pm update <id> --<field> "<value>"',
summary: "Backfill the custom-profile required fields on the reported item(s).",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Point custom-profile hints at config

When pm validate --metadata-profile custom --fix-hints runs with no configured custom metadata fields, the validator emits validate_metadata_custom_profile_missing_required_fields because the validation settings are incomplete and it falls back to core fields (src/cli/commands/validate.ts); updating an item with --<field> cannot clear that configuration warning. I checked the config command registration, which supports config set metadata-required-fields --criterion ..., so this hint sends agents to mutate arbitrary items instead of fixing the setting that actually drives the finding.

Useful? React with 👍 / 👎.

Comment thread src/cli/main.ts
// Surface actionable settings-read warnings once per command, before the
// activation-skipped fast path returns, so create/list/search/... all report a
// corrupt settings.json instead of silently running on defaults.
emitSettingsReadWarnings(discoverySnapshot.settingsReadWarnings);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Surface settings warnings with no extensions

Because the new warning emission is only reached after the --no-extensions early return in maybeLoadRuntimeExtensions, commands such as pm --no-extensions list --json still read invalid settings through their normal command path and silently fall back to defaults. I checked runList, which still calls readSettings() directly, so users/agents that disable extensions—the common safe mode—miss the settings_read_invalid_* warning this change is meant to expose.

Useful? React with 👍 / 👎.

Comment thread src/core/diagnostics/remediation.ts Outdated
Comment on lines +222 to +223
code: "validate_lifecycle_active_closure_like_metadata",
command: "pm update <id> --unset resolution",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Match the closure-like field in the fix hint

For validate_lifecycle_active_closure_like_metadata, buildLifecycleCheck reports whichever active-item field matched the configured closure-like patterns (blocked_reason, resolution, or actual_result), but this generic hint always unsets only resolution. When the row is, for example, pm-123:actual_result or pm-123:blocked_reason, running the advertised command leaves the same lifecycle warning in place.

Useful? React with 👍 / 👎.

Comment on lines +233 to +234
code: "validate_lifecycle_stale_blockers",
command: "pm update <id> --unset blocked-by --unset blocked-reason",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not clear blockers for missing blocker context

This code also backs validate_lifecycle_stale_blockers, but buildLifecycleCheck uses that same warning when a blocked item has neither blocked_by nor blocked_reason (blocked_status_missing_blocker_context). In that scenario both fields are already absent, so --unset blocked-by --unset blocked-reason is a no-op and the fix hint cannot clear the finding; it needs to either set blocker context or move the item out of a blocked status.

Useful? React with 👍 / 👎.

Comment thread src/core/diagnostics/remediation.ts Outdated
Comment on lines +250 to +251
code: "validate_files_missing_linked_paths",
command: "pm files <id> --remove <path>",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Unlink missing docs with the docs command

buildFilesCheck raises validate_files_missing_linked_paths across both item.files and item.docs, so a missing doc-only link gets the same warning code. For those rows this advertised pm files <id> --remove <path> command won't remove the doc entry, leaving validation failing; the remediation needs to distinguish docs or point agents to pm docs <id> --remove <path> when the missing link came from docs.

Useful? React with 👍 / 👎.

Comment thread docs/COMMANDS.md Outdated

Use dry-run modes before broad lifecycle or cleanup changes.

`--fix-hints` is a read-only flag: each failing check gains `details.fix_hints`, an array of executable `pm` commands derived from the warning codes it raised (for example `pm history-repair <id>` for history drift, or `pm update <id> --author "<name>"` for missing metadata). It never mutates items. The mapping comes from the shared remediation registry that also backs `pm health --json` (see Self-Repair Remediation below), so agents gating on `pm validate` can auto-repair findings without hardcoding warning-code-to-command lookups.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Remove the stale author fix example

This new documentation still tells agents that missing metadata can be fixed with pm update <id> --author "<name>", but the registry intentionally removed that remediation because --author changes the audit/history author rather than metadata.author. Following this example for a missing-author validation warning records an audit mutation without clearing the finding.

Useful? React with 👍 / 👎.

Comment thread src/core/diagnostics/remediation.ts Outdated
Comment on lines +289 to +290
code: "validate_command_references_stale_pm_ids",
command: 'pm update <id> --replace-tests --test "command=<corrected-command>"',

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve tests when correcting one stale reference

For validate_command_references_stale_pm_ids, this hint uses pm update <id> --replace-tests --test ...; I checked runUpdate, and --replace-tests replaces the entire linked-test array with only the provided --test entries. If an item has multiple linked tests and only one command references a stale PM id, running this remediation drops every unrelated linked test on that item while fixing the stale one.

Useful? React with 👍 / 👎.

Comment thread src/cli/commands/validate.ts Outdated
Comment on lines +649 to +653
const existingResolutionHints = check.details.missing_resolution_remediation_hints;
const fixHints =
Array.isArray(existingResolutionHints) && existingResolutionHints.length > 0
? existingResolutionHints.filter((hint): hint is string => typeof hint === "string")
: buildRemediationCommands(checkWarnings);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include all resolution fixes or mark them truncated

When more than five closed items are missing resolution metadata and --verbose-diagnostics is not set, buildResolutionCheck truncates missing_resolution_remediation_hints to the diagnostic summary limit before this alias copies it into fix_hints. The new fix_hints field has no truncation marker of its own, so an agent consuming the advertised uniform fix list will repair only the first few items and leave the remaining validation failures unresolved.

Useful? React with 👍 / 👎.

Codex re-review found 8 more cases where a hint did not reliably resolve its
finding; corrected each:

- validate_metadata_custom_profile_missing_required_fields: this is config-driven
  (the custom profile has no fields and falls back to core), so the hint now
  points at `pm config set metadata-required-fields --criterion ...` instead of
  mutating an item.
- validate_lifecycle_active_closure_like_metadata: the matched field varies
  (resolution / actual_result / blocked_reason), so the hint is now a
  `--unset <closure-like-field>` template the agent fills from the detail row.
- validate_lifecycle_stale_blockers: summary now also covers the
  blocked_status_missing_blocker_context case, where clearing the (already empty)
  blocker fields is a no-op and the fix is to add context or change status.
- validate_files_missing/orphaned_paths: one code spans item.files and item.docs,
  so the hint is now a `pm <files|docs> <id> ...` template.
- validate_command_references_stale_pm_ids: summary warns that --replace-tests
  overwrites the whole linked-test list, so unaffected tests must be re-included.
- main.ts: settings_read_* warnings were skipped under --no-extensions (the
  common safe mode) because emission sat after the early return; now surfaced
  there too via a single settings read on that path.
- validate --fix-hints: aliased resolution hints now propagate
  fix_hints_truncated so agents know the list is partial.
- docs/COMMANDS.md: removed the stale `pm update --author` example (the registry
  intentionally dropped that hint).

Also added a defensive non-string guard to resolveRemediation (exported helper
SDK consumers may call from untyped JS). Full suite 2303 passed; registry 100%.

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

unbraind commented Jun 4, 2026

Copy link
Copy Markdown
Owner Author

Round 2 — thanks again, Codex; all 8 follow-ups were valid and are fixed in e80d49a.

Codex (round 2):

  • validate_metadata_custom_profile_missing_required_fields: config-driven finding → hint now points at pm config set metadata-required-fields --criterion ... instead of mutating an item.
  • validate_lifecycle_active_closure_like_metadata: the matched field varies → pm update <id> --unset <closure-like-field> template (filled from the <id>:<field> detail row).
  • validate_lifecycle_stale_blockers: summary now covers the blocked_status_missing_blocker_context case where --unset is a no-op (add context / change status instead).
  • validate_files_missing/orphaned_paths: one code spans item.files + item.docs → pm <files|docs> <id> ... template.
  • validate_command_references_stale_pm_ids: summary warns --replace-tests overwrites the whole tests list (re-include unaffected tests).
  • --no-extensions gap: settings warnings were skipped before the early return; now surfaced on that path too (one read), verified with pm --no-extensions list.
  • --fix-hints: aliased resolution hints now set fix_hints_truncated when the list is summarized.
  • docs/COMMANDS.md: removed the stale pm update --author example (P3).

Gemini:

  • resolveRemediation now defensively returns undefined for non-string input (exported helper SDK consumers may call from untyped JS); check.details access uses optional chaining.
  • Re: longest-prefix — I'm keeping the resolver as first-match by design: registry codes are mutually exclusive under the colon-boundary rule (enforced by a dedicated test), so at most one entry ever matches and a longest-prefix tie-break would be an unreachable branch (it would fail the module's 100% branch-coverage gate). The doc now states this invariant explicitly.

All corrected commands re-verified executable in a sandbox; full suite 2303 passed; registry module 100% covered.

@codex review
/gemini review

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@unbraind

unbraind commented Jun 4, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@unbraind

unbraind commented Jun 4, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@unbraind

unbraind commented Jun 5, 2026

Copy link
Copy Markdown
Owner Author

Final status — ready to merge

All 13 CI checks pass on `e80d49aa`. Two substantive review rounds were fully addressed:

  • Round 1 (11 items): corrected 5 remediation commands that pointed at the wrong mechanism (Codex), fixed the longest-prefix doc/impl drift (Sourcery/Gemini), clarified placeholder/docstring wording.
  • Round 2 (8 items, Codex): config-driven custom-profile hint, per-field closure-like template, stale-blocker summary covering the missing-context case, pm <files|docs> template for the files/docs-spanning code, --replace-tests warning, --no-extensions settings-warning surfacing, fix_hints_truncated marker, and removed the stale --author doc example. Also added the resolveRemediation non-string guard + check.details optional chaining.

The currently-open review threads are GitHub re-anchoring the rounds 1–2 comments onto the latest commit — each is already resolved in HEAD (verified: non-string guard, optional chaining, --no-extensions emission, first-match doc, stale-blocker summary). On the one design point: the resolver stays first-match by design — registry codes are mutually exclusive under the colon-boundary rule (test-enforced), so a longest-prefix tie-break would be an unreachable branch that fails the module's 100% branch-coverage gate.

Every corrected command was re-verified executable in a sandbox; full suite 2303 passed; the remediation module is 100% covered.

@unbraind
unbraind merged commit 2f023b5 into main Jun 5, 2026
13 checks passed
@unbraind
unbraind deleted the agent-self-repair-remediation-registry-20260604 branch June 5, 2026 00:05
unbraind added a commit that referenced this pull request Jun 5, 2026
Filed as a follow-up from PR #111 dependency review; deferred from the feature
PR to avoid mixing a risky major dep bump into it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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