Skip to content

fix(core): Phase-1 validation must accept sourcePatch rule updates (#566)#596

Merged
laofahai merged 5 commits into
mainfrom
fix/sourcepatch-approval-validation
Jun 13, 2026
Merged

fix(core): Phase-1 validation must accept sourcePatch rule updates (#566)#596
laofahai merged 5 commits into
mainfrom
fix/sourcepatch-approval-validation

Conversation

@laofahai

@laofahai laofahai commented Jun 13, 2026

Copy link
Copy Markdown
Owner

What — a real end-to-end gap, found by live testing

Phase-1 proposal validation rejected a sourcePatch-carrying rule update as MISSING_DEFINITION, so a natural-language "say→change an existing code-condition rule's threshold" proposal could be resolved but never approved — the chain was silently broken at the approval gate.

How it was found

Live-walking the freshly-landed chat-assistant channel (#594): typed "把经理审批阈值改成 20000" → the SchemaProposalCard rendered correctly → clicked 批准 (Approve)Proposal validation failed. Cannot approve. The approve envelope showed:

Phase 1 failed — MISSING_DEFINITION:
  Change for "manager_approval_threshold" (update) is missing a definition

A sourcePatch change deliberately has no definition — it rewrites a single named constant in existing source, so the sourcePatch IS its specification. Phase-1 didn't know that and blocked it. #595's integration test passed because it set status: "approved" directly, bypassing the approve-validation — so green tests never caught this.

Fix

Phase-1 now skips the MISSING_DEFINITION requirement for a change carrying a sourcePatch, exactly like it already does for revert/delete. Safety is unchanged: the value is validated at assembly (isSafeValueLiteral, #593) and re-validated by the patcher at graduation (NOT FOUND / AMBIGUOUS / NO INITIALIZER throw, #591).

Tests

proposal-validation-phase1.test.ts gains a case asserting a definition-less sourcePatch rule-update passes Phase 1 (mirroring the existing revert carve-out test). 149 validation-suite tests green; bun run check + bun run typecheck clean.

This is the missing middle of #566 — the approve gate now accepts the governed sourcePatch draft, so resolve → approve → graduate completes end-to-end.

Related: #566, #591, #593, #595

Summary by CodeRabbit

  • Bug Fixes
    • Validation now correctly accepts rule updates containing source patches without separate definitions. Previously these were incorrectly rejected, preventing governed drafts from advancing through the approval process. This fix allows changes to pass the initial approval gate and benefits rule threshold update workflows.

)

A "say→change an existing code-condition rule's threshold" proposal carries a
definition-less change whose sourcePatch IS the spec. Phase-1 flagged it
MISSING_DEFINITION, so the NL rule-threshold update could be RESOLVED but never
APPROVED — the chain was silently broken at the approval gate (found by live
testing the chat-assistant approve flow; #595's integration test bypassed approve
by setting status directly).

Skip the MISSING_DEFINITION requirement for a change carrying a sourcePatch, like
revert/delete. The sourcePatch is value-validated at assembly (isSafeValueLiteral)
and re-validated by the patcher at graduation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@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 fixes Phase-1 proposal validation to prevent rejecting rule updates carrying a sourcePatch as MISSING_DEFINITION by skipping the definition requirement for these changes. The reviewer suggests adding basic static validation for the sourcePatch properties (such as filePath, constantName, and newValueLiteral) during Phase 1 to catch malformed proposals early, along with a corresponding test case to verify this validation.

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 packages/core/src/engine/validation-engine.ts Outdated
Comment thread packages/core/__tests__/proposal-validation-phase1.test.ts
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

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

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

⌛ How to resolve this issue?

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

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

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

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

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1f64611f-a5c3-441d-ad38-e60478ff5ec2

📥 Commits

Reviewing files that changed from the base of the PR and between 4607e49 and 9079bad.

📒 Files selected for processing (2)
  • packages/core/__tests__/proposal-validation-phase1.test.ts
  • packages/core/src/engine/validation-engine.ts
📝 Walkthrough

Walkthrough

Phase-1 proposal validation now exempts changes carrying sourcePatch from the MISSING_DEFINITION error check, allowing rule updates with source patches but no separate definition to pass validation. The fix includes a test case verifying the behavior and a changeset documenting the change.

Changes

sourcePatch Approval Validation

Layer / File(s) Summary
sourcePatch handling in Phase-1 validation with test and documentation
packages/core/src/engine/validation-engine.ts, packages/core/__tests__/proposal-validation-phase1.test.ts, .changeset/sourcepatch-approval-validation.md
validatePhase1 adds an early-exit condition to skip MISSING_DEFINITION validation when a proposal change includes sourcePatch, mirroring existing behavior for revert and delete operations. A new test case verifies that rule update changes with sourcePatch but no definition pass validation. The changeset documents the fix and its impact on chat-assistant approval flows.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • laofahai/linchkit#590: Adds the sourcePatch-based writer path for Phase-1 graduation, which this PR now enables by skipping the MISSING_DEFINITION validation check.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title precisely describes the main change: fixing Phase-1 validation to accept sourcePatch rule updates, with a clear reference to the issue number.
Description check ✅ Passed The description provides comprehensive context including the problem, discovery method, fix explanation, and test coverage; however, the Quality Gates checklist section is not completed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sourcepatch-approval-validation

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.

Comment thread packages/core/src/engine/validation-engine.ts Outdated
The MISSING_DEFINITION carve-out for sourcePatch changes skipped ALL Phase-1
validation, so a malformed sourcePatch (empty filePath/constantName/value) would
fail opaquely at graduation. Now validate the patch's own shape early and emit
INVALID_SOURCE_PATCH when incomplete (gemini). Adds a failing-case test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review summary — one finding.

The fix correctly resolves the live-testing regression: Phase 1 was blocking every NL rule-threshold update with MISSING_DEFINITION because the sourcePatch IS the specification for that change type, not a missing field. The test mirrors the existing revert carve-out test and the changeset note is accurate.

One narrowness issue flagged inline on line 205: the continue guard is target-agnostic and also fires when both sourcePatch and definition are present, silently skipping definition validation in that case. Suggested tightening: if (change.sourcePatch && !change.definition) continue;. Not exploitable with current production code, but the validator should not silently tolerate an internally inconsistent change.

Comment thread packages/core/src/engine/validation-engine.ts Outdated
laofahai and others added 2 commits June 13, 2026 23:27
…Patch (#596 review)

The carve-out's unconditional `continue` also skipped the target-specific
definition `switch` for a change that carries BOTH a sourcePatch AND a
definition (permitted by the type, not produced today) — silently accepting a
malformed definition. Narrow it: still validate the sourcePatch shape, but only
`continue` when there is NO definition; otherwise fall through to
validateRule()/validateEntity()/… so an accompanying definition is still
validated (claude).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`sourcePatch` is an optional field on the BASE ProposalChange, so the carve-out
previously let a crafted entity/action change carrying a sourcePatch (no
definition) bypass MISSING_DEFINITION and reach graduation with undefined
behaviour. Narrow the guard to `target === "rule" && operation === "update"` —
exactly the NL-threshold case it exists for; any other definition-less change
(including a non-rule sourcePatch) now correctly fails MISSING_DEFINITION
(claude). Adds a scope test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/core/src/engine/validation-engine.ts
@claude

claude Bot commented Jun 13, 2026

Copy link
Copy Markdown

Reviewed. One real bug found (inline comment on line 207 of validation-engine.ts): the sourcePatch carve-out is not guarded to operation === "update", so a create change carrying a valid-shaped sourcePatch and no definition silently passes Phase 1. Everything else — the logic flow, the malformed-patch validation, the continue placement to avoid double-reporting, and the new test coverage — looks correct.

@laofahai laofahai merged commit 9a2f4ad into main Jun 13, 2026
8 checks passed
@laofahai laofahai deleted the fix/sourcepatch-approval-validation branch June 13, 2026 15:34
// through to MISSING_DEFINITION like any other definition-less change.
if (change.sourcePatch && change.target === "rule" && change.operation === "update") {
const { filePath, constantName, newValueLiteral } = change.sourcePatch;
if (!filePath || !constantName || !newValueLiteral) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: whitespace-only field values bypass the INVALID_SOURCE_PATCH guard

!filePath is false for " " (non-empty string), so a sourcePatch whose filePath/constantName/newValueLiteral contains only whitespace silently passes Phase 1 and is emitted as a well-formed change. It will then fail opaquely at graduation — exactly the "failing opaquely during graduation" scenario the comment on line 205 says this check was added to prevent.

Suggested change
if (!filePath || !constantName || !newValueLiteral) {
if (!filePath.trim() || !constantName.trim() || !newValueLiteral.trim()) {

The same .trim() tightening should be applied in the test on line 366 (filePath: " ") to cover this path explicitly.

@claude

claude Bot commented Jun 13, 2026

Copy link
Copy Markdown

Overall the fix is correct and well-scoped. The Phase-1 carve-out is tightly constrained to rule+update+sourcePatch, the fall-through when definition is also present is intentional and documented, and the three new test cases cover the happy path, the non-rule guard, and the empty-string shape check.

One real bug filed inline: the !filePath / !constantName / !newValueLiteral guards use plain falsiness, so a whitespace-only string (e.g. filePath: " ") slips through Phase 1 as valid, only failing opaquely at graduation — exactly the scenario the new early-check comment says it was added to prevent. A .trim() on each field and a matching whitespace test case would close the gap.

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