Skip to content

fix(objectql): bind previous to null on insert so a rule that reads it answers with its own message - #23971

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-23910-validation-previous-insert
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-23910-validation-previous-insert

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #23910

Clause-②: yes (widening)

What changed

On insert, previous is now bound to null in validation rules and in declarative hook conditions. So a rule or condition that reads it decides the insert. The flow engine already gives a start condition on its create leg the same binding.

Before, the insert path left previous unbound. Any reference to it, including the guard previous == null, faulted Unknown variable: previous. A violating insert was then refused (validation) or aborted (hook) as unevaluable, naming the CEL fault instead of the rule's message. sys_user_permission_set.validity_window_order is the measured case.

This widens the published accept set, hence Clause-②: yes (widening) and a minor changeset:

  • A rule shaped previous != null && … ("judge updates only") now ADMITS inserts that it refused before with the unevaluable fault.
  • A field's requiredWhen: previous == null now requires the field on insert, instead of refusing every insert.

No shipped rule's or hook's verdict moves (census below).

Validation (packages/objectql/src/validation/rule-validator.ts)

  • previousBinding(mode, priorRecord, fields) decides the binding once per evaluation. On insert it returns null. On an update with the prior row in hand, it returns that row made total over the declared fields, as before.

  • An update whose prior row is not in hand stays unbound. Examples are the bulk seam's previous: null and an update-mode preview that names no row. null there would answer previous == null with "this is an insert", which is false. Unbound, a rule that reads previous fails closed.

  • ruleScope(record, previous, extra) builds the scope for every rule-evaluation ExpressionEngine.evaluate in the file. That covers:

    • a script / cross_field condition;
    • a conditional's when;
    • a field's requiredWhen and its ADR-0113 pre-check;
    • an option's visibleWhen;
    • readonlyWhen, which is update-only and unchanged.

    The previous ?? undefined that turned an explicit null back into "unbound" is gone. null rides extra, the bag buildScope merges onto the variable scope, because EvalContext.previous is typed for a row only.

  • nullPreviousReadDetail. A column read off a null previous (record.stage != previous.stage) still has no value on insert and still refuses. The refusal now names previous.KEY and gives the guards: previous == null || … judges an insert, previous != null && … skips one. Without this, the generic text would have said "a field this object declares, but its value was not supplied". All four refusal builders carry it.

