Skip to content

bug(review): bound STATUS loses committed-range selector after Pi restart, and baseRef input is unreachable from direct tool calls (4.3.0) #2379

Open

Description

@ManuelDevOps

Before submitting

  • I searched open and closed issues and did not find a report of this problem.
  • I reviewed this report and removed credentials, tokens, private paths, hostnames, and other sensitive data.

Problem

After a Pi restart, gentle_review status with the exact retained lineageId no longer resolves an open committed-range lineage. The facade keeps the lineage's committed-range selector only in memory (CandidateViewRegistry.projections), so after the restart STATUS falls back to the workspace current-changes projection: applicability: "unrelated", next_transition: collect / empty_candidate_base_ref_required, and repair counts lineages: 0. The native authority is intact (review-state.json: state: correction_required, selector base-diff with a full base_ref, 4 admitted role results).

The only workaround, passing {"baseRef":"<FULL_SHA>","committedOnly":true} to STATUS, is unreachable: the facade requires input to be a serialized string for every operation except START/ASSESS, and the tool-call layer always JSON-parses string values that look like JSON into objects. Same root class as #2375 (answer-consent) and #1573 (fixed for START/ASSESS only).

Steps to reproduce

  1. RDD on; START a committed-range lineage (baseRef + committedOnly: true) and drive it to correction_required with a targeted-validator slot pending.
  2. Restart Pi (e.g. to set GENTLE_PI_REVIEW_RELAY_PI_TIMEOUT_MS after a relay timeout).
  3. gentle_review status with lineageId (with or without workspaceRoot).
  4. gentle_review status with input {"baseRef":"<FULL_SHA>","committedOnly":true}.

Expected and actual behavior

Expected: STATUS reoffers the pending validator slot for the lineage, either because the facade restores the committed selector from native state or because STATUS accepts input as an object like START/ASSESS do.

Actual:

  • Step 3: applicability: "unrelated", workspace projection, empty_candidate_base_ref_required, no collect bindings.
  • Step 4, object form or with leading whitespace: Review controller input must be a string.
  • Step 4, escaped string: schema validation fails (must match pattern "^\s*\{").
  • inspect only offers a fresh whole-range START, which had already hit lens_context_budget_exceeded.

The lineage is stranded with no supported facade route. Nothing was mutated.

gentle-pi version

4.3.0 (gentle-ai 4.2.0, contract gentle-ai.review-integration/v2)

Pi version

Pi coding agent 1.3.0

Operating system

Windows (amd64)

Activity

  1. carlosmoradev commented on Oct 18, 2026

    @carlosmoradev
    Contributor

    Very sharp diagnosis, @ManuelDevOps.

    Two linked gaps confirmed

    1. In-memory projection loss across restarts: CandidateViewRegistry.projections is purely volatile memory. When Pi restarts during an open committed-range lineage, subsequent status calls lose the remembered base selector and fall back to the workspace current-changes projection, rendering the lineage as unrelated.
    2. Tool-call object coercion: In extensions/gentle-ai.ts:5567, parseReviewControllerParameters only serialized object inputs for START and ASSESS (bug(review): gentle_review START fails with "input: must be string" — escaped-string contract has no object→string coercion #1573). We just opened PR fix(review): coerce answer-consent object input to serialized string (#2375) #2387 to include answer-consent (bug(review): gentle_review answer-consent input is unreachable from direct tool calls (object never coerced to string) (4.3.0) #2375), and your report proves that STATUS (REVIEW_CONTROLLER_OPERATION.STATUS) needs the exact same object coercion so passing input: {"baseRef":"...","committedOnly":true} can reach the status probe rather than aborting with Review controller input must be a string.

    Path forward

    • Expanding serializeReviewJsonArgument in parseReviewControllerParameters to include STATUS will immediately unblock the workaround via tool calls.
    • A cleaner long-term recovery is having CandidateViewRegistry hydrate known lineage base selectors from the persisted native review-state.json on restart so manual re-specification is unnecessary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions