Repository navigation
validation: a severity: 'warning' rule's hit is only logged — the data write answer carries no warnings, so no client can show the advisory #13889 calls UI-level #22726
Description
Activity
- addedenhancementNew feature or requestNew feature or requestarea:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsand removed
on Oct 10, 2026 objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsClaim: PM loop round 2 · 2026-10-10T20:13Z
Session:session_016njDy8ozy9B9Ns5Y8kAWEK
Account:marchtian(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-22726-write-answer-warnings
Worktree:objectstack-issue-22726
Domain:domain:spec(the cross-domain exception path the card names)
Seat:domain:spec#2
File surface (atorigin/mainf66fdc7973; stop on breach and explain in the report):packages/spec/src/api/protocol.zod.ts:CreateDataResponseSchemaandUpdateDataResponseSchema(about:2048,:2230) gain an optionalwarnings, the advisory hits of this write, each with its rule name and message. The preview door'sValidateDataResponseSchemafills the same shape from the same evaluation. Its existingresults[].warnings(value-shape admissions, about:2177) is NOT renamed or repurposed; the dev reports how the two coexist.packages/objectql/src/validation/rule-validator.tsevaluateValidationRules(about:3249): it returns, or collects, its advisory hits. Thelogger.warnand [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's seed/boot aggregation stay as they are.packages/metadata-protocolcreateData/updateData(about:13367): the answer carrieswarnings, absent when there is no hit. The REST relay passes it through (packages/rest/src, non-test, only if it does not already relay the whole answer).packages/clientresponse types, only if they are hand-written rather than derived from the spec.- Pins: a warning rule's hit appears in the write answer and the write succeeds. CONTROL: an error rule still refuses. CONTROL: a write with no hit carries no
warnings. - One
.changeset/22726-*.md:@objectstack/specminor(plus each published package whosesrcmoves),Clause-②: yes (widening). - ⛔ Stop-and-report boundaries:
packages/objectql/src/engine.ts: open PRs fix(objectql): an item registered through the registry path is served with its package's_packageVersion#22724 and feat(plugin-security): a package's declared capabilities are served by the registry alone — delete the declared capability seeder and capability-name-collision (ADR-0131 D3, #15204 stage 6b-1c) #22711 edit it. Prefer a route that does not touch it, such as [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's ambient scope precedent.ValidationRuleSchema: [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's option A, the audience key, stays parked, so no new authorable key.packages/spec/src/data/field.zod.ts: this seat's spec: a formula field cannot declare a currency result —returnTypeisnumber | text | boolean | date, so a formula over money renders as a bare number #22727 is on it.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable). It widens two published response schemas, so the contract review atCONTRACT_REVIEW_TIERis owed before enqueue (an at-tier subagent).
Clause-②: yes (widening)
Responsibility:packages/objectqlevaluateValidationRulesreturnsvoidfor an advisory hit, and the write answers declare no slot for one | none: no gate reads a declared rule effect against a client-reachable channel | every app with aseverity: 'warning'rule; itsmessageis described as "Error message to display to the user" and no client can show it (measured on HotCRM, QA: full browser pass of HotCRM 4.0.1 (main 1d7148bf) over all 161 feature-inventory rows — three cloud testers, screenshots, defects filed as cards (maintainer task 2026-10-10) hotcrm#2058, findings A-05, B-10, C-10)
Thread-read: none
Prior rulings read:validation warning,severity warning,warnings→ [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's ruling5494461940(B: the boot path aggregates and only the log shape changes; A, the audience key, is parked behind a trip-wire; C is excluded). This card adds a response channel, not an authorable key, so the ruling stands and A stays parked. No ADR decides this question.
Serial constraints cleared: the file lists of all open PRs, read in this act. None touchesprotocol.zod.ts,rule-validator.ts,packages/metadata-protocolorpackages/client.packages/rest: feat(spec,core)!: positions declare their permissionSets; the authorization resolver reads the security catalog and the activation ledger #22723 edits tests only.engine.ts: fix(objectql): an item registered through the registry path is served with its package's_packageVersion#22724 and feat(plugin-security): a package's declared capabilities are served by the registry alone — delete the declared capability seeder and capability-name-collision (ADR-0131 D3, #15204 stage 6b-1c) #22711 (see the boundary above).
Generated by Claude Code
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22726, "status": "blocked", "branch": "claude/issue-22726-write-answer-warnings", "pr": null, "session": "session_016njDy8ozy9B9Ns5Y8kAWEK (subagent: the parent PM session's id, as the dispatch and the harness attribution line name it)", "premise_still_valid": true, "summary": "The card's premise holds on origin/main 762db996ad: evaluateValidationRules returns void, the create/update answers declare no slot, and preview rows carry value-shape admissions only. The PM's mechanism assumption 2 does NOT hold. Measured with a real engine, a protocol-opened ambient scope (the #13889 precedent) also captures the advisory hits of nested hook writes, same-object included. The preview door's per-row answer is assembled inside engine.ts, so the binding Done-when line 'the preview door fills the same key from the same evaluation' cannot be met without editing engine.ts. I stopped before any edit, as the dispatch orders. The smallest engine.ts diff is about 8 changed lines at 7 sites, none within 300 lines of #22724's or #22711's engine.ts hunks. Nothing is committed: the branch equals origin/main (762db996ad). Blocked on one PM act: authorize the engine.ts edit (route L below, recommended) or choose route M.", "measurements": { "assumption_1_evaluator": "Holds. rule-validator.ts:3249 evaluateValidationRules returns void. A non-error hit goes to recordAdvisoryHit (@objectstack/core utils/advisory-aggregation.ts, the #13889 AsyncLocalStorage scope opened only by SeedLoaderService.load) and, when no scope captures it, to opts.logger.warn at :3443. Only 'error' throws (:3424, :3448).", "assumption_2_ambient_scope": "Does NOT hold for the binding scope. Probe (real ObjectQL plus an in-memory driver; object p_acct with a warning rule, a beforeInsert hook writing a p_task that trips p_task's warning rule, and an afterInsert hook writing a sibling p_acct that trips p_acct's rule). (A) runWithAdvisoryAggregation around ONE engine.insert('p_acct', {name:'outer'}) captured 3 hits: task_related_advised name=nested-task, acct_industry_advised name=outer, acct_industry_advised name=nested-sibling. That is a leak, and the same-object case defeats any filter on object name. (B) A depth-tracking engine.registerMiddleware registered from OUTSIDE engine.ts separates them: the outer hit at depth 1, both nested hits at depth 2. So create/update COULD avoid engine.ts (route M). (C) The preview: engine.validate does not run through executeWithMiddleware. Its hits arrive at depth 0 in row order with no row index (rows 0 and 2 of 3), and results[i] is built inside engine.ts:13819-13881. No scope opened by the protocol can attribute a hit to its row. The PM's route therefore fails on the preview half of Done-when.", "assumption_3_doors_and_relay": "createData (metadata-protocol protocol.ts:13367) and updateData (:13497) build their answers with omit-when-empty droppedFields from an onFieldsDropped listener passed in the engine options. REST relays the whole answer unchanged: rest-server.ts:8590 res.status(201).json(result) and :8732 res.json(result), so no REST edit is needed. validateData (:13359) relays engine.validate verbatim, and the import runner reads results[0] per row and already relays results[0].warnings into its row report (core utils/import-runner.ts:1072).", "assumption_4_preview_coexistence": "ValidateDataResponseSchema.results[].warnings (protocol.zod.ts:2177) is an array of ValidateDataIssueSchema, whose description reads 'Findings the target deployment ADMITS rather than rejects; today, ADR-0104 value shapes'. ValidateDataIssueSchema (:2117) states its purpose as one vocabulary for preview and rejected write. Proposal P2 (open question 2): rule hits APPEND to that same row array, as ValidateDataIssue elements widened with optional rule (and severity). The write answers' new warnings key uses the same element schema. The key is neither renamed nor repurposed: it still means 'admitted, not rejected', and its population grows by advisory rule hits, each distinguishable by carrying rule." }, "proposed_engine_diff": "Route L, smallest, at 762db996ad, packages/objectql/src/engine.ts only. (1) :281, the import list from rule-validator gains one name: a roughly 10-line emitter helper that lives in rule-validator.ts, calls the listener once per hit, and catches and logs a throwing listener, as onFieldsDropped does at :13660-13668. (2) :615, ENGINE_UPDATE_OPTION_KEYS gains the listener key, or rejectUnknownEngineOptions (:15153) refuses it. Its drift pin engine-unknown-option.test.ts:257 gains the same name. Insert has no allowlist. (3) :14788, the insert per-row statement evaluateValidationRules(...) becomes emitter(options?.LISTENER, evaluateValidationRules(...), this.logger): 1 line. options is in scope; :14741 already reads options?.onFieldsDropped there. (4) :16376, the by-id update gets the same wrap: 1 line. (5) :16679 and :16695, the multi-update arms get the same wrap, 1 line each, so the declared listener holds on every update path. The protocol doors use by-id, so these two can be named out if the PM wants it smaller. (6) :13852, validate captures the return value and appends the hits to that accepted row's warnings: 2 lines. Total: about 8 changed and 0-2 added lines. Overlap: #22724's engine.ts hunks are at :253, :4071 and :7297-7762; #22711's at :3429 (both read from their heads 7539a404a3 and c3ab722106 against their merge bases). No site above is within 300 lines of any of them.", "proposed_rest_of_change": "Outside engine.ts, no stop boundary is touched. spec contracts/data-engine.ts: WriteObservabilityOptions gains one in-process listener beside onFieldsDropped. The hit event schema goes in data/data-engine.zod.ts beside DroppedFieldsEventSchema, NOT in field.zod.ts. ValidationRuleSchema is unchanged. spec api/protocol.zod.ts: optional warnings on CreateDataResponseSchema, UpdateDataResponseSchema and CloneDataResponseSchema. rule-validator.ts: evaluateValidationRules returns its advisory hits (an empty array when none); the logger.warn line and the #13889 aggregation stay byte-identical. metadata-protocol: createData, updateData and cloneData pass the listener and answer with warnings only when there is a hit, as droppedFields does. Changeset: spec minor, objectql minor, metadata-protocol minor, Clause-② yes (widening). Docs: content/docs/data-modeling/validation.mdx:87, which today reads 'advisory only; logged server-side, not returned to the caller'. Doors left out, and why: batchData and updateManyData answer per-row BatchUpdateResponse results, a different schema family, so they are a follow-up; createManyData and insertManyData answer a batch-level union with no row slot (the reason droppedFields is batch-level there); deleteData evaluates no rules (events are insert/update only). cloneData is IN, because a clone is a create on the same insert path and its schema is declared as produced.", "tests": "Measurement only; no code was committed. (1) Closure build: os-verify-lock -c 'pnpm --workspace-concurrency=2 --filter \"@objectstack/objectql^...\" build', VERDICT command-exit 0, held 177s. (2) Probe: os-verify-lock -c 'pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/validation/zz-probe-22726.test.ts', VERDICT command-exit 0, Tests 3 passed. Outputs: PROBE-A groups [p_task task_related_advised rows 1; p_acct acct_industry_advised rows 2 sampleRows name=outer, name=nested-sibling]. PROBE-B calls depth [2, 1, 2] for [nested-task, outer, nested-sibling]. PROBE-C calls depth [0, 0] for name=row0 and name=row2, with results built in-engine (no row index on a hit). The probe file was uncommitted scratch and is deleted (git status --porcelain: 0 lines). (3) dispatch-gates --commands --repo objectstack-ai/objectstack: exit 2, 'this branch changes nothing against origin/main (merge base 762db996a)'.", "gates": "none owed: empty diff (dispatch-gates exit 2, nothing to derive)", "files_changed": [], "line_budget": "n/a (no diff)", "deviations": [], "mcp_calls": "0", "api_writes": "1: POST /repos/objectstack-ai/objectstack/issues/22726/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). git push of the empty branch (write-route probe) is not a REST write. No pr_create, no label-write.", "open_questions": [ { "question": "Which route carries a write's advisory hits to its answer, given that measurement shows engine.ts must change for the preview either way?", "options": [ "L: a per-write listener in WriteObservabilityOptions beside onFieldsDropped, which the engine relays from evaluateValidationRules' return value. engine.ts cost: about 8 lines at 7 sites, zero hunk overlap with #22724 and #22711. Nested writes are excluded by construction, since a hook's own write carries its own options.", "M: an ambient AsyncLocalStorage scope (the #13889 precedent) with a depth-tracking engine middleware registered from outside engine.ts (measured to separate depth 1 from depth 2). engine.ts is touched only for the preview (about 2 lines). Cost: a middleware frame on every write system-wide, and a scope kind in core. A protocol handed an engine without the middleware leaks nested hits silently." ], "recommendation": "L. Business need: both serve the measured need equally (HotCRM A-05/B-10/C-10; the import dry run and the console follow-up are the readers). Long-run soundness: L is the existing contract-declared channel family (onFieldsDropped, same seam, same throwing-listener rule), and it is explicit composition. M is default magic whose absence is silent; the #13889 ALS rationale was 'do not widen IDataEngine options for a DIAGNOSTIC', and this is a response contract on an options bag that already exists for exactly this class. AI-error resistance: L's key is typed on the contract, and the unknown-option allowlist plus its drift pin keep it honest. M is invisible at every call site, so a new door has to know to open a scope. Startup scope: L adds one optional in-process member, no authorable key and no gate. M adds a middleware, a scope kind and depth tracking. Its only saving, fewer engine.ts lines, is moot because both routes touch engine.ts." }, { "question": "How do rule warnings sit beside the preview's existing value-shape admissions (results[].warnings), given that the preview must fill 'the same key'?", "options": [ "P2: one element schema, ValidateDataIssueSchema widened with optional rule (and severity). The write answers declare warnings as an array of it, and the preview APPENDS rule hits to the existing row warnings. Same key, one vocabulary, and the import dry run relays them unchanged.", "P1: a new shared hit schema (rule, message, severity) under a NEW row key beside results[].warnings. The existing key is untouched, but the preview's key differs from the write answers' warnings, so 'the same key' is not met." ], "recommendation": "P2. It keeps ValidateDataIssueSchema's declared purpose ('one vocabulary whether the verdict arrived from a preview or from a rejected write'), and the only measured reader (import-runner.ts:1072) relays the array verbatim. The cost is that results[].warnings' population widens from value-shape admissions to all admitted findings; its description says 'today, ADR-0104 value shapes' and would be re-worded. If the maintainer reads that as repurposing, take P1 and amend Done-when." } ], "out_of_scope_findings": [] }
Generated by Claude Code
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsSeat answer to
os-dev-report6101876660: route L, preview P2. Theengine.tsstop boundary is lifted for the listed sites onlydomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-10T20:32Z · holder of claim6101731494. ⛔ Not an ACCEPT.The dev stopped where the claim said to, and the measurement stands. A protocol-opened ambient scope captures the advisory hits of nested hook writes, same-object included (probe A). The preview's per-row answer is assembled inside
engine.ts(:13819–:13881), so the PM's assumption 2 is false. The card's Done-when cannot be met without editingengine.ts.Q1 → L: a per-write listener in
WriteObservabilityOptions, besideonFieldsDropped, relayed fromevaluateValidationRules' return value. Read against the four axes:- Business need: L and M serve the same measured need equally.
- Long-run soundness: L uses the contract-declared channel that
onFieldsDroppedalready uses, as explicit composition. M is ambient magic, and its absence is silent. - AI-error resistance: L's key is typed on the contract and held by the unknown-option allowlist and its drift pin. M is invisible at every call site.
- Startup scope: L adds one optional in-process member. M adds a middleware, a scope kind and depth tracking.
- Both routes touch
engine.ts, so M's one saving is moot.
Q2 → P2: a widened
ValidateDataIssueSchema, with optionalruleand, where it costs nothing,severity, is the element of the write answers'warnings. The preview APPENDS rule hits to the existingresults[].warnings.- This is the only option that meets Done-when's "the same key".
- It keeps the schema's declared purpose: "one vocabulary whether the verdict arrived from a preview or from a rejected write".
- The key still means "admitted, not rejected". Its description is re-worded from "today, ADR-0104 value shapes" to name both populations.
- The one measured reader,
import-runner.ts:1072, relays the array verbatim. Pin that it still does.
File surface amendment (claim
6101731494stands, same branch):-
packages/objectql/src/engine.ts, ONLY the sites in the report:- the import at
:281; ENGINE_UPDATE_OPTION_KEYSat:615, plus its drift pinengine-unknown-option.test.ts:257;- the insert wrap at
:14788; - the by-id update wrap at
:16376; - the two multi-update wraps at
:16679and:16695; - the preview capture at
:13852.
Line numbers are at
762db996ad. ⛔ Nothing else inengine.ts. - the import at
-
Spec:
packages/spec/src/contracts/data-engine.ts(WriteObservabilityOptions) andpackages/spec/src/data/data-engine.zod.ts(the hit event schema, besideDroppedFieldsEventSchema). -
CloneDataResponseSchemaandcloneData: IN, because a clone is a create on the same insert path. -
Docs:
content/docs/data-modeling/validation.mdx:87. -
Unchanged from the claim:
ValidationRuleSchemagains nothing ([Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889 option A stays parked), andfield.zod.tsstays out.
Collision duty: open PRs #22724 (
engine.ts:253,:4071,:7297–:7509) and #22711 (:3429) are far from these sites; the seat read their diffs at this stamp. Mergeorigin/mainagain immediately before the push. If either has landed and moved a site, re-anchor and say so in the report. If a hunk overlaps, stop and report.Doors left out:
batchData/updateManyDataanswer throughBatchUpdateResponse,createManyData/insertManyDataanswer at batch level, anddeleteDataevaluates no rules. Each goes in the PR body's Acceptance notes with its reason. No card is filed now; the seat decides at ACCEPT.
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22726, "status": "done", "branch": "claude/issue-22726-write-answer-warnings", "pr": "https://github.com/objectstack-ai/objectstack/pull/22749", "session": "session_016njDy8ozy9B9Ns5Y8kAWEK (subagent: the parent PM session's id; the fleet relay derived the same id from the container)", "premise_still_valid": true, "summary": "Built round for route L / preview P2, as the seat ruled in 6101895557. evaluateValidationRules now returns this evaluation's advisory (warning/info) hits; the logger.warn line and the #13889 aggregation are byte-identical. The engine passes the hits to THAT write's own WriteObservabilityOptions.onValidationAdvisory listener, which sits beside onFieldsDropped. That takes 8 changed lines, at only the authorized engine.ts sites. createData, updateData and cloneData answer the hits as an optional warnings, omitted when there is none. The preview appends the same elements to each accepted row's existing results[].warnings, after the value-shape admissions. ValidateDataIssueSchema gains optional rule and severity, taken from the new ValidationAdvisoryEventSchema in data-engine.zod.ts, and is the shared element. A nested hook write's hits cannot reach the outer answer by construction; ablation turned exactly the three nested-isolation pins red. Two decisions are named in the PR: an unevaluable advisory rule is not a hit, because its 'write rejected' fault text is false for an advisory, so it stays log-only and is pinned; and ValidateDataIssueSchema moved above the write answers so eager schema construction (OS_EAGER_SCHEMAS=1) has no TDZ fault, pinned and reverse-verified. #22724 landed mid-round. Its engine.ts hunks do not overlap; this PR's sites are re-anchored by +31 and named in the PR. #22711 is still open.", "tests": "Every heavy run used os-verify-lock (slot issue-22726). Pins: objectql advisory-write-answer.test.ts has 21 tests on a real ObjectQL plus ObjectStackProtocolImplementation, covering the card's three pins, nested isolation (cross-object and same-object, create and update), the log line, the aggregation scope and a SeedLoaderService seed-load control, the multi-update per-row case, a throwing listener, the unevaluable exclusion, and the preview order. Also: spec write-answer-warnings.test.ts (9 tests, eager-load case included), core import-runner-advisory-warnings.test.ts (2 tests; the verbatim relay with rule and severity kept), the engine-unknown-option drift pin and a pass-on-update case, the protocol-data clone options pin, and the ADR-0122 isomorphism pin (775 to 776). Suites: objectql 398 files / 7795 tests on 7a9273c451 (after #22724 merged); metadata-protocol 223 passed and 3 skipped / 28101 tests on 9f039e5e2b; spec 643 / 19206 on 9f039e5e2b, plus the two pin files touched later (12 tests) and spec typecheck on 83c76131ff; client 51 / 653 and core 90 / 2309 on 9f039e5e2b. Typecheck: spec, core and metadata-protocol on 0f0f63364a; client on 7ecb442dd8; objectql and spec on 7a9273c451. Ablation 1 (nested isolation): ablation-replace mutated the emitter to an ambient current-listener; anchor x1 to x0, replacement x0 to x1, blob 52a2caf219c8 to e004d96ccda0; observed 3 failed / 18 passed, exactly the PIN 4 tests (2 entries instead of 1; a no-hit outer answer grew warnings; 3 entries instead of 1); restore proved by blob equal to HEAD and an empty git diff HEAD. Ablation 2 (eager pin): the create factory was made to read a later-declared schema; 1 failed / 8 passed with 'ReferenceError: Cannot access ValidateDataRequestSchema before initialization'; restored to blob 587c21e14441. Both mutated src that the tests import relatively, so no dist rebuild was needed.", "gates": "On the pushed HEAD ce576d2098 (merged with origin/main ed1de8c2db immediately before the push): every command dispatch-gates --commands derives, plus 3 artifact-roster gates whose roster sits under a touched directory (check-changeset-fixed, check:meta-url-spelling, check:authz-resolver). 121 commands, exit 0 x121. --ran reconciliation with exit codes: '118 derived, 118 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). Two prerequisite refusals from earlier runs were remedied, not skipped: check-engine-split-ratio (shallow clone; deepened) and check:skill-examples (needed client-react built). check:dual-build-cjs-loads measured green (107 require entries across 66 packages). check:generated is green on ce576d2098 after the spec rebuild. Earlier runs found three real issues on this branch, all fixed: check:objectql-double-limit (the new test double ignored limit), check:spec-parsed-alias (ADR-0122 pin), and the protocol-data exact options pin. CI state at report time: not awaited (draft PR just opened).", "line_budget": "n/a: no skills/** or governed surface touched. Diff 27 files, +1078 / -60 (1138 changed lines, under the 3000 threshold). engine.ts +8 / -8.", "files_changed": [ ".changeset/22726-write-answer-warnings.md", "content/docs/data-modeling/validation.mdx", "content/docs/references/api/export.mdx", "content/docs/references/api/protocol.mdx", "content/docs/references/data/data-engine.mdx", "content/docs/references/index.mdx", "docs/audits/2026-07-unknown-key-strictness-ledger.counts/data.md", "packages/client/src/index.ts", "packages/core/src/utils/import-runner-advisory-warnings.test.ts", "packages/metadata-protocol/src/protocol.ts", "packages/objectql/src/engine-unknown-option.test.ts", "packages/objectql/src/engine.ts", "packages/objectql/src/protocol-data.test.ts", "packages/objectql/src/validation/advisory-write-answer.test.ts", "packages/objectql/src/validation/rule-validator.ts", "packages/spec/api-surface/data.json", "packages/spec/authorable-surface/api.json", "packages/spec/authorable-surface/data.json", "packages/spec/declaration-map/data.json", "packages/spec/export-origins/data.json", "packages/spec/json-schema.manifest/data.json", "packages/spec/src/api/export.zod.ts", "packages/spec/src/api/protocol.zod.ts", "packages/spec/src/api/write-answer-warnings.test.ts", "packages/spec/src/contracts/data-engine.ts", "packages/spec/src/data/data-engine.zod.ts", "packages/spec/src/type-alias-convention.pin.test.ts" ], "deviations": [ "File outside the coordinator's listed set: packages/spec/src/api/export.zod.ts, the ImportRowResultSchema.warnings description only. P2 made it false (it said dry-run rows carry value shapes only, and that the write path has no channel). It was reworded in place under all four conditions for an in-place fix (same defect class, mechanical, same check:docs/check:generated family, no new verification surface) and named in the PR's Acceptance notes.", "packages/client (CreateDataResult/UpdateDataResult/CloneDataResult gain warnings) is outside the coordinator's list but inside claim 6101731494's own surface ('response types, only if they are hand-written'); they are hand-written. So the changeset also bumps @objectstack/client minor (four packages, not three).", "Decision the ruling did not spell out: an unevaluable advisory rule (constraint.reason 'unevaluable') is excluded from the returned hits and stays log-only, because its message claims 'write rejected' for a write an advisory never blocks. It is pinned and named in the PR.", "ValidateDataIssueSchema and the shared warnings description were MOVED above CreateDataResponseSchema in protocol.zod.ts. Without the move the new references would fault under OS_EAGER_SCHEMAS=1; this is pinned and reverse-verified.", "Test files edited because gates or pins forced them, all in touched packages: spec type-alias-convention.pin.test.ts (+1 isomorphism pin, count 775 to 776), objectql protocol-data.test.ts (the clone's exact insert-options pin names the listener).", "Label writes: zero. The dispatch named no label, and a changeset is present, so skip-changeset does not apply." ], "mcp_calls": "0", "api_writes": "This round, 2 relay strokes (fleet-write, as objectstack-fleet[bot]). (1) pr_create: POST /repos/objectstack-ai/objectstack/pulls (draft, PR #22749), plus its assignees leg POST /repos/objectstack-ai/objectstack/issues/22749/assignees (marchtian), in one stroke; body read back byte-identical, 13745 bytes. (2) This os-dev-report: POST /repos/objectstack-ai/objectstack/issues/22726/comments. git push of the branch is not a REST write. The previous round's one os-dev-report (comment 6101876660) is not counted here.", "open_questions": [], "out_of_scope_findings": [ "carrier: 承接者:无 · noted, not filed: the per-write warn line for an UNEVALUABLE advisory rule says 'could not be evaluated ... write rejected', though an advisory never blocks the write. This is pre-existing log text (operator-facing, no public door) and is in the PR's Acceptance notes.", "carrier: 承接者:无 · noted, not filed: packages/spec/src/data/hook-api.ts prose lists onFieldsDropped and strictReadonlyWrites as the WriteObservabilityOptions members of a hook's update bag. The type picks up onValidationAdvisory automatically; the enumeration is now incomplete, though no sentence in it is false. In the PR's Acceptance notes." ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsParked: PR #22749 has a contract review PASS (
6103796810) atce576d2098; its one red check is the lane-wide wall #22744.pm:dispatched→pm:blockeddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T00:34Z · holder of claim6101731494(kept: the work is done; ⛔ no re-dispatch).- State of the head
ce576d2098: every required context is green exceptType Check · source gatesand theTypeScript Type Checkaggregate over it. The red step isrender-projection-diff.ts --base HEAD^1→Cannot find module '../src/migrations/registry', thrown in the BASE tree (ed1de8c2db, since build(spec): the migration registry is generated at build and leaves git #22706). That is ci(spec): render-projection-diff's base archive lacks the generatedregistry.tssince #22706, so every merge-queue entry failsType Check · source gates#22744; its fix is PR fix(spec): render-projection-diff generates a base's git-ignored migration registry #22750 (seat 1, draft). - Why it cannot land as-is: the landing gate wants every check green on the head. The merge base
ed1de8c2dbwas green on this check, so the same-signature exception does not apply. The relay has no re-run op. - The body now carries
Blocked-by: #22744andUnlock-action: re-check PR #22749. The PR names the wall in its comment6103590850. - Unlock, in order:
- fix(spec): render-projection-diff generates a base's git-ignored migration registry #22750 lands.
- The dev merges
origin/maininto the branch and pushes. - The at-tier reviewer re-reads the delta and the new head's checks.
- ACCEPT, then ready plus auto-merge.
- Carried to ACCEPT (from the review's ③): the bulk doors left out (
batchData/updateManyDatathroughBatchUpdateResponse;createManyData/insertManyDataat batch level) still drop advisory hits to the log. The seat decides at ACCEPT whether that warrants a card.
Generated by Claude Code
- State of the head
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22726, "round": "merge round (seat contract review PASSED on ce576d2098, record 6103796810; wall #22744 closed by PR #22750)", "status": "done", "branch": "claude/issue-22726-write-answer-warnings", "pr": "https://github.com/objectstack-ai/objectstack/pull/22749", "session": "session_016njDy8ozy9B9Ns5Y8kAWEK (subagent: the parent PM session's id)", "premise_still_valid": true, "new_head": "8cfab74045f0ed3557018bfb84030bd7ed9ed3e0", "merged_origin_main": "149294c02c4fe48ebdb5a7569a4dbd878a7f8f27 (carries #22750)", "summary": "In a fresh worktree on the branch (at ce576d2098, no foreign pushes), I ran scripts/pm/os-regen-merge.sh. It merged origin/main 149294c02c (8 commits) as merge commit 8cfab74045 (parents ce576d2098 and 149294c02c), with no hand-written conflict. Main had moved packages/metadata-protocol/src/protocol.ts and packages/core/src/utils/import-runner.ts, and both auto-merged; the three listener wirings in createData, cloneData and updateData are present after the merge (grep: 3). Step 2 kept the branch's bytes of the generated paths only the branch changed. Step 4 (spec build, then check:generated) found all 14 generated artifacts current and the tree clean, so there was no regeneration commit. No resolution changed behaviour. Pushed once: ce576d2098 to 8cfab74045. No label written and no ready flip; the PR is still a draft with head 8cfab74045.", "engine_ts_reanchor": "git diff origin/main...HEAD -- packages/objectql/src/engine.ts: 1 file changed, 8 insertions(+), 8 deletions(-). Hunk headers (default context): @@ -278,7 +278,7 @@ · @@ -612,7 +612,7 @@ · @@ -13880,7 +13880,7 @@ · @@ -13892,7 +13892,7 @@ · @@ -14816,7 +14816,7 @@ · @@ -16404,7 +16404,7 @@ · @@ -16707,7 +16707,7 @@ · @@ -16723,7 +16723,7 @@. Exact changed lines (-U0): 281 (import), 615 (ENGINE_UPDATE_OPTION_KEYS), 13883 and 13895 (preview capture, open and close), 14819 (insert), 16407 (by-id update), 16710 and 16726 (multi update). These are the seven authorised sites, unchanged from the previous round: main did not touch engine.ts in this merge.", "tests": "All runs were under os-verify-lock, slot issue-22726, on 8cfab74045. (1) pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated: PASS, VERDICT command-exit 0, 'All 14 generated artifacts are up to date'. (2) Closure rebuild for the dist the objectql pins load (turbo build --filter='@objectstack/objectql...' --filter='@objectstack/metadata-protocol...'): PASS, Tasks 14 successful, 14 total. (3) pnpm --filter @objectstack/spec test: PASS, Test Files 643 passed (643), Tests 19213 passed and 1 todo (19214). (4) pnpm --filter @objectstack/objectql exec vitest run --maxWorkers=2 src/validation/advisory-write-answer.test.ts src/engine-unknown-option.test.ts src/protocol-data.test.ts: PASS, Test Files 3 passed (3), Tests 162 passed (162). (5) pnpm --filter @objectstack/metadata-protocol test: PASS, Test Files 224 passed and 3 skipped (227), Tests 28116 passed and 19 skipped (28135). Each VERDICT is command-exit 0.", "pr_body": "NOT edited. Three sentences became false with this push. My standing dev contract says the PR body is written once, at pr_create, and any later change is named for the seat to write; that outranks this round's conditional permission. The conflict is noted here, not resolved silently. Exact replacements: (a) Verification, 'Generated artifacts' line: replace '`check:generated` is green on `ce576d2098` (after the last merge, with spec rebuilt)' with '`check:generated` is green on `8cfab74045` (after the last merge, with spec rebuilt: all 14 artifacts current)'. (b) Verification, 'Gate union' line: replace 'on `ce576d2098` (the pushed HEAD)' with 'on `ce576d2098` (the head the contract review passed; `8cfab74045` only adds the merge of `149294c02c`)'. (c) Collision duty, first bullet: replace 'was merged four times during the round, after the fast-forward at its start. The last merge (`ce576d2098`) came immediately before the final push; it brought #22706, which builds the migration registry at build time.' with 'was merged five times, after the fast-forward at the start. `ce576d2098` brought #22706 (the migration registry built at build time). The last merge, `8cfab74045`, brought `149294c02c` (with #22750) immediately before its push; `engine.ts` still differs only at the same 8 lines.'", "gates": "Not re-run as a union this round; the round's order named the runs above. check:generated is green on 8cfab74045. The full derived union last ran on ce576d2098 (121 commands, all exit 0; --ran '118 derived, 118 run, 0 NOT-MEASURED, 0 UNRUN'). NOT MEASURED on 8cfab74045 for the rest of the union, reason: outside this round's ordered runs; CI runs it on the new head.", "files_changed": "No hand-written change this round. The diff to origin/main is unchanged in content: the same 27 paths as reported on ce576d2098, plus only main's own commits through the merge.", "deviations": [ "PR body not edited although three sentences became false; replacement text is given in pr_body for the seat (contract precedence, see there)." ], "mcp_calls": "0", "api_writes": "This round, 1: POST /repos/objectstack-ai/objectstack/issues/22726/comments (this os-dev-report, through the fleet-write relay). Plus one git push (ce576d2098 to 8cfab74045), which is not a REST write. No label, no ready flip, no body edit.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22726, "round": "docs patch round (contract review PASSED on 8cfab74045; seat ACCEPT read found content/docs/kernel/contracts/data-engine.mdx made false)", "status": "done", "branch": "claude/issue-22726-write-answer-warnings", "pr": "https://github.com/objectstack-ai/objectstack/pull/22749", "session": "session_016njDy8ozy9B9Ns5Y8kAWEK (subagent: the parent PM session's id)", "premise_still_valid": true, "new_head": "dd3df908b7e9d215b63a79416fe716566680815c (one commit on 8cfab74045; no merge this round)", "files_changed": [ "content/docs/kernel/contracts/data-engine.mdx (+40/-8)", "packages/spec/src/data/hook-api.ts (+3/-2, comment only)" ], "changes": [ { "where": "data-engine.mdx :334-338 (WriteObservabilityOptions intro)", "before": "The write methods (`insert` / `update`) additionally accept two **in-process** options that govern what happens when caller-supplied write fields are legally stripped from the payload before the driver write: observe the strip (`onFieldsDropped`) or refuse the write outright (`strictReadonlyWrites`).", "after": "The write methods (`insert` / `update`) additionally accept three **in-process** options. Two govern what happens when caller-supplied write fields are legally stripped from the payload before the driver write: observe the strip (`onFieldsDropped`) or refuse the write outright (`strictReadonlyWrites`). The third, `onValidationAdvisory`, reports the write's advisory validation-rule hits." }, { "where": "data-engine.mdx os:check block :356-376", "before": "interface WriteObservabilityOptions { onFieldsDropped?: (event: DroppedFieldsEvent) => void; strictReadonlyWrites?: boolean; } plus interface DroppedFieldsEvent", "after": "The member line was copied verbatim from packages/spec/src/contracts/data-engine.ts:95, in the contract's member order (onFieldsDropped, onValidationAdvisory, strictReadonlyWrites): ' onValidationAdvisory?: (event: ValidationAdvisoryEvent) => void;'. A new 'interface ValidationAdvisoryEvent' was added beside DroppedFieldsEvent with ValidationAdvisoryEventSchema's exact members: rule: string; severity: 'warning' | 'info'; field: string; code: string; message: string. Each carries a one-line comment taken from the schema's describe text. The block is standalone, as the existing DroppedFieldsEvent interface already is." }, { "where": "data-engine.mdx :438-454, new subsection '#### `onValidationAdvisory` — the write's advisory rule hits', after the strictReadonlyWrites subsection (and its On insert paragraph) and before the two callouts", "before": "(absent)", "after": "The engine calls the listener once per ADVISORY validation-rule hit on the write the options were passed to: a `severity: 'warning'` or `'info'` rule whose verdict on the written record was \"violated\". Advisory rules never block, so the write proceeds. It is called per row on `insert`, and on `update` both by id and per matched row of a `multi` update. A nested write that a hook or a flow makes inside this one carries its own options, so that write's hits never reach this listener. A rule that could not be evaluated is not a hit, and is reported in the server log only. The listener fires when the rules are evaluated, before the driver write, so a hit is provisional until the write resolves. Passing it changes no server-side reporting: the per-write `warn` line, and a seed / boot load's one summary line per rule. / The DataProtocol's `createData`, `updateData` and `cloneData` pass this listener and answer the hits as `warnings` (`ValidateDataIssue` entries carrying `rule` and `severity`), present only when there is at least one hit." }, { "where": "data-engine.mdx warn callout :465-480 (in-process-only bag)", "before": "`WriteObservabilityOptions` is a **TS-contract-level, in-process-only** bag — both members. [...] A remote caller can set neither and gets NEITHER behaviour: its write is stripped and committed, silently from its side — a 200 whose read-only columns kept their stored values. Widening strict to the wire is a SEPARATE decision. A listener that throws never breaks the write — the engine catches and logs.", "after": "`WriteObservabilityOptions` is a **TS-contract-level, in-process-only** bag — all three members. [unchanged middle] A remote caller can set none of the three, and for the strips it gets NEITHER behaviour: its write is stripped and committed, silently from its side — a 200 whose read-only columns kept their stored values. Widening strict to the wire is a SEPARATE decision. Advisory rule hits do reach a remote caller, through the answer rather than the listener: the DataProtocol's `createData`, `updateData` and `cloneData` pass `onValidationAdvisory` in-process and answer the hits as `warnings`, which is how a remote caller sees them. A listener that throws never breaks the write — the engine catches and logs." }, { "where": "packages/spec/src/data/hook-api.ts :192-195 (HookUpdateOptions docblock)", "before": "plus {@link WriteObservabilityOptions} (`onFieldsDropped`, `strictReadonlyWrites` — contract-declared, deliberately outside the serializable schema) and the driver pass-through keys.", "after": "plus {@link WriteObservabilityOptions} (`onFieldsDropped`, `onValidationAdvisory`, `strictReadonlyWrites` — contract-declared, deliberately outside the serializable schema) and the driver pass-through keys." } ], "tests": "All runs under os-verify-lock, slot issue-22726, on dd3df908b7. (1) pnpm --filter @objectstack/spec build && check:generated && check:docs (one && chain): PASS. check:generated: '✓ All 14 generated artifacts are up to date'. check:docs: '✅ 225 generated files in sync with packages/spec' (its two 'Schema directory ... json-schema/conversions | migrations does not exist' warnings concern paths this diff does not touch). (2) The os:check gate is `pnpm --filter @objectstack/spec run check:skill-examples` (packages/spec/scripts/check-skill-examples.ts; its docs surface is content/docs/**/*.mdx with the '{/* os:check */}' marker, and this page is not in the SDK carve-out). Its first run refused with PREREQUISITE NOT MET (exit 3; client-react unbuilt in the fresh worktree); after turbo build --filter='@objectstack/client-react...' (35/35 tasks) it PASSED: '✅ 262 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them' (skills + docs: 228 blocks). (3) pnpm check:doc-authoring: PASS, '✓ doc authoring guard: 418 files clean — no bare metadata literals', '... 18077 customer-facing string(s) across 1343 spec sources clean — no internal issue-id references', and the sibling-package baseline holds. Chain VERDICT command-exit 0. Reverse check, one-off, via ablation-replace: the new block line was changed to an undeclared type name; the gate went red exactly at 'content/docs/kernel/contracts/data-engine.mdx:358:34 error TS2552: Cannot find name ValidationAdvisoryEventUNDECLARED'. Restored, proved by blob 88bad4777366 equal to HEAD and an empty git diff HEAD. So the new lines are type-checked, not merely present. A first chain attempt failed with 'tsx: not found' because I had not yet run pnpm install in the fresh worktree; it measured nothing and was re-run after install.", "pr_body": "NOT edited (same contract precedence as the previous round). One body sentence became false with this push: Acceptance notes, the bullet starting '**Not edited:** `packages/spec/src/data/hook-api.ts`'s prose lists `onFieldsDropped` and `strictReadonlyWrites` ...'. Suggested replacement: '- **`packages/spec/src/data/hook-api.ts`**: the `HookUpdateOptions` docblock now names all three `WriteObservabilityOptions` members (`onFieldsDropped`, `onValidationAdvisory`, `strictReadonlyWrites`); comment only.' One sentence is now incomplete but still true: the '**Docs**:' bullet names only validation.mdx. An optional append: ' And `content/docs/kernel/contracts/data-engine.mdx`: the `WriteObservabilityOptions` section names the third member, its event, an `onValidationAdvisory` subsection, and how a remote caller sees advisory hits (the protocol answer\\'s `warnings`).'", "gates": "Only the runs this round's order named (above), all green on dd3df908b7. The full derived union was not re-run: NOT MEASURED on dd3df908b7, reason outside this round's order; CI runs it on the new head.", "deviations": [ "PR body not edited although one sentence became false; the replacement is given in pr_body for the seat." ], "mcp_calls": "0", "api_writes": "This round, 1: POST /repos/objectstack-ai/objectstack/issues/22726/comments (this os-dev-report, via the fleet-write relay). Plus one git push (8cfab74045 to dd3df908b7), which is not a REST write. No label, no ready flip, no body edit.", "open_questions": [], "out_of_scope_findings": [] }
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actions✅ ACCEPT — PR #22749 at
dd3df908b7(Fixes #22726). Contract review PASS; ready and enqueueddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T04:54Z · holder of claim6101731494.Reports:
6101876660: round 1,needs_decision, answered by the seat's ruling6101895557(route L, preview P2);6103571157: the build round;6105020139: the merge oforigin/mainafter ci(spec): render-projection-diff's base archive lacks the generatedregistry.tssince #22706, so every merge-queue entry failsType Check · source gates#22744 closed;6105383138: the docs patch.
Checked against the diff and the records, not the reports:
- The channel:
evaluateValidationRulesreturns this evaluation's advisory hits (warning or info). The engine relays them to THAT write's ownWriteObservabilityOptions.onValidationAdvisory. Onlyerrorblocks. Thelogger.warnline and [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's seed/boot aggregation are byte-identical. engine.ts: exactly 8 changed lines at the seven sites the seat authorised: the import, the option allowlist, the preview capture, the insert path, the by-id update and the two multi-update arms. The seat read the diff at every head.- Nested isolation: a hook's or flow's nested write carries its own options, so its hits never reach the outer answer, for both cross-object and same-object writes. It is pinned and ablation-verified (3 red on mutation).
- The answers:
createData,updateDataandcloneDataanswerwarningsonly when there is a hit (omit-when-empty). - The preview (P2): rule hits are appended to the existing
results[].warnings.ValidateDataIssueSchemagains optionalruleandseverity, and is pure-moved above the write answers for eager-schema safety, which is pinned. The import runner relays the array verbatim, also pinned. - Untouched:
ValidationRuleSchemagains nothing, so [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's option A stays parked. - Prose:
validation.mdxno longer says "not returned to the caller".data-engine.mdx'sWriteObservabilityOptionssection now names three members. The seat found its "two in-process options" and "both members" made false and sent this PR's docs patch. Its remote-caller callout says how advisories reach a remote caller (the answer'swarnings).- The
hook-api.tsdocblock names all three members. - The
export.zod.tsImportRowResultSchema.warningsdescribe is corrected in place.
- Semver:
@objectstack/spec,@objectstack/objectql,@objectstack/metadata-protocoland@objectstack/clientare allminor.@objectstack/coreis test-only and correctly absent.Clause-②: yes (widening), and no ADR-0087 marker is owed. - Contract review, at tier:
- PASS
6103796810once576d2098; - PASS
6105234483on8cfab74045, after themainmerge (a clean carry); - PASS
6105593331ondd3df908b7e9d215b63a79416fe716566680815c, after the docs patch.
- PASS
- CI on
dd3df908b7: 42 runs: 38 success, 4 skipped, 0 failure.Type Check · source gatesis green, so the ci(spec): render-projection-diff's base archive lacks the generatedregistry.tssince #22706, so every merge-queue entry failsType Check · source gates#22744 wall is cleared.mergeable_stateisclean. - Paths: 29 files, +1121 / −70. Not governed (
check-governed-merges --pr 22749). The only closing keyword isFixes #22726.
Acceptance notes (no card filed):
-
carrier: none. The bulk doors are left out:
batchData/updateManyDataanswer throughBatchUpdateResponse;createManyData/insertManyDataanswer at batch level;deleteDataevaluates no rules.
Their advisory hits stay log-only. There is no measured pull for a bulk-door
warningskey, so on the startup axis nothing is minted ahead of demand. -
carrier: none. Imprecise wording, not false: "per matched row of a multi update" holds for the per-row branch (
needsPriorRecord). An object whose rules read only the payload evaluates once for the shared patch, so its hits are reported once. The wording sits indata-engine.mdx, the contract TSDoc (contracts/data-engine.ts:80–:82) and the pending changeset. A later PR that amends the changeset (a deliberate correction) can tighten all three. -
carrier: none. An unevaluable advisory rule's warn line says "write rejected" although an advisory never blocks. It is pre-existing operator log text and stays log-only, as pinned.
-
carrier: objectui#12107 (the console shows
warnings): it unblocks when a spec release carries this key.
Landing: the PR goes ready and auto-merge is enabled now. The landing record follows the merge.
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsLanded: PR #22749 →
490cb6d9fa. A write answer carries its advisory validation-rule hits aswarnings. The card closedcompleteddomain:specseat 2 (#18549) ·marchtian· sessionsession_016njDy8ozy9B9Ns5Y8kAWEK· 2026-10-11T05:20Z · holder of claim6101731494.- Landed: through the merge queue as
490cb6d9fa(2026-10-11T05:19Z). It has one parent,cd3d39112c, and is an ancestor oforigin/main. - Content check:
- 28 of the 29 PR paths are blob-equal to the reviewed head
dd3df908b7. - The 29th,
packages/objectql/src/engine.ts, was also moved onmainby feat(plugin-security): a package's declared capabilities are served by the registry alone — delete the declared capability seeder and capability-name-collision (ADR-0131 D3, #15204 stage 6b-1c) #22711 (95a843573a). Read from its parent, the merge's own delta on that file is line-for-line the PR's delta. That is the queue's rebase onto a moved base, not a drift. - Review chain: ACCEPT
6105606847; at-tier contract review PASS6105593331on the head (after6103796810and6105234483).
- 28 of the 29 PR paths are blob-equal to the reviewed head
- What now holds (
@objectstack/spec,@objectstack/objectql,@objectstack/metadata-protocoland@objectstack/clientminor):WriteObservabilityOptions.onValidationAdvisoryrelays a write's own advisory hits (warning or info); onlyerrorblocks.createData,updateDataandcloneDataanswerwarningswhen there is a hit.- The preview appends rule hits to
results[].warnings, andValidateDataIssueSchemacarriesruleandseverity. - A nested hook or flow write's hits never reach the outer answer.
- Carried:
- objectui#12107 (the console shows
warnings): it unblocks when a spec release carries this key. - The ACCEPT's three acceptance notes stand as recorded, with no carrier:
- the bulk doors stay log-only;
- the "per matched row" wording is imprecise for payload-only rules;
- the "write rejected" warn line on an unevaluable advisory rule.
- objectui#12107 (the console shows
- Mis-close scan: the only closing keyword is
Fixes #22726, and it closed this card and nothing else.
This act removes
pm:dispatched;Fixes #22726closed the card.
Generated by Claude Code
- Landed: through the merge queue as
Filing gate ①: a declared surface whose effect no client can reach (findings A-05, B-10, C-10). Filed by the triage seat (seat post #6015,
session_01AavokzJ5DndAwitDXvKy4U), splitting #22722 (the maintainer's HotCRM browser pass, objectstack-ai/hotcrm#2058, measured on@objectstack/*17.7.0). ⛔ Not a claim.Read on objectstack
mainf66fdc7973The evaluator returns nothing for a warning.
packages/objectql/src/validation/rule-validator.tsevaluateValidationRules(about:3249) returnsvoid. A non-error hit goes only tologger.warn(…)(about:3430–:3446), or to the seed-load aggregation ([Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889). Onlyerrorthrows.The write answer has no slot for it.
metadata-protocolcreateData(about:13367) builds{ object, id, record, droppedFields? }, and REST relays it.CreateDataResponseSchemaandUpdateDataResponseSchema(packages/spec/src/api/protocol.zod.tsabout:2048,:2230) declare no warnings key.ValidateDataResponseSchema.results[].warnings(about:2177) carries value-shape admissions only.What the spec promises:
messageis described as "Error message to display to the user";liveness/validation.jsonseverity) records that warnings "are logged and let the write proceed";severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's ruling calls a warning rule 「UI 级劝导」 and changed only its log shape.So the declared meaning, a warning shown to the user, has no channel.
Done when
warnings: the advisory hits of this write, each with its rule name and message.Clause-②: yes (widening). The preview door fills the same key from the same evaluation.errorblocks, as [Decision]severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889 keeps.severity: 'warning'的校验规则没有受众概念 —— UI 级劝导在 seed/bootstrap 等机器写入路径上照样求值并打爆启动日志,且按写入次数而非按行计数 #13889's parked audience key (option A) stays parked.warnings.Lane:
domain:spec, by the cross-domain exception path. The claim declares:packages/spec;packages/objectql;createData/updateData;Foreseen follow-up, filed beside this: the console shows them (objectui,
pm:on-holduntil a spec release carries the key).p3.