Hook conditions (packages/objectql/src/hook-wrappers.ts)

  • Same scope as validation. A declarative hook condition is now evaluated in ruleScope. pickPreviousPayload returns null on beforeInsert / afterInsert. On any other write that carries a prior row, it returns that row, copied and made total as before. When an existing row's prior state is not in hand, previous stays unbound.
  • ctx.previous is untouched. The engine's pre-image, which the after-hooks observe, is not changed. Only the CEL binding is.
  • The refusal names the read. A column read off the null on insert still aborts the operation (hook 的 condition 求不出值时:全局 fail loud —— 抛错并中断该次操作(方案 B 已拍板;Blocked-by #4770) #4775). unevaluableConditionError names the read with the same nullPreviousReadDetail sentence.
  • Docblock rewritten. The "When it is ABSENT" section of the docblock is now "When it is null, and when it is ABSENT". It keeps the update-without-row reasoning.

Docs (content/docs/data-modeling/formulas.mdx)

The previous row of the scope table now says the following. previous is the row before the update. It is null on insert in validation, hook conditions and flows, so previous == null judges an insert. A column read off it there has no value and faults. No other sentence on the page says previous is unbound on insert.

engine.ts is not edited. The insert call site hands the evaluator mode: 'insert', and the binding derives from that. The validate-only preview's insert mode judges the same way.

Census (triage's instruction)

Validation rules reading previous

Searched at b0d12e2e9 across packages/** (platform objects, plugins, services) and examples/**, non-test, TS and non-TS metadata.

Rule File Condition Insert verdict before Insert verdict after
validity_window_order (cross_field, error) packages/plugins/plugin-security/src/objects/sys-user-permission-set.object.ts (previous == null or a bound moved) and both bounds set and valid_until at or before valid_from inverted / equal window: refused, 400, reason: unevaluable. Valid, one-bound or open window: admitted inverted / equal window: refused, 400, the rule's own message. Valid, one-bound or open window: admitted

That is the only shipped validation rule that reads previous. Its verdict cannot move, for this reason:

  • Before: the first clause was a CEL error, which && absorbs whenever another conjunct is false.
  • After: the first clause is true on insert, and the rule answers the remaining conjuncts.

So the same writes are refused and the same writes are admitted.

No field requiredWhen / readonlyWhen / visibleWhen in shipped metadata reads previous. The other *.object.ts hits are prose: comments, field descriptions and action copy in sys-metadata-history, sys-account, sys-member, sys-oauth-application, sys-oauth-consent, sys-two-factor, sys-user-preference, sys-setting-audit and sys-audit-log.

Declarative hook conditions reading previous

Searched at a2863de08 across packages/** and examples/**.

Hook File Events Condition Insert verdict before Insert verdict after
showcase_audit_task_completion examples/app-showcase/src/data/hooks/index.ts afterUpdate previous.done != true && record.done == true not dispatched on insert not dispatched on insert (unchanged)
showcase_guard_task_reopen examples/app-showcase/src/data/hooks/index.ts beforeUpdate previous.done == true && record.done != true not dispatched on insert not dispatched on insert (unchanged)

No other shipped declarative hook condition names previous. Plugin and service hooks read ctx.previous in JS, not in a CEL condition. The 16 flow start conditions that read previous (examples/app-showcase/src/automation/flows/**, examples/app-todo/src/flows/task.flow.ts) are the flow engine's, which already binds null on insert.

Verification

CEL seam

Measured against the built @objectstack/formula.

Expression previous Result
previous == null unbound type: Unknown variable: previous
previous == null null true
previous != null && record.a > 5 null false
previous.x null runtime: No such key: x
previous == null || record.a != previous.a null true
has(previous.x) null false

buildScope (packages/formula/src/stdlib.ts) binds previous only when it is not undefined.

Pins

  • packages/objectql/src/validation/rule-previous-insert-binding.test.ts (new) pins these cases:
    • A guarded predicate refuses a violating insert with its own message and no unevaluable reason. It admits a valid insert, and still reads the prior row on update.
    • A conditional with when: previous == null runs its branch on insert.
    • requiredWhen: previous == null is required on insert.
    • An unguarded previous.KEY read refuses as unevaluable, naming previous.KEY.
    • An update with no prior row keeps previous unbound, so previous != null && … is refused, not admitted.
  • packages/objectql/src/hook-condition-previous-scope.test.ts. The insert case flipped from "leaves previous UNBOUND on insert" to two cases:
    • previous == null && record.done == true fires on beforeInsert and afterInsert, and the engine's ctx.previous stays undefined.
    • previous.done != true && record.done == true on beforeInsert still aborts with a HookConditionError, reason: 'unevaluable', and a message naming 'previous.done'.
  • packages/plugins/plugin-security/src/sys-user-permission-set-window-order.test.ts asserts that every refusal carries validity_window_order's own declared message, read off the object, and no unevaluable reason. It drives the real ObjectQL insert path over a real SQL driver with the real SecurityPlugin.

Red, then green

Validation pins.

  • Red on 099cc9d69 (uncommitted pins): objectql pin 5 failed / 7; window-order 4 insert refusals failed.
  • Green at 70374639e: 7 / 7 and 11 / 11. The objectql local suite: 435 files, 8220 tests passed.
  • Ablation (scripts/ablation-replace.mjs): if (mode === 'insert') return null; to return undefined; gave 5 / 7 and 4 / 11 failed. The restore was proven: blob equal to HEAD, git diff HEAD empty.

Hook pin.

  • Red on b0d12e2e9 (flipped test, unchanged source): 2 failed / 25. The message received was … (type: Unknown variable: previous) ….
  • Green after the change: 25 / 25.
  • Ablation at ee0ed2834: if (isInsertEvent(ctx.event)) return null; to return undefined; gave 2 failed / 25. The restore was proven: blob fb6056cc7567 equal to HEAD, git diff HEAD empty.

No dist/ is involved in either suite. objectql's tests import its source, and plugin-security aliases @objectstack/objectql to src/index.ts.

Final head a2863de08, through scripts/pm/os-verify-lock.sh:

  • objectql src/validation/, src/hook-*, src/engine-after-hook-abort-unit.test.ts and src/bulk-write-per-row-hooks.test.ts: 41 files, 1300 passed.
  • plugin-security window-order: 11 / 11.
  • pnpm --filter @objectstack/objectql typecheck: tsc --noEmit plus check:test-typecheck OK, with no new ledger signature.

REST door

On a stock showcase boot of b0d12e2e9, POST /api/v1/data/sys_user_permission_set with valid_from: 2027-01-02T00:00:00Z and valid_until: 2027-01-01T00:00:00Z:

  • It answered 400 VALIDATION_FAILED. fields[0] = { field: valid_until, code: rule_violation, message: "Valid Until must be later than Valid From. …" }, with no constraint.
  • The valid window answered 201.

Gates at a2863de08

Derived set. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 100 commands, and all 100 were run.

  • First pass: 97 exited 0. Three refused on a prerequisite and were not measurements:
    • check:skill-examples (needs the client-react declarations);
    • check:dual-build-cjs-loads (needs a workspace build);
    • check:i18n (needs the built CLI closure).
  • After a workspace turbo run build: all three exited 0.
  • Reconciliation: dispatch-gates --ran reported "100 derived, 100 run, 0 NOT-MEASURED, 0 UNRUN".
  • Docs gate: pnpm check:doc-authoring exited 0.

Changeset gates.

  • node scripts/check-changeset-no-major.mjs --base origin/main exited 0: "This diff introduces no major bump". Its level axis reads the PR body, and a local run has none.
  • The level axis was also driven offline with --event and this body: exit 0, "LEVEL AXIS: this PR declares clause-② yes (widening), and no package whose published source … it moves is graded patch".
  • node scripts/check-adr-0087-registration.mjs --base origin/main exited 0: "this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen)". It reads yes (widening) as not breaking.

Acceptance notes

  • Published skill text (skills/**). It still describes previous as unbound on insert:

    • skills/objectstack-data/references/data-hooks.md: the "previous is UNBOUND where there is no prior state" bullet, and the "a previous reference on an insert" clause.
    • skills/objectstack-formula/SKILL.md: the scope-table row "Insert events …, validation rule on insert | unbound", plus the note under the migration table.

    That surface is governed (Tier H) and outside this card's surface, so it is not edited here. The seat filed the correction as skills: objectstack-data and objectstack-formula still say previous is unbound on insert; once PR #23971 lands it is null in validation and hook conditions #23983 (domain:skills), to land after this PR. Its advice ("write insert-event conditions over record alone"; a previous.x read on insert fails the write) is still safe. Its "unbound" wording is now false: previous == null evaluates on insert.

  • Dogfood comment. packages/qa/dogfood/test/hook-after-condition-rollback.dogfood.test.ts comments that previous is unbound on an insert. Its condition, previous.name != record.name, still faults on insert, because the column read off null faults. So the test's verdict is unchanged; only the comment's reason is stale. The objectql twin's comment (engine-after-hook-abort-unit.test.ts) is corrected here.

  • EvalContext.previous type. In @objectstack/formula it is typed for a row only, while buildScope already binds null. Both surfaces therefore route null through extra.


Generated by Claude Code

… it is evaluable

A validation rule's CEL scope left `previous` unbound on insert, so any
reference to it -- the guard `previous == null` included -- faulted
`Unknown variable: previous`, and a violating insert was refused as
`unevaluable` instead of with the rule's own message
(`sys_user_permission_set.validity_window_order` is the measured case).

`previousBinding` decides the binding once per evaluation -- `null` on
insert, the prior row made total on update, unbound when an update's row
is not in hand -- and `ruleScope` builds every rule predicate's scope from
it, so no seam can drop it with a `?? undefined` again. A column read off a
null `previous` still refuses, now naming the read and the guard to add.

Claude-Session: https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 16, 2026
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql, touching 19 documentable anchor(s).

10 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/skills-reference.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/data-modeling/field-types.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/data-modeling/fields.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/data-modeling/formulas.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/data-modeling/validation-rules.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/deployment/cli.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/deployment/validating-metadata.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/permissions/system-context.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/protocol/objectui/concept.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/protocol/objectui/layout-dsl.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))

⛔ 5 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/implementation-status.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/releases/v15.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/releases/v16.mdx (via evaluateValidationRules (symbol, a top-level function), requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/releases/v17/17-0.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))
  • content/docs/releases/v17/17-5.mdx (via requiredWhen (literal, a string literal in evaluateValidationRules))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 17 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 68ffa5dc1af1abeb012cd1ed7dfa18be4c07a8c4 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from ba79d2ebc2d48ac572c812fbca6213b75355bdb1 — the merge of head a2863de0861e12bd401614b795fa57b69948a90b into base 68ffa5dc1af1abeb012cd1ed7dfa18be4c07a8c4, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin ba79d2ebc2d48ac572c812fbca6213b75355bdb1 && git checkout ba79d2ebc2d48ac572c812fbca6213b75355bdb1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 68ffa5dc1af1abeb012cd1ed7dfa18be4c07a8c4 a2863de0861e12bd401614b795fa57b69948a90b && git checkout -B drift-repro 68ffa5dc1af1abeb012cd1ed7dfa18be4c07a8c4 && git merge --no-ff a2863de0861e12bd401614b795fa57b69948a90b

node scripts/docs-audit/affected-docs.mjs --json 68ffa5dc1af1abeb012cd1ed7dfa18be4c07a8c4

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 68ffa5dc1af1abeb012cd1ed7dfa18be4c07a8c4 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…; Clause-② yes (widening)

A declarative hook condition is now evaluated in the validation side's
scope (`ruleScope`): `previous` is `null` on beforeInsert/afterInsert, the
prior row on any other write that carries one, and unbound when an existing
row's prior state is not in hand. Only the CEL binding changes; the engine's
`ctx.previous` is untouched. A column read off the null `previous` still
aborts the operation, now naming the read and the guard to add
(`nullPreviousReadDetail`, shared with validation).

The changeset becomes `minor` with `Clause-②: yes (widening)`: a rule or
condition shaped `previous != null && ...` now admits inserts it refused
with an `unevaluable` fault. The formulas page's `previous` row says
`previous` is null on insert in validation, hook conditions and flows.

Claude-Session: https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ✅ ACCEPT: PR #23971 at a2863de086 · domain:engine seat 2 · session_01TQLy3Yq6vF4SDpTXdr3Uwi · 2026-10-16T16:19Z

Basis:

This comment is also the review of record for the yes (widening) declaration (TIER_DEFAULT, the lane seat's own). No published schema and no governed path is touched, so no CONTRACT_REVIEW_TIER review is owed.

Checklist

  • Shape. Draft, base main, first line Fixes #23910. Clause-②: yes (widening) is on its own line in the body and in the changeset. No other card number carries a closing verb.
  • Why yes (widening) is the truthful line.
    • The documented scope (formulas.mdx's previous row before this PR: "hooks, validation on update") and the implementation agreed: previous was unbound on insert. So an insert-time predicate reading it faulted and was refused, per ADR-0137 D2.
    • Now a rule shaped previous != null && …, or a requiredWhen: previous == null on an insert that supplies the field, ADMITS inserts it refused. The changeset names both shapes in its first paragraph.
    • @objectstack/objectql goes minor. check-changeset-no-major drove the level axis offline with this body: arm widening, no moved package graded patch. check-adr-0087-registration reads it as non-breaking.
  • No shipped verdict moves.
    • The census covers every validation rule and declarative hook condition in packages/** and examples/**.
    • validity_window_order is the one rule that reads previous. Its first conjunct was a CEL error, which && absorbs whenever another conjunct is false. It is now true on insert. So it refuses and admits the same inserts, and only the reason moves.
    • The two showcase hook conditions that read previous run on update only.
  • The binding, read hunk by hunk.
    • previousBinding decides the binding once: null on insert, the total prior row on update, and unbound when an update has no row in hand. So previous == null can never read an update as an insert, and a pin holds that previous != null && … stays refused there.
    • ruleScope builds every rule-evaluation scope. The previous ?? undefined that unbound an explicit null is gone.
    • readonlyWhen keeps the binding it had (update only).
    • Hook conditions use the same ruleScope, and pickPreviousPayload returns null on insert events. The engine's ctx.previous is untouched, and a pin asserts it stays undefined after an insert dispatch.
    • A column read off the null still faults, and both surfaces now name previous.KEY and the guard to add.
  • Evidence.
    • Validation pins: red on uncommitted pins (5/7 and 4/11), green, and an ablation (return null → return undefined) that turns them red again, with the restore proven.
    • Hook pin: red on b0d12e2e9 (2/25, "Unknown variable: previous"), green 25/25, and an ablation with the restore proven.
    • Final head: 1300 of 1300 across objectql's validation and hook files, 11 of 11 for the window-order test, and typecheck exit 0 with no new ledger signature.
    • REST door on a stock showcase boot: the inverted window answers 400 VALIDATION_FAILED with the rule's own message, and the valid window answers 201.
  • Docs. formulas.mdx's previous row states the new rule. The seat re-read every content/docs page that pairs previous with insert or create, outside the release-owned pages. flows.mdx already says previous == null on create. forms.mdx:169 describes the imperative ctx.previous, which is unchanged. No other page is falsified.
  • Gates. dispatch-gates --commands: 100 derived, 100 run, 0 NOT MEASURED, 0 UNRUN, after building three prerequisites. check:doc-authoring exits 0.
  • Landing checks.

The dev's open questions, answered by this seat (not escalated):

Out of scope, one line each:

Gate on the head: ci-failure.mjs --pr 23971: GREEN, 41 of 41. The five skips (Console Pin Gate, Temporal Conformance (live PG + MySQL), Dogfood Verify CLI, the Dogfood Regression Gate matrix template, Packed-tarball smoke (opt-in)) are on check-expected-skips.mjs's roster, and each gate is unmet on this diff. Build Docs ran, because the formulas page changed, and passed. Check Changeset re-ran on the seat's body rewrite and passed, reading yes (widening) against the minor changeset. Auto Label and Check PR Size read skipped only on that body-edit run, and read success on the push run of the same head.

Next: ready, then auto-merge through the queue.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 16, 2026 16:20
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 16, 2026 16:20
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into main with commit b25d98a Oct 16, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23910-validation-previous-insert branch October 16, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants