Repository navigation
fix(objectql): an unevaluable advisory validation rule logs one line saying the write proceeded, not two saying it was rejected - #25051
Conversation
…saying the write proceeded Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJ2vytxktPaEAmkEavY9KP
…e evaluator and on an engine write Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YJ2vytxktPaEAmkEavY9KP
…visory-unevaluable-log
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 18 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f646083565d6dc757a6f67ac3a112897d1074f5f && git checkout f646083565d6dc757a6f67ac3a112897d1074f5f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 157ebd008b07d0683ee1762d6c858f4ad766618e 2e42d14e43a2c36103e4857a3118b06141e73a63 && git checkout -B drift-repro 157ebd008b07d0683ee1762d6c858f4ad766618e && git merge --no-ff 2e42d14e43a2c36103e4857a3118b06141e73a63
node scripts/docs-audit/affected-docs.mjs --json 157ebd008b07d0683ee1762d6c858f4ad766618e
|
Contract reviewServed-tier: Scope: the net diff against the merge base (4 files, +289/−12):
Read with them:
① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 ·
|
| Item | Reading |
|---|---|
| Shape | Draft PR → main. The body opens Fixes #24782. 4 files, +289/−12. Not governed: check-governed-merges matched 0 of 4 paths; 301 changed lines. |
| Scope | An unevaluable warning / info validation rule logs one line: it names the rule, its severity and the fault, and says the write proceeded because the rule is advisory. Before, it logged two lines that both said the write was rejected. An unevaluable error rule is refused as before, and its one log line still says the write was rejected and the rule fails closed. |
| Seat's own read | The verb is chosen after severity: the loop sets advisory per rule, and failClosedLogger gives the evaluators no logger for an advisory rule. The loop then reports the rule once, through the seed fold or the per-write warn. No violation, refusal body, blocking decision or #22726 advisory event moves. |
| Review | TIER_DEFAULT. Record 6308469198 is PASS on this head. |
| Declaration | One changeset, @objectstack/objectql patch, with Clause-②: no (log text only: …) at a line start, as on the claim and the PR body. No export is added. |
| Cross-lane | None. Every changed file is in packages/objectql or .changeset/, and no existing pin moved. The checklist's knownGaps[1] for records-forms.validation-rule-type-matrix is a post-merge re-run by its own words; the seat points domain:devx to it at landing. |
| Gates | Dev at 2e42d14e4: 70 derived, 70 run, all exit 0, after a full build for the two that first exited 3. The new pins pass 8/8, and the engine-level pin passes (its file 22/22). In the ablation the 6 advisory pins go red and the 3 error controls stay green; restored to HEAD. objectql passed 8533 tests, typecheck exit 0, core test:repo 82 passed, full turbo build exit 0. |
| CI | At this read on 2e42d14e43: ci-failure GREEN, all 41 check-runs completed, none failed. git merge-tree against today's origin/main (63085c3806) is clean. |
Disposition:
open_questions: A, as the dev settled it. The loop words the line once, after severity, in the per-write line's existing shape.deviations: all accepted: no pin moved; the knownGaps line is left todomain:devx; the model-free trailers; the PR subscription is held by the seat; and the first build attempt's flag error, re-run clean.out_of_scope_findings: none.
Landing: ordinary queue (pr_ready + automerge_enable through the relay). Fixes #24782 then closes the card.
Generated by Claude Code
… apply, then the first os serve), and validation-rule-type-matrix drops its red-until-#24782 text - cli.fresh-install-migrate-apply-first (new, r1): on an empty database `os migrate apply --yes` writes the organization-ownership birth marker (sys_migration adr-0131-organization-ownership), and the first `os serve` reaches its serving line with no ownership refusal. Ships in 18.1.0; a run against 18.0.0 records the refusal as that release's known behaviour. Driven live at base 688052e. - records-forms.validation-rule-type-matrix (r6 -> r7): knownGaps[1] and acceptance[5]'s "Red until #24782 lands" removed after a verification re-run of acceptance[5] at base 688052e read as #25051 fixed it. The history rows carry the placeholder ref #PENDING-25054; the next commit swaps in this PR's number and changes nothing else. Claude-Session: https://claude.ai/code/session_01FM29ZqLTrnPNXxijioZwMp Co-authored-by: Claude <noreply@anthropic.com>
Fixes #24782
Clause-②: no (log text only: no response, status, code, outcome or export moves)
What changes
An unevaluable
severity: 'warning'/'info'validation rule never blocks the write (#4649 fails closed onerrorrules only). But the server logged two lines for it, and both said the write was rejected. Now it logs ONE line, written after the rule's severity is known:An unevaluable
errorrule is unchanged: the same400 VALIDATION_FAILEDbody, and its one log line still says— write rejected: a rule that cannot be evaluated fails closed, it is never skipped.Log-line census (
packages/objectql/src/validation/rule-validator.ts, before the edit, atd68aab6fde)Every line that says "write rejected" or "fails closed":
checkPredicatefail-closed warn ("predicate failed to evaluate … fails closed")evaluateRule, which theevaluateValidationRulesloop andcheckConditional's branch recursion callerrorrules onlycheckConditionalwhen-predicate twintraversalScopeRefusalwarn ("predicate / when-predicate could not be evaluated … write rejected")evaluateValidationRules((warning): ${violation.message})requiredWhenarm ("requiredWhen for 'X' failed to evaluate … write rejected")visibleWhentraversal refusal (~:3112)unevaluableRuleError/traversalScopeRefusalviolationmessageerrorrule returnsCallers:
evaluateValidationRulesis the one door for the engine's insert / update / multi-row update, the delete's reference-cleanup update (referentialClearRefusalruns insidecheckPredicate, so the same seam covers it), and the validate-only preview.requiredWhen/readonlyWhen/ optionvisibleWhenuse their own evaluators and are not touched.The seam
RuleContextgains a per-ruleadvisoryflag, set by theevaluateValidationRulesloop from the TOP-LEVEL rule's severity and inherited by aconditional's branch (its outer rule's severity governs). The predicate evaluators write their fail-closed sentence tofailClosedLogger(ctx): the write's logger for anerrorrule, none for an advisory one. The loop then reports an unevaluable advisory rule once withunevaluableAdvisoryMessage(rule, severity, the fault fromconstraint.fault, and the branch name when a branch faulted). No new export. The violation and itsmessageare untouched, so blocking, theValidationErrorbody and the #22726advisories(an unevaluable rule stays out) do not move.#13889 seed / boot aggregation: the hit still folds into the load's summary line, which keeps its shape. The text it folds is the advisory sentence, not the violation's "— write rejected." message. On that path the evaluator's per-row fail-closed line no longer prints for an advisory rule, so the summary is the only line.
Pin census
rule-fail-closed.test.ts:213: anerrorrule's warn matches/write rejected/, a stable part. Still green.:216(warning) asserts only that a warn was logged. Still green.rule-validator.option-visibility.test.ts,rule-validator.test.ts:380: field-level evaluators, untouched.advisory-boot-path.test.ts:316,advisory-write-answer.test.tsperWriteLine: exact per-write line for a MATCHING advisory rule. Unchanged, still green.docs/qa/platform-checklist/areas/records-forms.json::1984quotes a matching advisory's line (unchanged);:1992scores the absence of "write rejected" / "fails closed" (now holds).content/docs/,qa/: zero hits.packages/lint/src/validate-expressions.ts:3266(comment) andpackages/spec/src/migrations/entries/semantic/18.field-predicate-reference-traversal-refused.ts:59quote error-path / field-predicate text, still true.Verification (head
2e42d14e4,origin/mainmerged)rule-unevaluable-advisory-log.test.ts(8 tests, realevaluateValidationRuleswith a logger spy):warningandinfoscriptconditions, aconditionalwhen, and the traversal refusal each log exactly one line naming the rule, severity and fault, saying "write proceeded", with no "write rejected" / "fails closed". The write is not refused and returns[]advisories. The seed aggregation folds the advisory text with no per-row line. CONTROL: the same three slots aterrorthrowValidationError(rule_violation,constraint.reason: 'unevaluable', message "… — write rejected.") and log exactly one fail-closed line.advisory-write-answer.test.ts:createDataon a runtime object with the card's rule stores the row, answers nowarnings, and logs one advisory line.rule-validator.tsrestored tod68aab6fdeon disk (failClosedLoggercount 6 → 0). Six advisory pins went red, and the threeerrorcontrols plus the other 21 stayed green (Tests 6 failed | 24 passed (30)). Restored: blob51aa0041equal to HEAD's,git diff HEADempty.pnpm --filter @objectstack/objectql test: 453 files / 8533 tests passed (exit 0).typecheck: exit 0 (re-run after the merge).pnpm --filter @objectstack/core test:repo: 6 files / 82 tests passed. Fullturbo run buildon the merged head: 76 packages, exit 0.dispatch-gates.mjs --commands(no paths): 70 lines, all exit 0 on2e42d14e4;--ranreports "70 run, 0 NOT-MEASURED (a DERIVED zero)".Acceptance notes
docs/qa/platform-checklist/areas/records-forms.jsonrecords-forms.validation-rule-type-matrixknownGapssays "acceptance[5] stays red until objectql(validation): an unevaluable severity:'warning' rule logs "write rejected: … fails closed" twice while the write is accepted 201 and persists #24782 lands … When objectql(validation): an unevaluable severity:'warning' rule logs "write rejected: … fails closed" twice while the write is accepted 201 and persists #24782's fix merges, re-run the clause and drop this line". That edit belongs todomain:devxafter the merge and a clause re-run. It is not made here.Session:
session_01YJ2vytxktPaEAmkEavY9KP(os-dev, dispatched bydomain:engineseat 1).🤖 Generated with Claude Code
Generated by Claude Code