You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
Filing gate ①: published agent-facing text that a landing change makes false. reach: every agent that loads the objectstack-data or objectstack-formula skill and writes an insert-time condition.
Filed by the domain:engine seat 2 (#20966) · os-tesla · session_01TQLy3Yq6vF4SDpTXdr3Uwi, from the #23910 dev's open question (PR #23971). ⛔ Not a claim. Grade is triage's.
Reader who acts: the domain:skills seat. skills/** is a governed surface (Tier H), so the edit is its own PR and lands on the maintainer's approval. The seat kept it out of PR #23971 so that an engine fix does not couple two landing tiers.
Premise and order: the premise holds once PR #23971 (Fixes #23910) merges. Until then, the skill text below is TRUE and must not change. After it, validation rules and declarative hook conditions bind previous to null on insert, the binding the flow engine already gives a start condition's create leg. So previous == null evaluates to true on an insert, and only a COLUMN read off it (previous.x) still faults.
The lines that become false (paths at origin/main)
skills/objectstack-data/references/data-hooks.md:247-248: "previous is UNBOUND where there is no prior state, and a reference to an unbound root makes the whole condition unevaluable. One case: insert events …", and the bullet's insert clause. :280 ("which is why the unbound-previous … bullets above are load-bearing") reads with it.
skills/objectstack-formula/SKILL.md:306: the scope-table row "Insert events (beforeInsert / afterInsert), validation rule on insert | unbound — there is no prior state". :312 and the note under the migration table restate it.
What the corrected text says
The runtime truth, stated in PR #23971's changeset and in content/docs/data-modeling/formulas.mdx's previous row after that PR:
on insert:previous is null in validation, hook conditions and flows, so previous == null || … judges an insert and previous != null && … skips one;
a column read: off it on insert (previous.status), it still has no value and faults;
an existing row whose prior state is not in hand:previous stays unbound.
The skills' current advice (write insert-event conditions over record alone) stays safe. Only the "unbound" statement and its consequence are wrong.
Premise now holds · domain:engine seat 2 · session_01TQLy3Yq6vF4SDpTXdr3Uwi · 2026-10-16T17:04Z. PR #23971 (Fixes #23910) merged as b25d98a46f. On main, validation rules and declarative hook conditions bind previous to null on insert. So the two skill lines this card names are now false, and the card is actionable for its lane. ⛔ Not a claim.
Why p2: agent-facing published text now tells authors that previous is unbound on insert. An agent following it writes insert-time conditions around a fault that no longer happens.
Governed:skills/** is Tier H, so the PR lands on a GOVERNED_APPROVERS approval.
Claim: PM loop round 2
Session: session_012PxNHU3dfov2hbpg9ycZ8q
Account: marchtian (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-23983-previous-null-on-insert
Worktree: objectstack-issue-23983
Domain: domain:skills
Seat: domain:skills#1
File surface: skills/objectstack-data/references/data-hooks.md (the 「previous is UNBOUND」 bullet and the 「unbound-previous」 clause, L247–248 and L280 at origin/main697301491; 10066/10066 tokens, headroom 0 — paid by lossless deletion inside the file), skills/objectstack-formula/SKILL.md (the scope-table insert row L306 and the note under the table L312–314; 5399/5997 tokens, headroom 598) and one @objectstack/skills patch changeset. ⛔ No other skills/** file (stop on breach; explain in the report)
Container & model: M(published skill text, two files, truth read off main), mode:subagent, model: CONTRACT_REVIEW_TIER (--tier --repo objectstack-ai/objectstack skills/objectstack-data/references/data-hooks.md skills/objectstack-formula/SKILL.md at 697301491: MANDATORY — both ⇢ 'skills/**', clause ①, verbatim 「必须 fable的还包括对外发布的skills」; no one-line exemption under this root)
Clause-②: no
Responsibility: n/a — not a defect card in the product sense: published agent-facing text made false by PR #23971 (b25d98a46f, merged 2026-10-16T17:02Z); filing gate ① with reach: every agent loading either skill
Thread-read: 6221580524
Serial constraints cleared: none. No open PR touches skills/** at this read (every open PR's file list read 2026-10-17T13:04Z); the Version Packages PR (#23825) merged today and the next one re-stamps skills/*/SKILL.md version lines only — a different hunk from L306. The premise holds on main: .changeset/23910-validation-previous-insert.md is on origin/main and content/docs/data-modeling/formulas.mdx L211 states the previous row.
Dispatch order — posted 2026-10-17T13:09Z by the claiming seat (session_012PxNHU3dfov2hbpg9ycZ8q, marchtian, domain:skills#1). The same text is the dev's prompt, run mode:subagent at model tier CONTRACT_REVIEW_TIER (the skills/** path mandate, clause ①; no one-line exemption under this root).
You are the os-dev developer for exactly one card: objectstack-ai/objectstack issue #23983.
Read first. Read the card's body and EVERY comment yourself: the engine seat's premise note 6220637775, triage's grade 6221580524, the seat's claim 6234215977, and this order. Check that the body is complete before you start.
Identity. The PM's Claim: comment (6234215977) is your identity: Branch:claude/issue-23983-previous-null-on-insert, worktree objectstack-issue-23983. Before any edit, verify that the newest Claim: on the card names that branch; on a mismatch, stop and report.
Assignees. ⛔ Leave no second claim, ⛔ never write the card's assignee. The PR's assignee is marchtian (pr_create with assignees: ["marchtian"]).
Standard duties live in .claude/agents/os-dev.md and AGENTS.md, including the two skills/** readings in the PR body (the edited files' before/after lines and the whole catalog's before/after; tokens too). This order carries only the increments.
Checkout./home/user/objectstack is at origin/main697301491; shared — ⛔ never edit it; your worktree goes beside it. Scratch under /tmp/claude-0/-home-user/a273b78d-90a5-59e2-8b79-a91e3d54473d/scratchpad/issue-23983/.
Run everything in the foreground. A round ends only with the report.
1. Ruling — none on wording; the merged runtime change is the specification
No maintainer ruling on this text. The truth the skills must state landed as PR #23971 (Fixes #23910, merged b25d98a46f, 2026-10-16). Its changeset, verbatim (git show b25d98a46:.changeset/23910-validation-previous-insert.md; consumed into packages/objectql/CHANGELOG.md L35–42 by today's version PR):
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 binding a flow's start condition already gets on its create leg. … previous == null || … judges an insert, previous != null && … skips one. A column read off previous on insert (previous.stage) still has no value and still refuses the write; the refusal now names that read and the guard to add. … The binding is the same for every predicate a write evaluates — a script / cross_field condition, a conditional's when, a field's requiredWhen and an option's visibleWhen — and for a hook's condition on beforeInsert / afterInsert; the validate-only preview judges an insert the same way. Only the CEL binding is null: a hook's ctx.previous is unchanged. … A write to an existing row whose prior state is not in hand keeps previous unbound and still fails closed, so previous == null never reads an update as an insert.
content/docs/data-modeling/formulas.mdx L211 on main already states it: 「null on insert, so previous == null judges an insert; a column read off it there (previous.status) has no value and faults | validation, hook conditions and flows — null on insert」.
Deliverable: ONE draft PR to main. Body line 1 Fixes #23983, line 2 Clause-②: no. Files: skills/objectstack-data/references/data-hooks.md (the bullet at L247–248 「previous is UNBOUND where there is no prior state … One case: insert events … write those over record alone」 and the L280 clause 「the unbound-previous and has(...) bullets above are load-bearing」), skills/objectstack-formula/SKILL.md (the scope-table row L306 「Insert events … unbound — there is no prior state」 and the note L312–314 「Referencing previous where it is unbound … So write insert-event conditions over record alone」), and ONE changeset .changeset/23983-previous-null-on-insert.md with '@objectstack/skills': patch and Clause-②: no (precedent: PR #23795's .changeset/23753-navigation-record-surface.md). ⛔ No other skills/** file: other occurrences of 「unbound」 go in the report as out-of-scope findings with the grep evidence. skills/** is Tier H governed: carry ## 维护者速读(草稿) (five parts, Chinese, 席位意见 blank), stay draft; the seat requests the authorized reviewers — you do not.
2. PM mechanism assumptions — measure first; falsify and report where wrong
(a) Token budget (the ratchet scripts/check-skills-token-ratchet.mjs):data-hooks.md 10066/10066 (headroom 0) — every token the new sentences cost is paid by lossless deletion inside the same file; skills/objectstack-formula/SKILL.md 5399/5997 (headroom 598). ⛔ Ceilings do not move; ⛔ re-wrapping is not payment; the PR body shows the before/after of each file and of the catalog.
(b) The three facts to state (card section 「What the corrected text says」): on insert previous is null in validation, hook conditions and flows (previous == null || … judges an insert, previous != null && … skips one); a column read off it on insert (previous.status) has no value and faults; an existing row whose prior state is not in hand keeps previous unbound. The current advice 「write insert-event conditions over record alone」 is still safe but no longer the rule — rewrite it as the null guard, do not delete the safety.
(c) What else reads these files:packages/skills/scripts/sync-catalog.mjs copies skills/** into packages/skills/dist (a published package, hence the changeset); packages/spec has check:skill-docs / check:skill-refs gates with generators gen:skill-docs / gen:skill-refs for SKILL.md changes — run pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated and regenerate ONLY what it proves stale (--fix), commit any regenerated artifact; scripts/check-skill-examples.mjs and check:skill-top-level-keys read skill bodies. Derive the full list with dispatch-gates.
(d) The Version Packages PR re-stamps skills/*/SKILL.md version lines every six hours (chore: version packages (next) #23825 merged today); your hunk at L306 is disjoint. If the base moves under you, merge origin/main only on a reported conflict.
(e) Writes: REST proxy through scripts/pm/ tools only; budget 4 (git push, pr_create draft with assignee, label-write — none expected: a changeset is owed, so ⛔ no skip-changeset; os-dev-report). Report api_writes and mcp_calls.
data-hooks.md L247: 「previous is null on insert and UNBOUND only where a prior row exists but is not in hand.previous == null judges an insert and previous != null && … skips one; a column read off it on insert (previous.done) has no value and faults, so guard the read, not the event.」 Pay by trimming the L280 clause to its still-true half (the has(...) bullet is load-bearing; the previous half now names the null guard).
objectstack-formula/SKILL.md L306 row: 「Insert events (beforeInsert / afterInsert), validation rule on insert | null — previous == null is how a condition judges an insert; a column read (previous.x) faults」; the L312–314 note drops 「Referencing previous where it is unbound」 as the first example and keeps the abort semantics for a typo'd key and a column read off null.
4. Verification you owe
Derive at 697301491: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack skills/objectstack-data/references/data-hooks.md skills/objectstack-formula/SKILL.md; after the FINAL commit re-derive with no paths, run the local subset (the token ratchet and the spec skill-doc gates among them), capture each exit code before any pipe, reconcile with --ran, quote git rev-parse --short HEAD in report and PR body. Ship-proof: the new sentences appear in packages/skills/dist/... after pnpm --filter @objectstack/skills build, the old 「UNBOUND where there is no prior state」 does not.
5. Report
Terminal JSON per os-dev.md, first as an os-dev-report issue comment. Machine-checkable: gates, line_budget (each file's lines and tokens before/after; the catalog total), deviations, files_changed.
Four-axis framework (verbatim from .claude/skills/pm-dispatch/SKILL.md L241–261 at origin/main697301491, file touch f7e836da1, md5 abff5f852c8fd09b79ec0623439aa4f0)
{
"issue": 23983,
"status": "done",
"branch": "claude/issue-23983-previous-null-on-insert",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24174",
"session": "session_012PxNHU3dfov2hbpg9ycZ8q",
"premise_still_valid": true,
"summary": "Rewrote the two published skill passages that said `previous` is unbound on insert. `skills/objectstack-data/references/data-hooks.md`: the `previous` bullet now states `previous` is `null` on insert (`previous == null || …` judges an insert, `previous != null && …` skips one), that a column read off it there (`previous.done`) faults, and that only a write to an existing row whose prior state is not in hand leaves it unbound (fails closed); the abort warning's example, the load-bearing clause and the authoring consequence were rewritten to the `null` guard. `skills/objectstack-formula/SKILL.md`: the §5 scope-table insert row says `null` with the two idioms, a new row covers the existing-row-not-in-hand case, the abort note under the table and the `OLD.x` / `ISCHANGED(x)` note under the legacy table name the column-read fault and the `previous != null &&` guard. One changeset `.changeset/23983-previous-null-on-insert.md` (`'@objectstack/skills': patch`, `Clause-②: no`). Premise re-verified on origin/main before editing: `ruleScope()` in packages/objectql/src/validation/rule-validator.ts binds `extra.previous = null` when previous === null and hook-wrappers.ts evaluates declarative hook conditions in that scope; content/docs/data-modeling/formulas.mdx already states the null-on-insert row. Draft PR #24174 to main, assignee marchtian, body line 1 `Fixes #23983`, line 2 `Clause-②: no`, with the `## 维护者速读(草稿)` section (席位意见 blank); the relay read the body back byte-identical (9581 sent / 9581 stored, one session-URL footer).",
"tests": "No package code changed, so no vitest suite is owed; the @objectstack/skills package has no test script and its build IS its test: `pnpm --filter @objectstack/skills build` exit 0 → 「sync-catalog self-test: 9 cases pass」 and 「65 file(s) across 10 skill(s) copied from skills/** into dist/skills and read back byte-identical」. Ship-proof at d0c7d6f13: `grep -rc 'UNBOUND where there is no prior state' packages/skills/dist/` → 0 in every one of the 65 files; new text present at packages/skills/dist/skills/objectstack-data/references/data-hooks.md:247 and packages/skills/dist/skills/objectstack-formula/SKILL.md:307. `pnpm --filter @objectstack/spec build` under scripts/pm/os-verify-lock.sh → 「VERDICT command-exit 0 · held the lock 98s」; `pnpm --filter @objectstack/spec check:generated` exit 0 → 「All 14 generated artifacts are up to date」, check:skill-docs ✓, check:skill-refs ✓ (SKILL.md change is body-only; nothing regenerated, nothing to commit). Control-character scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the three changed files: no hits. No ablation: no tested behaviour changed (text + changeset only).",
"gates": "Derived at 697301491 in the shared checkout with the two paths (32 commands) as ordered, then re-derived at the final commit d0c7d6f13 with no paths: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 44 commands (changeset families now included). Ran all 44 in the worktree, exit captured before any pipe (`bash -c \"$cmd\" > log 2>&1; EXIT=$?`): 42 exit 0 — among them node scripts/check-skills-token-ratchet.mjs (54 authored files within ceilings; data-hooks.md 10059/10066, formula SKILL.md 5511/5997) and its --self-test, pnpm --filter @objectstack/spec run check:skill-docs, check:skill-refs, pnpm check:role-word, check:corpus-claim-drift, check:skill-identifier-liveness, check:skill-compatibility, check:skill-frame-sync, check:doc-authoring, check:nul-bytes, check:pm-skill-id-lint, node scripts/check-adr-0087-registration.mjs --base origin/main, check-changeset-no-major.mjs --base origin/main, check-empty-changeset.mjs --base origin/main, pnpm check:published-files, check:objectui-changeset, check:pm-changeset-deadline-census, check:changeset-gate-self-tests, check:ci-filter-parity, check-closing-keyword-parity, check-comment-mask-corpus, check-doc-route-spelling --advisory, check:agent-test-spelling, check:cross-package-test-inputs, check:driver-memory-census, check:dts-closure, check:gitlink-declared, check:merge-driver, check:pm-governed-merges, check:refd-timer-probe, check:sourcemap-no-sources-content, check:watch-hint-literal, release-rehearsal-clone --self-test, release-pending-publish --self-test, check-dts-emitted --self-test. `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first exit 3 (PREREQUISITE NOT MET: formula + lint not built); after `turbo run build --concurrency=2 --filter=@objectstack/formula --filter=@objectstack/lint` under the verify lock it ran and exit 0 — recorded as exit 0 in ran.list. NOT MEASURED locally, exit 3 PREREQUISITE NOT MET both: `pnpm check:dual-build-cjs-loads` (reads dist of 92 packages) and `pnpm check:lean-entry-closure` (loads packages/objectql/dist/core.*) — whole-tree families named on every card, not readers of skills/**; CI's Lint & Repo Gates builds first and runs them. `node scripts/pm/dispatch-gates.mjs --ran ran.list` exit 0 → 「44 derived famil(ies) accounted for — 42 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3), 0 UNRUN」. Not run locally, CI-owned: test:repo, pnpm lint (eslint .), the type-check lanes, Test Core, Build Core, dogfood. PR #24174 check-runs on d0c7d6f13 at report time (36): completed success — Check PR Size, Check Documentation Links, filter, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch; completed skipped — Build Docs, Dogfood Regression Gate matrix, Dogfood Verify CLI, Temporal Conformance, Console Pin Gate, Packed-tarball smoke; in_progress — Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · ESLint, Lint · scoped (×3), Type Check · workspace / source gates / consumer gates / debt ledger, Test Core 1-6, Build Core, Check Changeset, Governed Surface Queue Guard, Dogfood Regression Gate, Auto Label. Not waited on.",
"line_budget": {
"skills/objectstack-data/references/data-hooks.md": { "lines_before": 997, "lines_after": 998, "tokens_before": 10066, "tokens_after": 10059, "ceiling": 10066, "headroom_before": 0, "headroom_after": 7, "paid_by": "deleted the restating sentence 「Those are opposite failures, so … the second one is loud」 and replaced the four-line never-on-insert rule with the shorter null-guard rule; no ceiling moved, re-wraps counted as nothing" },
"skills/objectstack-formula/SKILL.md": { "lines_before": 462, "lines_after": 465, "tokens_before": 5399, "tokens_after": 5511, "ceiling": 5997, "headroom_before": 598, "headroom_after": 486 },
"all_SKILL_md_lines": { "before": 4389, "after": 4392 },
"whole_skills_tree_lines": { "before": 13332, "after": 13336, "files": 65 },
"catalog_tokens": { "ratcheted_authored_before": 143907, "ratcheted_authored_after": 144012, "ceilings_sum": 154888, "bundle_total_before": 155093, "bundle_total_after": 155198 },
"token_convention": "scripts/check-skills-token-ratchet.mjs — ceil(utf8 bytes / 4), release-stamp line excluded"
},
"files_changed": [
"skills/objectstack-data/references/data-hooks.md",
"skills/objectstack-formula/SKILL.md",
".changeset/23983-previous-null-on-insert.md"
],
"deviations": [
"Worktree branched from origin/main at bd4276a91 after `git fetch` (the order named the shared checkout's 697301491; origin/main had moved by one docs-only commit, #23969). The ordered derivation with the two paths was run read-only in the shared checkout at 697301491 (32 commands); the final derivation and every gate run are at d0c7d6f13 on the bd4276a91 base. No conflict, no merge needed.",
"Two derived families NOT MEASURED locally (exit 3 PREREQUISITE NOT MET): check:dual-build-cjs-loads and check:lean-entry-closure need the whole tree / objectql built; declared to CI (see gates).",
"In addition to the L306 row and the L312–314 note the order enumerated, the `OLD.x` / `ISCHANGED(x)` note under the legacy-translation table in skills/objectstack-formula/SKILL.md (L374–378 at origin/main) was also rewritten: the card body names it (「the note under the migration table restate it」) and it said `previous.x` exists only where `previous` is bound, which is the same false statement. Same file, inside the declared file surface.",
"The checkout is shallow (`git rev-parse --is-shallow-repository` → true); no history-dated or ancestry claim was made from it.",
"Commit trailers use the AGENTS.md model-free pair (`Claude-Session: https://claude.ai/code/session_012PxNHU3dfov2hbpg9ycZ8q`, `Co-authored-by: Claude <noreply@anthropic.com>`); the harness-suggested model-named Co-Authored-By line and its two-line PR footer block were not used (os-dev.md: the harness reminder yields to this file; platform-readings: a stacked harness block stores as three footers).",
"No label write: a changeset is owed, so `skip-changeset` is not applicable and the order named no other label."
],
"mcp_calls": "0 — no MCP GitHub tool was called; card and comments read from the public issue page's embedded payload (backTimelineItems 12/12, hasNextPage false, newest Claim: 6234215977 names this branch; order 6234266379 present), PR state and check-runs read with `gh api` GET.",
"api_writes": "2 relay strokes through scripts/pm/fleet-write/dispatch.mjs (POST /repos/objectstack-ai/objectstack/dispatches, run as objectstack-fleet[bot]): ① pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24174/assignees [marchtian] (run 39037054837 success, read-back identical 9581/9581 bytes); ② comment → POST /repos/objectstack-ai/objectstack/issues/23983/comments (this report). Plus `git push` ×2 (empty branch probe, then d0c7d6f13) — not REST. No label-write, no PATCH, no MCP write.",
"open_questions": [],
"out_of_scope_findings": [
"class: b · Seam: spec:`previous` CEL binding (packages/objectql/src/validation/rule-validator.ts `ruleScope`, null on insert since b25d98a46f) → runtime:packages/objectql/src/hook-wrappers.ts declarative hook `condition` | consumer: skills/objectstack-formula/evals/cel-predicates-formulas.json:27 · reach: every agent that loads the objectstack-formula skill's eval fixture (it ships in @objectstack/skills dist, 65-file copy) · evidence: `grep -n 'Not usable on insert events' skills/objectstack-formula/evals/cel-predicates-formulas.json` → line 27, eval case 3 expected_output says 「Not usable on insert events (unbound `previous` aborts the write)」 — false on main: an insert-time `previous.status` read still faults but `previous == null` now judges the insert; the case's assertions (must_contain previous.status, record.status, !=) do not pin that sentence, so no eval reds on it today · ⛔ not edited here: the order allows no other skills/** file · proposed as a sub-issue of #23983 for the domain:skills seat (same file surface rules, token ceiling 1046 for that fixture) · dedupe words: cel-predicates-formulas previous unbound insert; formula eval expected_output insert previous null; skills eval fixture previous null",
"carrier: none · noted, not filed — other `unbound` hits in skills/** are unrelated and still true: skills/objectstack-formula/SKILL.md (form-view `visibleWhen` client-side unbound root falls back to true), skills/objectstack-ai/SKILL.md and skills/objectstack-query/rules/pagination.md (「unbounded」 numbers/queries); data-hooks.md's `ctx.previous` row (`undefined` on insert) is still true because only the CEL binding changed; docs/adr/0058 mentions `bulk_write_previous_unbound` as retired history."
]
}
File surface of claim 6234215977 amended for a patch round on PR #24174 (same branch claude/issue-23983-previous-null-on-insert, head d0c7d6f13 before the patch): + skills/objectstack-formula/evals/cel-predicates-formulas.json (eval case 3's expected_output, one sentence). Reason: the dev's report 6234524569 found the same false statement there — 「Not usable on insert events (unbound previous aborts the write)」 — and the file ships in @objectstack/skillsdist. The in-place fix's four conditions hold: same defect class as the card (text that says previous is unbound on insert), mechanical (one sentence, no assertion change), no other claim holds the file (no open PR touches skills/** but #24174), same gate family (the skills token ratchet row for that fixture, ceiling 1046, and the skills build). The dev was sent the patch round by the seat; it reports round 2 on this card with the new head. The PR's Fixes #23983 is unchanged; no sub-issue is filed.
{
"issue": 23983,
"status": "done",
"round": 2,
"branch": "claude/issue-23983-previous-null-on-insert",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24174",
"head": "058737c52",
"session": "session_012PxNHU3dfov2hbpg9ycZ8q",
"premise_still_valid": true,
"summary": "Patch round: the accepted in-place fix. `skills/objectstack-formula/evals/cel-predicates-formulas.json` case `id: 3` `expected_output` no longer says 「Not usable on insert events (unbound `previous` aborts the write).」; it now says 「On insert `previous` is `null`: the `previous.status` read faults and aborts the write, so to run on inserts guard with `previous != null &&`.」 Assertions untouched (must_contain previous.status / record.status / != ; must_not_contain ISCHANGED( / {previous.), no assertion added, valid JSON, no control characters. The file sat at zero token headroom (1046/1046, 4184 bytes), so the new sentence was paid for inside the same expected_output string: 「Writes the transition once as」→「Writes it once as」, 「the record's post-write state (true on every update of an already-escalated case)」→「the post-write state (true on every later update too)」, 「only a `previous` comparison says」→「only `previous` says」, 「per matched row on `before*` and `after*` update events, bulk included」→「per row on `before*`/`after*` update events, bulk too」, 「There is no」→「No」. Worktree recreated from origin/claude/issue-23983-previous-null-on-insert at d0c7d6f13 (Resume-from), nothing merged from main. Commit 058737c52 pushed to the same branch (no force). PR body not touched; PR #24174 still draft, assignee marchtian, head 058737c52.",
"mechanism_falsified": "The order's wording 「this transition condition is simply false there and aborts nothing」 does not hold for case 3's own condition: `previous.status != 'escalated' && record.status == 'escalated'` READS a column off `previous`, so on an insert it is the faulting shape (the changeset's `previous.stage` case), not the `previous != null` shape that is false and aborts nothing. The fixture text therefore states that the `previous.status` read faults and aborts the write and that the guard `previous != null &&` is what makes the condition runnable on inserts — the ruling's intent (state the truth on main), not its literal sentence.",
"tests": "`pnpm --filter @objectstack/skills build` exit 0 → 「sync-catalog self-test: 9 cases pass」, 「65 file(s) across 10 skill(s) copied from skills/** into dist/skills and read back byte-identical」. Ship-proof at 058737c52: `grep -rc 'Not usable on insert events' packages/skills/dist/` → 0 files with a hit; the new sentence present at packages/skills/dist/skills/objectstack-formula/evals/cel-predicates-formulas.json:27. `python3 -I json.loads` on the written file succeeded; `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` → no hits. No ablation (fixture text only).",
"gates": "Re-derived at 058737c52 with no paths: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 44 commands, byte-identical to round 1's list (diff exit 0): the fixture path adds no family — there is no evals validator in the derived set. Worktree re-installed (`pnpm install --frozen-lockfile` exit 0) and spec+formula+lint rebuilt under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, 4 tasks successful) before running. All 44 re-run at 058737c52, exit captured before any pipe: 42 exit 0 — node scripts/check-skills-token-ratchet.mjs (fixture 1046/1046, data-hooks.md 10059/10066, formula SKILL.md 5511/5997; 54 authored files within ceilings) and its --self-test (79 cases pass), pnpm --filter @objectstack/lint run check:doc-formula-expressions (exit 0 this round — formula+lint built first), pnpm --filter @objectstack/spec run check:skill-docs, check:skill-refs, pnpm check:role-word, check:corpus-claim-drift, check:skill-identifier-liveness, check:skill-compatibility, check:skill-frame-sync, check:doc-authoring, check:nul-bytes, check:pm-skill-id-lint, the changeset gates (check-adr-0087-registration / check-changeset-no-major / check-empty-changeset --base origin/main, check:published-files, check:objectui-changeset, check:pm-changeset-deadline-census, check:changeset-gate-self-tests) and the rest of the list. NOT MEASURED locally (exit 3 PREREQUISITE NOT MET, whole-tree build readers): pnpm check:dual-build-cjs-loads, pnpm check:lean-entry-closure — CI's Lint & Repo Gates runs them. `node scripts/pm/dispatch-gates.mjs --ran ran-r2.list` exit 0 → 「44 derived famil(ies) accounted for — 42 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)」, 0 UNRUN. PR #24174 check-runs on 058737c52 at report time (38): completed success — Lint & Repo Gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · ESLint, Lint · scoped (×3), TypeScript Type Check, Type Check · workspace / source gates / consumer gates / debt ledger, Build Core, Test Core 3/6–6/6, Check Changeset, Governed Surface Queue Guard, Dogfood Regression Gate, Check PR Size, Check Documentation Links, Auto Label, filter, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch; in_progress — Test Core 1/6, Test Core 2/6; skipped — Build Docs, Dogfood Regression Gate matrix, Dogfood Verify CLI, Temporal Conformance, Console Pin Gate, Packed-tarball smoke. Not waited on.",
"line_budget": {
"skills/objectstack-formula/evals/cel-predicates-formulas.json": { "lines_before": 69, "lines_after": 69, "bytes_before": 4184, "bytes_after": 4181, "tokens_before": 1046, "tokens_after": 1046, "ceiling": 1046, "headroom_before": 0, "headroom_after": 0, "paid_by": "tightened the same expected_output sentence (see summary); no ceiling moved" },
"skills/objectstack-data/references/data-hooks.md": { "lines": "997 → 998", "tokens": "10066 → 10059", "ceiling": 10066, "unchanged_this_round": true },
"skills/objectstack-formula/SKILL.md": { "lines": "462 → 465", "tokens": "5399 → 5511", "ceiling": 5997, "unchanged_this_round": true },
"catalog_tokens": { "ratcheted_authored_before_pr": 143907, "ratcheted_authored_after": 144012, "ceilings_sum": 154888, "bundle_total_before_pr": 155093, "bundle_total_after": 155198, "note": "unchanged by this round: 4184 → 4181 bytes rounds to the same 1046" },
"whole_skills_tree_lines": { "before_pr": 13332, "after": 13336 }
},
"files_changed": [
"skills/objectstack-formula/evals/cel-predicates-formulas.json"
],
"files_changed_cumulative": [
"skills/objectstack-data/references/data-hooks.md",
"skills/objectstack-formula/SKILL.md",
"skills/objectstack-formula/evals/cel-predicates-formulas.json",
".changeset/23983-previous-null-on-insert.md"
],
"deviations": [
"The round-1 worktree had already been removed when the patch order arrived; recreated from origin/claude/issue-23983-previous-null-on-insert at d0c7d6f13, dependencies re-installed and spec/formula/lint rebuilt before any gate ran.",
"Order wording falsified for this fixture's own condition (see mechanism_falsified); the text states the measured truth.",
"Two derived families NOT MEASURED locally (exit 3 PREREQUISITE NOT MET): check:dual-build-cjs-loads, check:lean-entry-closure — CI-owned.",
"PR body not updated (⛔ no PATCH per the order); it still describes the fixture line as out of scope in its Acceptance notes — the seat records the new head and this comment is the record of the in-place fix. The existing changeset text already covers the catalog, no second changeset.",
"Commit trailers use the AGENTS.md model-free pair; the harness-suggested model-named line was not used."
],
"mcp_calls": "0 — PR state and check-runs read with `gh api` GET.",
"api_writes": "1 relay stroke this round through scripts/pm/fleet-write/dispatch.mjs: comment → POST /repos/objectstack-ai/objectstack/issues/23983/comments (this report). Plus `git push` ×1 (058737c52) — not REST. No label write, no PATCH, no MCP write. Cumulative on this card: pr_create (POST /pulls + POST /issues/24174/assignees), 2 report comments.",
"line_budget_line_counts_note": "the fixture is JSON; 69 lines before and after",
"open_questions": [],
"out_of_scope_findings": []
}
ACCEPT → PR #24174 comment 6234803590 · seat domain:skills#1 · session_012PxNHU3dfov2hbpg9ycZ8q · 2026-10-17T14:05Z. Contract review PASS (Tier H face skills/**, in seat at CONTRACT_REVIEW_TIER) on head 058737c52: 6234710626. The PR waits for an authorized APPROVED review (os-zhuang / hotlong, requested; needs-user-decision on the PR); the seat lands it after. This card closes by the PR's Fixes.
APPROVED review 5558138338 by os-zhuang (2026-10-17T14:06Z, on head 058737c52, a GOVERNED_APPROVERS account); the same account flipped the PR ready and enqueued it (2026-10-17T14:06Z) — the maintainer's hand, so the seat's landing stroke was not needed
git ls-remote origin 'refs/heads/gh-readonly-queue/main/*' names 0 entry for PR #24174
origin/main
tip f1c0b5784 after fetch; git log -1 -- skills/objectstack-formula/SKILL.md skills/objectstack-data/references/data-hooks.md → f1c0b5784
Content on main
「UNBOUND where there is no prior state」 in data-hooks.md: 0 hits; 「Not usable on insert events」 in the eval fixture: 0 hits; the new 「whose prior state is not in hand」 row in formula SKILL.md: 1 hit
Card
state=closed reason=completed closed_at=2026-10-17T14:22Z labels=documentation,priority:p2,pm:dispatched,domain:skills,area:devpath — closed by the PR's Fixes; pm:dispatched removed in this act
State change: the closure keeps domain:skills, documentation, area:devpath, priority:p2; needs-user-decision leaves the PR (the approval arrived). subscribe_pr_activity on PR #24174 ended at the merge. Records: contract review 6234710626 (PASS, head 058737c52), 席位核对 6234803590 (ACCEPT), pointer 6234859318. The @objectstack/skills patch changeset ships with the next version PR.
Activity
objectstack-fleet commentedon Oct 16, 2026
Premise now holds ·
domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T17:04Z. PR #23971 (Fixes #23910) merged asb25d98a46f. Onmain, validation rules and declarative hook conditions bindprevioustonullon insert. So the two skill lines this card names are now false, and the card is actionable for its lane. ⛔ Not a claim.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Triage: grade,
documentation·priority:p2·area:devpath·pm:queue· the filer'sdomain:skillsstands · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T17:58ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
previousto null on insert so a rule that reads it answers with its own message #23971 (Fixes #23910) merged at 2026-10-16T17:02Z (b25d98a46f), anddomain:engineseat 2 confirmed it in6220637775. So the skill lines are false onmainfrom now on.previousis unbound on insert. An agent following it writes insert-time conditions around a fault that no longer happens.skills/**is Tier H, so the PR lands on aGOVERNED_APPROVERSapproval.objectstack-fleet commentedon Oct 17, 2026
Claim: PM loop round 2
Session:
session_012PxNHU3dfov2hbpg9ycZ8qAccount:
marchtian(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23983-previous-null-on-insertWorktree:
objectstack-issue-23983Domain:
domain:skillsSeat:
domain:skills#1File surface:
skills/objectstack-data/references/data-hooks.md(the 「previousis UNBOUND」 bullet and the 「unbound-previous」 clause, L247–248 and L280 atorigin/main697301491; 10066/10066 tokens, headroom 0 — paid by lossless deletion inside the file),skills/objectstack-formula/SKILL.md(the scope-table insert row L306 and the note under the table L312–314; 5399/5997 tokens, headroom 598) and one@objectstack/skillspatch changeset. ⛔ No otherskills/**file (stop on breach; explain in the report)Container & model:
M(published skill text, two files, truth read offmain),mode:subagent,model: CONTRACT_REVIEW_TIER(--tier --repo objectstack-ai/objectstack skills/objectstack-data/references/data-hooks.md skills/objectstack-formula/SKILL.mdat697301491: MANDATORY — both ⇢ 'skills/**', clause ①, verbatim 「必须 fable的还包括对外发布的skills」; no one-line exemption under this root)Clause-②: no
Responsibility:
n/a — not a defect cardin the product sense: published agent-facing text made false by PR #23971 (b25d98a46f, merged 2026-10-16T17:02Z); filing gate ① withreach:every agent loading either skillThread-read: 6221580524
Serial constraints cleared:
none. No open PR touchesskills/**at this read (every open PR's file list read 2026-10-17T13:04Z); the Version Packages PR (#23825) merged today and the next one re-stampsskills/*/SKILL.mdversion lines only — a different hunk from L306. The premise holds onmain:.changeset/23910-validation-previous-insert.mdis onorigin/mainandcontent/docs/data-modeling/formulas.mdxL211 states thepreviousrow.objectstack-fleet commentedon Oct 17, 2026
Dispatch order — posted 2026-10-17T13:09Z by the claiming seat (
session_012PxNHU3dfov2hbpg9ycZ8q, marchtian,domain:skills#1). The same text is the dev's prompt, runmode:subagentat model tierCONTRACT_REVIEW_TIER(theskills/**path mandate, clause ①; no one-line exemption under this root).Dispatch — issue #23983 (objectstack-ai/objectstack) ·
domain:skills· PMsession_012PxNHU3dfov2hbpg9ycZ8q(marchtian)You are the
os-devdeveloper for exactly one card: objectstack-ai/objectstack issue #23983.Claim:comment (6234215977) is your identity:Branch:claude/issue-23983-previous-null-on-insert, worktreeobjectstack-issue-23983. Before any edit, verify that the newestClaim:on the card names that branch; on a mismatch, stop and report.marchtian(pr_createwithassignees: ["marchtian"])..claude/agents/os-dev.mdandAGENTS.md, including the twoskills/**readings in the PR body (the edited files' before/after lines and the whole catalog's before/after; tokens too). This order carries only the increments./home/user/objectstackis atorigin/main697301491; shared — ⛔ never edit it; your worktree goes beside it. Scratch under/tmp/claude-0/-home-user/a273b78d-90a5-59e2-8b79-a91e3d54473d/scratchpad/issue-23983/.1. Ruling — none on wording; the merged runtime change is the specification
No maintainer ruling on this text. The truth the skills must state landed as PR #23971 (
Fixes #23910, mergedb25d98a46f, 2026-10-16). Its changeset, verbatim (git show b25d98a46:.changeset/23910-validation-previous-insert.md; consumed intopackages/objectql/CHANGELOG.mdL35–42 by today's version PR):content/docs/data-modeling/formulas.mdxL211 onmainalready states it: 「nullon insert, soprevious == nulljudges an insert; a column read off it there (previous.status) has no value and faults | validation, hook conditions and flows —nullon insert」.Deliverable: ONE draft PR to
main. Body line 1Fixes #23983, line 2Clause-②: no. Files:skills/objectstack-data/references/data-hooks.md(the bullet at L247–248 「previousis UNBOUND where there is no prior state … One case: insert events … write those overrecordalone」 and the L280 clause 「the unbound-previousandhas(...)bullets above are load-bearing」),skills/objectstack-formula/SKILL.md(the scope-table row L306 「Insert events … unbound — there is no prior state」 and the note L312–314 「Referencingpreviouswhere it is unbound … So write insert-event conditions overrecordalone」), and ONE changeset.changeset/23983-previous-null-on-insert.mdwith'@objectstack/skills': patchandClause-②: no(precedent: PR #23795's.changeset/23753-navigation-record-surface.md). ⛔ No otherskills/**file: other occurrences of 「unbound」 go in the report as out-of-scope findings with the grep evidence.skills/**is Tier H governed: carry## 维护者速读(草稿)(five parts, Chinese, 席位意见 blank), stay draft; the seat requests the authorized reviewers — you do not.2. PM mechanism assumptions — measure first; falsify and report where wrong
scripts/check-skills-token-ratchet.mjs):data-hooks.md10066/10066 (headroom 0) — every token the new sentences cost is paid by lossless deletion inside the same file;skills/objectstack-formula/SKILL.md5399/5997 (headroom 598). ⛔ Ceilings do not move; ⛔ re-wrapping is not payment; the PR body shows the before/after of each file and of the catalog.previousisnullin validation, hook conditions and flows (previous == null || …judges an insert,previous != null && …skips one); a column read off it on insert (previous.status) has no value and faults; an existing row whose prior state is not in hand keepspreviousunbound. The current advice 「write insert-event conditions overrecordalone」 is still safe but no longer the rule — rewrite it as thenullguard, do not delete the safety.packages/skills/scripts/sync-catalog.mjscopiesskills/**intopackages/skills/dist(a published package, hence the changeset);packages/spechascheck:skill-docs/check:skill-refsgates with generatorsgen:skill-docs/gen:skill-refsforSKILL.mdchanges — runpnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generatedand regenerate ONLY what it proves stale (--fix), commit any regenerated artifact;scripts/check-skill-examples.mjsandcheck:skill-top-level-keysread skill bodies. Derive the full list with dispatch-gates.skills/*/SKILL.mdversion lines every six hours (chore: version packages (next) #23825 merged today); your hunk at L306 is disjoint. If the base moves under you, mergeorigin/mainonly on a reported conflict.scripts/pm/tools only; budget 4 (git push,pr_createdraft with assignee,label-write— none expected: a changeset is owed, so ⛔ noskip-changeset;os-dev-report). Reportapi_writesandmcp_calls.3. PM suggested route — optional; measurement wins
data-hooks.mdL247: 「previousisnullon insert and UNBOUND only where a prior row exists but is not in hand.previous == nulljudges an insert andprevious != null && …skips one; a column read off it on insert (previous.done) has no value and faults, so guard the read, not the event.」 Pay by trimming the L280 clause to its still-true half (thehas(...)bullet is load-bearing; theprevioushalf now names thenullguard).objectstack-formula/SKILL.mdL306 row: 「Insert events (beforeInsert/afterInsert), validation rule on insert |null—previous == nullis how a condition judges an insert; a column read (previous.x) faults」; the L312–314 note drops 「Referencingpreviouswhere it is unbound」 as the first example and keeps the abort semantics for a typo'd key and a column read offnull.4. Verification you owe
Derive at
697301491:node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack skills/objectstack-data/references/data-hooks.md skills/objectstack-formula/SKILL.md; after the FINAL commit re-derive with no paths, run the local subset (the token ratchet and the spec skill-doc gates among them), capture each exit code before any pipe, reconcile with--ran, quotegit rev-parse --short HEADin report and PR body. Ship-proof: the new sentences appear inpackages/skills/dist/...afterpnpm --filter @objectstack/skills build, the old 「UNBOUND where there is no prior state」 does not.5. Report
Terminal JSON per
os-dev.md, first as anos-dev-reportissue comment. Machine-checkable:gates,line_budget(each file's lines and tokens before/after; the catalog total),deviations,files_changed.Four-axis framework (verbatim from
.claude/skills/pm-dispatch/SKILL.mdL241–261 atorigin/main697301491, file touchf7e836da1, md5abff5f852c8fd09b79ec0623439aa4f0)每个方案必须沿四条固定评估轴分析,这是决策分析的核心原则,不是可选项:
还是投机性能力面?判据要求实测(谁在写这个键、谁在读、
示例应用与真实部署的用法),「读起来像有用」不作数。这条轴会改变结论,不是陪衬。
contract-first),临时补丁式选项要明说长期代价。
契约收紧(严格 schema、publish 时响亮拒绝)优于消费端宽容(
??回退、静默容错)——宽容恰是 AI 批量犯错被掩盖的温床;声明即强制,绝不让 AI 声明一个运行时不兑现的能力。
从紧,无拉动的声明面按 implementation-first 处置,已发布零消费能力不因沉没成本豁免。
过渡也从紧 —— 创业阶段不渐进:废弃别名/拼写与能力退役默认立即退休,无分阶段
窗口、无双拼写宽限;staged 仅凭具名外部用户证据方可荐。新增门禁默认否:门禁是只减
不增的零件,例外只有维护者点名;荐新增门禁须引其点名原话,否则荐「不加」。
基本裁决原则:spec 声明 > 实现 > 文档面;声明而未兑现是实现缺口,补实现或退役,
⛔ 不在消费端收窄;未声明键随实现,文档随之。「声明即强制」只及 spec 与元数据契约、
运行时能力,⛔ 不及 AGENTS.md 这类给 agent 读的约定 —— 那些的读者是 agent,不是门禁。
推荐意见必须基于这四条轴给出理由;四轴冲突时如实呈现权衡,
交维护者拍板。
objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 23983, "status": "done", "branch": "claude/issue-23983-previous-null-on-insert", "pr": "https://github.com/objectstack-ai/objectstack/pull/24174", "session": "session_012PxNHU3dfov2hbpg9ycZ8q", "premise_still_valid": true, "summary": "Rewrote the two published skill passages that said `previous` is unbound on insert. `skills/objectstack-data/references/data-hooks.md`: the `previous` bullet now states `previous` is `null` on insert (`previous == null || …` judges an insert, `previous != null && …` skips one), that a column read off it there (`previous.done`) faults, and that only a write to an existing row whose prior state is not in hand leaves it unbound (fails closed); the abort warning's example, the load-bearing clause and the authoring consequence were rewritten to the `null` guard. `skills/objectstack-formula/SKILL.md`: the §5 scope-table insert row says `null` with the two idioms, a new row covers the existing-row-not-in-hand case, the abort note under the table and the `OLD.x` / `ISCHANGED(x)` note under the legacy table name the column-read fault and the `previous != null &&` guard. One changeset `.changeset/23983-previous-null-on-insert.md` (`'@objectstack/skills': patch`, `Clause-②: no`). Premise re-verified on origin/main before editing: `ruleScope()` in packages/objectql/src/validation/rule-validator.ts binds `extra.previous = null` when previous === null and hook-wrappers.ts evaluates declarative hook conditions in that scope; content/docs/data-modeling/formulas.mdx already states the null-on-insert row. Draft PR #24174 to main, assignee marchtian, body line 1 `Fixes #23983`, line 2 `Clause-②: no`, with the `## 维护者速读(草稿)` section (席位意见 blank); the relay read the body back byte-identical (9581 sent / 9581 stored, one session-URL footer).", "tests": "No package code changed, so no vitest suite is owed; the @objectstack/skills package has no test script and its build IS its test: `pnpm --filter @objectstack/skills build` exit 0 → 「sync-catalog self-test: 9 cases pass」 and 「65 file(s) across 10 skill(s) copied from skills/** into dist/skills and read back byte-identical」. Ship-proof at d0c7d6f13: `grep -rc 'UNBOUND where there is no prior state' packages/skills/dist/` → 0 in every one of the 65 files; new text present at packages/skills/dist/skills/objectstack-data/references/data-hooks.md:247 and packages/skills/dist/skills/objectstack-formula/SKILL.md:307. `pnpm --filter @objectstack/spec build` under scripts/pm/os-verify-lock.sh → 「VERDICT command-exit 0 · held the lock 98s」; `pnpm --filter @objectstack/spec check:generated` exit 0 → 「All 14 generated artifacts are up to date」, check:skill-docs ✓, check:skill-refs ✓ (SKILL.md change is body-only; nothing regenerated, nothing to commit). Control-character scan `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` over the three changed files: no hits. No ablation: no tested behaviour changed (text + changeset only).", "gates": "Derived at 697301491 in the shared checkout with the two paths (32 commands) as ordered, then re-derived at the final commit d0c7d6f13 with no paths: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 44 commands (changeset families now included). Ran all 44 in the worktree, exit captured before any pipe (`bash -c \"$cmd\" > log 2>&1; EXIT=$?`): 42 exit 0 — among them node scripts/check-skills-token-ratchet.mjs (54 authored files within ceilings; data-hooks.md 10059/10066, formula SKILL.md 5511/5997) and its --self-test, pnpm --filter @objectstack/spec run check:skill-docs, check:skill-refs, pnpm check:role-word, check:corpus-claim-drift, check:skill-identifier-liveness, check:skill-compatibility, check:skill-frame-sync, check:doc-authoring, check:nul-bytes, check:pm-skill-id-lint, node scripts/check-adr-0087-registration.mjs --base origin/main, check-changeset-no-major.mjs --base origin/main, check-empty-changeset.mjs --base origin/main, pnpm check:published-files, check:objectui-changeset, check:pm-changeset-deadline-census, check:changeset-gate-self-tests, check:ci-filter-parity, check-closing-keyword-parity, check-comment-mask-corpus, check-doc-route-spelling --advisory, check:agent-test-spelling, check:cross-package-test-inputs, check:driver-memory-census, check:dts-closure, check:gitlink-declared, check:merge-driver, check:pm-governed-merges, check:refd-timer-probe, check:sourcemap-no-sources-content, check:watch-hint-literal, release-rehearsal-clone --self-test, release-pending-publish --self-test, check-dts-emitted --self-test. `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first exit 3 (PREREQUISITE NOT MET: formula + lint not built); after `turbo run build --concurrency=2 --filter=@objectstack/formula --filter=@objectstack/lint` under the verify lock it ran and exit 0 — recorded as exit 0 in ran.list. NOT MEASURED locally, exit 3 PREREQUISITE NOT MET both: `pnpm check:dual-build-cjs-loads` (reads dist of 92 packages) and `pnpm check:lean-entry-closure` (loads packages/objectql/dist/core.*) — whole-tree families named on every card, not readers of skills/**; CI's Lint & Repo Gates builds first and runs them. `node scripts/pm/dispatch-gates.mjs --ran ran.list` exit 0 → 「44 derived famil(ies) accounted for — 42 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3), 0 UNRUN」. Not run locally, CI-owned: test:repo, pnpm lint (eslint .), the type-check lanes, Test Core, Build Core, dogfood. PR #24174 check-runs on d0c7d6f13 at report time (36): completed success — Check PR Size, Check Documentation Links, filter, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch; completed skipped — Build Docs, Dogfood Regression Gate matrix, Dogfood Verify CLI, Temporal Conformance, Console Pin Gate, Packed-tarball smoke; in_progress — Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · ESLint, Lint · scoped (×3), Type Check · workspace / source gates / consumer gates / debt ledger, Test Core 1-6, Build Core, Check Changeset, Governed Surface Queue Guard, Dogfood Regression Gate, Auto Label. Not waited on.", "line_budget": { "skills/objectstack-data/references/data-hooks.md": { "lines_before": 997, "lines_after": 998, "tokens_before": 10066, "tokens_after": 10059, "ceiling": 10066, "headroom_before": 0, "headroom_after": 7, "paid_by": "deleted the restating sentence 「Those are opposite failures, so … the second one is loud」 and replaced the four-line never-on-insert rule with the shorter null-guard rule; no ceiling moved, re-wraps counted as nothing" }, "skills/objectstack-formula/SKILL.md": { "lines_before": 462, "lines_after": 465, "tokens_before": 5399, "tokens_after": 5511, "ceiling": 5997, "headroom_before": 598, "headroom_after": 486 }, "all_SKILL_md_lines": { "before": 4389, "after": 4392 }, "whole_skills_tree_lines": { "before": 13332, "after": 13336, "files": 65 }, "catalog_tokens": { "ratcheted_authored_before": 143907, "ratcheted_authored_after": 144012, "ceilings_sum": 154888, "bundle_total_before": 155093, "bundle_total_after": 155198 }, "token_convention": "scripts/check-skills-token-ratchet.mjs — ceil(utf8 bytes / 4), release-stamp line excluded" }, "files_changed": [ "skills/objectstack-data/references/data-hooks.md", "skills/objectstack-formula/SKILL.md", ".changeset/23983-previous-null-on-insert.md" ], "deviations": [ "Worktree branched from origin/main at bd4276a91 after `git fetch` (the order named the shared checkout's 697301491; origin/main had moved by one docs-only commit, #23969). The ordered derivation with the two paths was run read-only in the shared checkout at 697301491 (32 commands); the final derivation and every gate run are at d0c7d6f13 on the bd4276a91 base. No conflict, no merge needed.", "Two derived families NOT MEASURED locally (exit 3 PREREQUISITE NOT MET): check:dual-build-cjs-loads and check:lean-entry-closure need the whole tree / objectql built; declared to CI (see gates).", "In addition to the L306 row and the L312–314 note the order enumerated, the `OLD.x` / `ISCHANGED(x)` note under the legacy-translation table in skills/objectstack-formula/SKILL.md (L374–378 at origin/main) was also rewritten: the card body names it (「the note under the migration table restate it」) and it said `previous.x` exists only where `previous` is bound, which is the same false statement. Same file, inside the declared file surface.", "The checkout is shallow (`git rev-parse --is-shallow-repository` → true); no history-dated or ancestry claim was made from it.", "Commit trailers use the AGENTS.md model-free pair (`Claude-Session: https://claude.ai/code/session_012PxNHU3dfov2hbpg9ycZ8q`, `Co-authored-by: Claude <noreply@anthropic.com>`); the harness-suggested model-named Co-Authored-By line and its two-line PR footer block were not used (os-dev.md: the harness reminder yields to this file; platform-readings: a stacked harness block stores as three footers).", "No label write: a changeset is owed, so `skip-changeset` is not applicable and the order named no other label." ], "mcp_calls": "0 — no MCP GitHub tool was called; card and comments read from the public issue page's embedded payload (backTimelineItems 12/12, hasNextPage false, newest Claim: 6234215977 names this branch; order 6234266379 present), PR state and check-runs read with `gh api` GET.", "api_writes": "2 relay strokes through scripts/pm/fleet-write/dispatch.mjs (POST /repos/objectstack-ai/objectstack/dispatches, run as objectstack-fleet[bot]): ① pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24174/assignees [marchtian] (run 39037054837 success, read-back identical 9581/9581 bytes); ② comment → POST /repos/objectstack-ai/objectstack/issues/23983/comments (this report). Plus `git push` ×2 (empty branch probe, then d0c7d6f13) — not REST. No label-write, no PATCH, no MCP write.", "open_questions": [], "out_of_scope_findings": [ "class: b · Seam: spec:`previous` CEL binding (packages/objectql/src/validation/rule-validator.ts `ruleScope`, null on insert since b25d98a46f) → runtime:packages/objectql/src/hook-wrappers.ts declarative hook `condition` | consumer: skills/objectstack-formula/evals/cel-predicates-formulas.json:27 · reach: every agent that loads the objectstack-formula skill's eval fixture (it ships in @objectstack/skills dist, 65-file copy) · evidence: `grep -n 'Not usable on insert events' skills/objectstack-formula/evals/cel-predicates-formulas.json` → line 27, eval case 3 expected_output says 「Not usable on insert events (unbound `previous` aborts the write)」 — false on main: an insert-time `previous.status` read still faults but `previous == null` now judges the insert; the case's assertions (must_contain previous.status, record.status, !=) do not pin that sentence, so no eval reds on it today · ⛔ not edited here: the order allows no other skills/** file · proposed as a sub-issue of #23983 for the domain:skills seat (same file surface rules, token ceiling 1046 for that fixture) · dedupe words: cel-predicates-formulas previous unbound insert; formula eval expected_output insert previous null; skills eval fixture previous null", "carrier: none · noted, not filed — other `unbound` hits in skills/** are unrelated and still true: skills/objectstack-formula/SKILL.md (form-view `visibleWhen` client-side unbound root falls back to true), skills/objectstack-ai/SKILL.md and skills/objectstack-query/rules/pagination.md (「unbounded」 numbers/queries); data-hooks.md's `ctx.previous` row (`undefined` on insert) is still true because only the CEL binding changed; docs/adr/0058 mentions `bulk_write_previous_unbound` as retired history." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
Claim amendment · seat
domain:skills#1·session_012PxNHU3dfov2hbpg9ycZ8q(marchtian) · 2026-10-17T13:39ZFile surface of claim 6234215977 amended for a patch round on PR #24174 (same branch
claude/issue-23983-previous-null-on-insert, headd0c7d6f13before the patch): +skills/objectstack-formula/evals/cel-predicates-formulas.json(eval case 3'sexpected_output, one sentence). Reason: the dev's report 6234524569 found the same false statement there — 「Not usable on insert events (unboundpreviousaborts the write)」 — and the file ships in@objectstack/skillsdist. The in-place fix's four conditions hold: same defect class as the card (text that sayspreviousis unbound on insert), mechanical (one sentence, no assertion change), no other claim holds the file (no open PR touchesskills/**but #24174), same gate family (the skills token ratchet row for that fixture, ceiling 1046, and the skills build). The dev was sent the patch round by the seat; it reports round 2 on this card with the new head. The PR'sFixes #23983is unchanged; no sub-issue is filed.objectstack-fleet commentedon Oct 17, 2026
os-dev-report
{ "issue": 23983, "status": "done", "round": 2, "branch": "claude/issue-23983-previous-null-on-insert", "pr": "https://github.com/objectstack-ai/objectstack/pull/24174", "head": "058737c52", "session": "session_012PxNHU3dfov2hbpg9ycZ8q", "premise_still_valid": true, "summary": "Patch round: the accepted in-place fix. `skills/objectstack-formula/evals/cel-predicates-formulas.json` case `id: 3` `expected_output` no longer says 「Not usable on insert events (unbound `previous` aborts the write).」; it now says 「On insert `previous` is `null`: the `previous.status` read faults and aborts the write, so to run on inserts guard with `previous != null &&`.」 Assertions untouched (must_contain previous.status / record.status / != ; must_not_contain ISCHANGED( / {previous.), no assertion added, valid JSON, no control characters. The file sat at zero token headroom (1046/1046, 4184 bytes), so the new sentence was paid for inside the same expected_output string: 「Writes the transition once as」→「Writes it once as」, 「the record's post-write state (true on every update of an already-escalated case)」→「the post-write state (true on every later update too)」, 「only a `previous` comparison says」→「only `previous` says」, 「per matched row on `before*` and `after*` update events, bulk included」→「per row on `before*`/`after*` update events, bulk too」, 「There is no」→「No」. Worktree recreated from origin/claude/issue-23983-previous-null-on-insert at d0c7d6f13 (Resume-from), nothing merged from main. Commit 058737c52 pushed to the same branch (no force). PR body not touched; PR #24174 still draft, assignee marchtian, head 058737c52.", "mechanism_falsified": "The order's wording 「this transition condition is simply false there and aborts nothing」 does not hold for case 3's own condition: `previous.status != 'escalated' && record.status == 'escalated'` READS a column off `previous`, so on an insert it is the faulting shape (the changeset's `previous.stage` case), not the `previous != null` shape that is false and aborts nothing. The fixture text therefore states that the `previous.status` read faults and aborts the write and that the guard `previous != null &&` is what makes the condition runnable on inserts — the ruling's intent (state the truth on main), not its literal sentence.", "tests": "`pnpm --filter @objectstack/skills build` exit 0 → 「sync-catalog self-test: 9 cases pass」, 「65 file(s) across 10 skill(s) copied from skills/** into dist/skills and read back byte-identical」. Ship-proof at 058737c52: `grep -rc 'Not usable on insert events' packages/skills/dist/` → 0 files with a hit; the new sentence present at packages/skills/dist/skills/objectstack-formula/evals/cel-predicates-formulas.json:27. `python3 -I json.loads` on the written file succeeded; `grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]'` → no hits. No ablation (fixture text only).", "gates": "Re-derived at 058737c52 with no paths: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` → 44 commands, byte-identical to round 1's list (diff exit 0): the fixture path adds no family — there is no evals validator in the derived set. Worktree re-installed (`pnpm install --frozen-lockfile` exit 0) and spec+formula+lint rebuilt under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, 4 tasks successful) before running. All 44 re-run at 058737c52, exit captured before any pipe: 42 exit 0 — node scripts/check-skills-token-ratchet.mjs (fixture 1046/1046, data-hooks.md 10059/10066, formula SKILL.md 5511/5997; 54 authored files within ceilings) and its --self-test (79 cases pass), pnpm --filter @objectstack/lint run check:doc-formula-expressions (exit 0 this round — formula+lint built first), pnpm --filter @objectstack/spec run check:skill-docs, check:skill-refs, pnpm check:role-word, check:corpus-claim-drift, check:skill-identifier-liveness, check:skill-compatibility, check:skill-frame-sync, check:doc-authoring, check:nul-bytes, check:pm-skill-id-lint, the changeset gates (check-adr-0087-registration / check-changeset-no-major / check-empty-changeset --base origin/main, check:published-files, check:objectui-changeset, check:pm-changeset-deadline-census, check:changeset-gate-self-tests) and the rest of the list. NOT MEASURED locally (exit 3 PREREQUISITE NOT MET, whole-tree build readers): pnpm check:dual-build-cjs-loads, pnpm check:lean-entry-closure — CI's Lint & Repo Gates runs them. `node scripts/pm/dispatch-gates.mjs --ran ran-r2.list` exit 0 → 「44 derived famil(ies) accounted for — 42 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)」, 0 UNRUN. PR #24174 check-runs on 058737c52 at report time (38): completed success — Lint & Repo Gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · PM dispatch-gates, Lint · ESLint, Lint · scoped (×3), TypeScript Type Check, Type Check · workspace / source gates / consumer gates / debt ledger, Build Core, Test Core 3/6–6/6, Check Changeset, Governed Surface Queue Guard, Dogfood Regression Gate, Check PR Size, Check Documentation Links, Auto Label, filter, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch; in_progress — Test Core 1/6, Test Core 2/6; skipped — Build Docs, Dogfood Regression Gate matrix, Dogfood Verify CLI, Temporal Conformance, Console Pin Gate, Packed-tarball smoke. Not waited on.", "line_budget": { "skills/objectstack-formula/evals/cel-predicates-formulas.json": { "lines_before": 69, "lines_after": 69, "bytes_before": 4184, "bytes_after": 4181, "tokens_before": 1046, "tokens_after": 1046, "ceiling": 1046, "headroom_before": 0, "headroom_after": 0, "paid_by": "tightened the same expected_output sentence (see summary); no ceiling moved" }, "skills/objectstack-data/references/data-hooks.md": { "lines": "997 → 998", "tokens": "10066 → 10059", "ceiling": 10066, "unchanged_this_round": true }, "skills/objectstack-formula/SKILL.md": { "lines": "462 → 465", "tokens": "5399 → 5511", "ceiling": 5997, "unchanged_this_round": true }, "catalog_tokens": { "ratcheted_authored_before_pr": 143907, "ratcheted_authored_after": 144012, "ceilings_sum": 154888, "bundle_total_before_pr": 155093, "bundle_total_after": 155198, "note": "unchanged by this round: 4184 → 4181 bytes rounds to the same 1046" }, "whole_skills_tree_lines": { "before_pr": 13332, "after": 13336 } }, "files_changed": [ "skills/objectstack-formula/evals/cel-predicates-formulas.json" ], "files_changed_cumulative": [ "skills/objectstack-data/references/data-hooks.md", "skills/objectstack-formula/SKILL.md", "skills/objectstack-formula/evals/cel-predicates-formulas.json", ".changeset/23983-previous-null-on-insert.md" ], "deviations": [ "The round-1 worktree had already been removed when the patch order arrived; recreated from origin/claude/issue-23983-previous-null-on-insert at d0c7d6f13, dependencies re-installed and spec/formula/lint rebuilt before any gate ran.", "Order wording falsified for this fixture's own condition (see mechanism_falsified); the text states the measured truth.", "Two derived families NOT MEASURED locally (exit 3 PREREQUISITE NOT MET): check:dual-build-cjs-loads, check:lean-entry-closure — CI-owned.", "PR body not updated (⛔ no PATCH per the order); it still describes the fixture line as out of scope in its Acceptance notes — the seat records the new head and this comment is the record of the in-place fix. The existing changeset text already covers the catalog, no second changeset.", "Commit trailers use the AGENTS.md model-free pair; the harness-suggested model-named line was not used." ], "mcp_calls": "0 — PR state and check-runs read with `gh api` GET.", "api_writes": "1 relay stroke this round through scripts/pm/fleet-write/dispatch.mjs: comment → POST /repos/objectstack-ai/objectstack/issues/23983/comments (this report). Plus `git push` ×1 (058737c52) — not REST. No label write, no PATCH, no MCP write. Cumulative on this card: pr_create (POST /pulls + POST /issues/24174/assignees), 2 report comments.", "line_budget_line_counts_note": "the fixture is JSON; 69 lines before and after", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 17, 2026
ACCEPT → PR #24174 comment 6234803590 · seat
domain:skills#1·session_012PxNHU3dfov2hbpg9ycZ8q· 2026-10-17T14:05Z. Contract review PASS (Tier H faceskills/**, in seat atCONTRACT_REVIEW_TIER) on head058737c52: 6234710626. The PR waits for an authorized APPROVED review (os-zhuang/hotlong, requested;needs-user-decisionon the PR); the seat lands it after. This card closes by the PR'sFixes.objectstack-fleet commentedon Oct 17, 2026
Landing — PR #24174 MERGED · seat
domain:skills#1·session_012PxNHU3dfov2hbpg9ycZ8q(marchtian) · read 2026-10-17T14:23Zos-zhuang(2026-10-17T14:06Z, on head058737c52, aGOVERNED_APPROVERSaccount); the same account flipped the PR ready and enqueued it (2026-10-17T14:06Z) — the maintainer's hand, so the seat's landing stroke was not neededgit ls-remote origin 'refs/heads/gh-readonly-queue/main/*'names 0 entry for PR #24174origin/mainf1c0b5784after fetch;git log -1 -- skills/objectstack-formula/SKILL.md skills/objectstack-data/references/data-hooks.md→f1c0b5784mainFixes;pm:dispatchedremoved in this actState change: the closure keeps
domain:skills,documentation,area:devpath,priority:p2;needs-user-decisionleaves the PR (the approval arrived).subscribe_pr_activityon PR #24174 ended at the merge. Records: contract review 6234710626 (PASS, head058737c52), 席位核对 6234803590 (ACCEPT), pointer 6234859318. The@objectstack/skillspatch changeset ships with the next version PR.