Skip to content

fix(spec): conformance-case notes and names state each decision in words instead of a tracker number (stage 6) - #21593

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20749-spec-strings-stage6
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20749-spec-strings-stage6

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20749
Clause-②: no

Stage 6 of the domain:spec lane's share of the runtime-string burn-down (ruling 5902360492, form D): class (c), the case notes and names of the eight shared conformance modules in packages/spec/src, with the two test seams that bind their text. Every rewritten note or name now states in words what the cited decision was, or drops a citation its sentence already explained. Text only.

What changed

  • 58 messages / 68 tracker ids / 40 distinct cards in eight modules: contracts/metadata-service-roundtrip-conformance.ts and, in data/, aggregation-conformance.ts, filter-comparand-type-conformance.ts, filter-logic-conformance.ts, filter-text-conformance.ts, filter-text-operator-declared-type.ts, temporal-conformance.ts, value-roundtrip-conformance.ts. Fifty-two are note / why texts and six are case names.
  • The selector seam (packages/lint/src/validate-empty-combinators.test.ts:275): selects the same four empty-combinator cases by a phrase the rewritten notes carry instead of #5322 (A3 below).
  • The name-pin seam (packages/services/service-analytics/src/__tests__/icontains-dialect-sql.test.ts:369): pins the renamed infix icontains case verbatim, the same strength (A4 below).
  • One @objectstack/spec patch changeset, Clause-②: no: the conformance tables ship in the package's dist (measured: the new note text is in dist/data/index.mjs; the two test files ship nowhere, since @objectstack/lint and @objectstack/service-analytics publish dist only and neither dist carries the seam text, with the package's own symbols as the positive control).
  • No generated artifact moves (check:generated green; the tables are values behind annotated types, so the API surface is unchanged).

Census at the base (A1)

Stage 3's instrument (census.cjs, byte-identical copy, md5 e5fe562290fcd1ac392bd560cb86d861) at base cc645f2385: class (c) is 58 messages / 68 ids in 8 files, 40 distinct cards, exactly stage 3's count (the [c-SELECTOR] 4 / 7 and [c-NAMEPIN] 1 / 1 rows included). Under the ~60-card bar, so one stage. Lines are the base's.

file (under packages/spec/src) line:id messages / ids field consumer
contracts/metadata-service-roundtrip-conformance.ts 194:#6725 · 202:#6725 · 280:#7378 · 296:#7378 · 304:#7378 5 / 5 why none prints it: both runners title a row by its id; the text is a reader's note in the shared table
data/aggregation-conformance.ts 342:#6409 · 398:#11065,#11151 · 407:#11065 · 417:#11152 419:#11249 · 535/550/561:#15546 · 577/596:#6401 9 / 11 note the assertion message in the driver-memory, driver-sql, driver-turso, driver-sqlite-wasm, objectql and mongodb-translation suites
data/filter-comparand-type-conformance.ts 153:#7956 · 160:#7872 · 272/282/289:#19757 (names) · 178:#5234 · 191:#6050 (notes) 7 / 7 5 name, 2 note names are test titles in five driver suites and in filter-save-door-face-parity.test.ts; notes are assertion messages
data/filter-logic-conformance.ts 339/362:#3774 · 420:#5322 · 426:#5322,#5134 · 432/438:#5322,#5297 · 470:#5298 · 476:#5146 · 503/509:#5298,#13356 · 539:#5299,#5962 · 561/567/573/579/595:#20444 16 / 22 note the assertion message in the eleven filter-logic harnesses; the one selector: validate-empty-combinators.test.ts:275 picks the four #5322 notes
data/filter-text-conformance.ts 300:#8934 (name) · 309:#8934 · 349:#6518,#6682 · 428:#4706 · 436:#5710,#6993 · 452:#5240 6 / 8 1 name, 5 note names are test titles in the text-conformance suites; the one name pin: icontains-dialect-sql.test.ts:369; notes are assertion messages
data/filter-text-operator-declared-type.ts 392:#8296 · 553:#8371 · 554:#8296 3 / 3 note the assertion message in engine-text-operator-declared-type-door.test.ts
data/temporal-conformance.ts 211:#3773 · 215/216:#3777 · 220:#20600 (row why) · 270:#3777 · 393:#3773 · 448/457:#1874 · 549:#3994 (case note) 9 / 9 4 why, 5 note a row why is seeded as DATA into each harness's why string column (never filtered on); a case note is an assertion message
data/value-roundtrip-conformance.ts 213:#11535 · 225/226:#11782 3 / 3 note printed in the TEST TITLE round-trips NAME (NOTE) in five driver suites

No non-test code reads any of these texts (every .note / .why read outside a test is another table's: RETIRED_FILTER_OPERATORS, the driver-conformance ledger).

Delivered (A2): each site, the decision as read, the new words

Each cited card was read with its body and every comment; the decision column names the record (comment id, or the landing record where the card carries none). Line = the base line of the edit anchor. "Old → new" shows only the words that changed.

file:line ids decision as read old → new
contracts/metadata-service-roundtrip-conformance.ts:194 #6725 #6725: card answers 404 here; landing record: changesets 1507ba3 / bbee302 (objectql and spec CHANGELOGs): MetadataFacade.register('object') wrote into a map none of its own object reads consulted #6725 would have failed → that catches an object write landing in a store none of the object reads consult — the hole a shipped implementation once fell through
contracts/metadata-service-roundtrip-conformance.ts:202 #6725 #6725: card answers 404 here; landing record: changesets 1507ba3 / bbee302 (objectql and spec CHANGELOGs): MetadataFacade.register('object') wrote into a map none of its own object reads consulted produced #6725 → let an object write land in the generic store while every object read looked elsewhere
contracts/metadata-service-roundtrip-conformance.ts:280 #7378 #7378: maintainer ruling 5261734580 (2026-08-12): row 1 refuse a name/data.name disagreement, row 2 one answer converged with check:meta-type-normalized, row 3 refuse non-object data #7378, maintainer 2026-08-12, row 1 → maintainer 2026-08-12, row 1 of the round-trip ruling
contracts/metadata-service-roundtrip-conformance.ts:296 #7378 #7378: maintainer ruling 5261734580 (2026-08-12): row 1 refuse a name/data.name disagreement, row 2 one answer converged with check:meta-type-normalized, row 3 refuse non-object data #7378, maintainer 2026-08-12, row 2 → maintainer 2026-08-12, row 2 of the round-trip ruling
contracts/metadata-service-roundtrip-conformance.ts:304 #7378 #7378: maintainer ruling 5261734580 (2026-08-12): row 1 refuse a name/data.name disagreement, row 2 one answer converged with check:meta-type-normalized, row 3 refuse non-object data #7378, maintainer 2026-08-12, row 3 → maintainer 2026-08-12, row 3 of the round-trip ruling
data/aggregation-conformance.ts:342 #6409 #6409: card body, executing the #6188 split ruling: count_distinct stays declared and is enforced as distinct non-null values on the SQL family #6409: → count_distinct was kept and enforced on every face, as distinct non-null values:
data/aggregation-conformance.ts:398 #11065 #11151 #11065: card answers 404 here; landing record: driver-memory changeset 2095040: avg/sum over a boolean answer the same numbers as every SQL face (a boolean is worth 1 or 0) · #11151: PR #12819 (triage 5446817173): driver-mongodb's boolean aggregand answers the ruled values too #11065/#11151: an arithmetic accumulator that drops booleans answers its identity 0 here — a plausible number, which → A boolean aggregand is worth 1 or 0 on every face — driver-memory and then driver-mongodb were both moved onto that answer: an arithmetic accumulator that drops booleans answers its identity 0 here — a plausible number, which
data/aggregation-conformance.ts:407 #11065 #11065: card answers 404 here; landing record: driver-memory changeset 2095040: avg/sum over a boolean answer the same numbers as every SQL face (a boolean is worth 1 or 0) #11065: the rate-over-a-flag-column shape (an SLA-violation rate, a win rate). → The rate-over-a-flag-column shape (an SLA-violation rate, a win rate) that driver-memory answered null for until it counted a boolean as 1 or 0.
data/aggregation-conformance.ts:417 #11152 #11249 #11152: maintainer ruling 2026-08-28, option A (changeset f6fa22c in the driver CHANGELOGs; decision request 5446817173): booleans aggregate as numbers, min/max answer 0/1 · #11249: ruling 5386670755 (false/true for min/max over a boolean), superseded by #11152's 2026-08-28 ruling #11152 ruling (2026-08-28): booleans aggregate as numbers with no per-aggregate exception, so the order statistics answer 0/1 in the same domain sum/avg answer in — not false/true (#11249, superseded) → Ruled 2026-08-28: booleans aggregate as numbers with no per-aggregate exception, so the order statistics answer 0/1 in the same domain sum/avg answer in — not the false/true an earlier ruling had chosen, which this one superseded
data/aggregation-conformance.ts:535 #15546 #15546: ruling 5572006116 (option A): a non-empty group whose aggregand is NULL in every row sums to 0 on every face #15546: → Ruled: a non-empty group whose aggregand is NULL in every row sums to 0.
data/aggregation-conformance.ts:550 #15546 #15546: ruling 5572006116 (option A): a non-empty group whose aggregand is NULL in every row sums to 0 on every face #15546: the reachability control for the → The reachability control for the all-NULL-sums-to-0
data/aggregation-conformance.ts:561 #15546 #15546: ruling 5572006116 (option A): a non-empty group whose aggregand is NULL in every row sums to 0 on every face #15546: the partial-null control → The partial-null control for the same ruling
data/aggregation-conformance.ts:577 #6401 #6401: dev report 5229051388 (PR #6849): GroupByNode.alias is enforced, not removed; every SQL face projects the group under alias ?? field #6401: t → A structured group node's alias names the projected group column on every face — the SQL faces that ignored it were made to honour it. T
data/aggregation-conformance.ts:596 #6401 #6401: dev report 5229051388 (PR #6849): GroupByNode.alias is enforced, not removed; every SQL face projects the group under alias ?? field #6401: the degenerate alias. Its twin above → The degenerate alias, under the same every-face alias rule. Its twin above
data/filter-comparand-type-conformance.ts:153 #7956 #7956: ACCEPT 5264821447: a measurement only; its control cell ({qty: {$eq: 100}}) returned the row on every driver, so the zeros were real answers #7956 control cell → control cell of the cross-driver comparand-type measurement
data/filter-comparand-type-conformance.ts:160 #7872 #7872: maintainer ruling 5265944890 and ACCEPT 5273103599 (PR #8234): a shared comparand-type door; a safe-range bigint narrows to its exact number, beyond 2^53 is refused (#7872) → at the shared comparand door
data/filter-comparand-type-conformance.ts:178 #5234 #5234: ACCEPT 5217619370 (PR #6296): a non-$field object member of $in/$nin and an object LIKE comparand are refused; each member is judged on its own right (#5234) → right, each judged like a scalar comparand
data/filter-comparand-type-conformance.ts:191 #6050 #6050: ruling B 5211349926: an undefined comparand is refused loudly; null keeps its meaning as the null predicate (#6050's untouched half) — → — the half left untouched when an undefined comparand was ruled a loud refusal — so
data/filter-comparand-type-conformance.ts:272 #19757 #19757: ruling 5793368540 (letter 乙): an array in the implicit-equality slot is refused at the shared face, for every driver at once (#19757) → at the shared face
data/filter-comparand-type-conformance.ts:282 #19757 #19757: ruling 5793368540 (letter 乙): an array in the implicit-equality slot is refused at the shared face, for every driver at once (#19757) → at the shared face
data/filter-comparand-type-conformance.ts:289 #19757 #19757: ruling 5793368540 (letter 乙): an array in the implicit-equality slot is refused at the shared face, for every driver at once too (#19757) → too, at the shared face
data/filter-logic-conformance.ts:339 #3774 #3774: defect: driver-sql OR-ed a $or branch's own keys and operators; rule: everything inside one filter object ANDs at every depth (card body) #3774: compiled → A $or combines its branches, never the keys inside one: a driver that OR-ed a branch's own keys compiled this
data/filter-logic-conformance.ts:362 #3774 #3774: defect: driver-sql OR-ed a $or branch's own keys and operators; rule: everything inside one filter object ANDs at every depth (card body) #3774: a single-key branch is miscompilable too — the operator map is looped with the same → A single-key branch is miscompilable too — that driver looped the operator map with the same OR
data/filter-logic-conformance.ts:420 #5322 #5322: maintainer ruling 5185589944: every face reduces an empty combinator to its boolean identity ($and:[] all rows, $or:[] none, {} TRUE, $not:{} none), whole tree #5322: a → Ruled: every face reduces an empty combinator to its boolean identity. A
data/filter-logic-conformance.ts:426 #5322 #5134 #5322: maintainer ruling 5185589944: every face reduces an empty combinator to its boolean identity ($and:[] all rows, $or:[] none, {} TRUE, $not:{} none), whole tree · #5134: defect repaired by PR #5243 (ACCEPT 5178806144): driver-sql dropped an empty $and/$or group instead of applying the boolean identity; empty $or and empty $not now compile to FALSE #5322/#5134: a disjunction of zero conditions matches nothing. Fail-closed for an RLS scope — a disjunct list that loops to zero items hides every row instead of exposing the table → Ruled: every face reduces an empty combinator to its boolean identity. A disjunction of zero conditions matches nothing. Fail-closed for an RLS scope — a disjunct list that loops to zero items hides every row instead of exposing the table, as a SQL lowering that dropped the empty group once did
data/filter-logic-conformance.ts:432 #5322 #5297 #5322: maintainer ruling 5185589944: every face reduces an empty combinator to its boolean identity ($and:[] all rows, $or:[] none, {} TRUE, $not:{} none), whole tree · #5297: dispatch 5184116664: read-scope-sql's { $not: {} } compiled to nothing (an RLS scope with no WHERE) and dropped a {} disjunct; both aligned to the boolean identity #5322: collapsing to the surviving branches instead compiles a = x — a silently NARROWED scope (#5297) → Ruled: every face reduces an empty combinator to its boolean identity, so {} is a TRUE disjunct. Collapsing to the surviving branches instead compiles a = x — a silently NARROWED scope, the answer the RLS read-scope compiler gave until it was aligned
data/filter-logic-conformance.ts:438 #5322 #5297 #5322: maintainer ruling 5185589944: every face reduces an empty combinator to its boolean identity ($and:[] all rows, $or:[] none, {} TRUE, $not:{} none), whole tree · #5297: dispatch 5184116664: read-scope-sql's { $not: {} } compiled to nothing (an RLS scope with no WHERE) and dropped a {} disjunct; both aligned to the boolean identity #5322: emitting nothing for it runs the query UNSCOPED — on an RLS lowering that is a permission bypass (#5297) → Ruled: every face reduces an empty combinator to its boolean identity, so NOT of {} is FALSE. Emitting nothing for it runs the query UNSCOPED — on an RLS lowering that is a permission bypass, which the read-scope compiler was until it compiled this to an always-false clause
data/filter-logic-conformance.ts:470 #5298 #5298: ruling 5202271174 (option A, confirmed 5204511921 item 4): $ne / $nin / $notContains are NULL-safe on the non-negated path, a no-value row is included #5298 → Ruled NULL-safe on every face
data/filter-logic-conformance.ts:476 #5146 #5146: maintainer ruling 5181102507: $not is NULL-safe on every driver; a row with no value does not satisfy the negated condition, so the negation returns it #5146: the same ruling reached through the combinator → The same NULL-safe ruling reached through the combinator, where it was first made for $not itself
data/filter-logic-conformance.ts:503 #5298 #5298: ruling 5202271174 (option A, confirmed 5204511921 item 4): $ne / $nin / $notContains are NULL-safe on the non-negated path, a no-value row is included #5298 option A → Ruled NULL-safe
data/filter-logic-conformance.ts:503 #13356 #13356: PR #13356: driver-memory's reference matcher realigned so a no-value row satisfies $nin / $notContains PR #13356 → it was realigned to the ruling
data/filter-logic-conformance.ts:509 #5298 #5298: ruling 5202271174 (option A, confirmed 5204511921 item 4): $ne / $nin / $notContains are NULL-safe on the non-negated path, a no-value row is included #5298 option A → Ruled NULL-safe
data/filter-logic-conformance.ts:509 #13356 #13356: PR #13356: driver-memory's reference matcher realigned so a no-value row satisfies $nin / $notContains PR #13356 → it was realigned to the ruling
data/filter-logic-conformance.ts:539 #5299 #5962 #5299: ruling 5219858433 item 2, kept by 5238865560: $exists means has a value (non-null), never key-presence, since SQL cannot tell a missing key from null · #5962: PR #5962 (ACCEPT 5205011148 on #5298): the NULL-safe operators and the $exists has-a-value reading shipped #5299 cell 2 / #5962: $exists means HAS A VALUE, never key-presence → Ruled: $exists means HAS A VALUE, never key-presence, because SQL cannot tell a missing key from a stored null
data/filter-logic-conformance.ts:561 #20444 #20444: card body executing ruling A on #20399 (5865693155): every compile surface answers $empty by the field's declared type; $empty: false is the exact complement #20444: null is empty on every row of the ruled table → Every face answers $empty by the field's declared type, and null is empty under every type's arm
data/filter-logic-conformance.ts:567 #20444 #20444: card body executing ruling A on #20399 (5865693155): every compile surface answers $empty by the field's declared type; $empty: false is the exact complement #20444: the exact complement → $empty: false is the exact complement by ruling
data/filter-logic-conformance.ts:573 #20444 #20444: card body executing ruling A on #20399 (5865693155): every compile surface answers $empty by the field's declared type; $empty: false is the exact complement #20444: $empty spells its NULL case out, so it → The ruled $empty arms spell their NULL case out, so $empty
data/filter-logic-conformance.ts:579 #20444 #20444: card body executing ruling A on #20399 (5865693155): every compile surface answers $empty by the field's declared type; $empty: false is the exact complement #20444: → Under the same declared-type arms,
data/filter-logic-conformance.ts:595 #20444 #20444: card body executing ruling A on #20399 (5865693155): every compile surface answers $empty by the field's declared type; $empty: false is the exact complement #20444: a face that lowers $empty → A face that lowers $empty to the field's declared-type arm
data/filter-text-conformance.ts:300 #8934 #8934: ruling A 5305708745: icontains joins the view and infix vocabularies; ilike/$ilike and icontains/$icontains are never aliased onto each other #8934 → ruled never an alias of ilike
data/filter-text-conformance.ts:309 #8934 #8934: ruling A 5305708745: icontains joins the view and infix vocabularies; ilike/$ilike and icontains/$icontains are never aliased onto each other capability (#8934) → capability, ruled when icontains joined the view and infix vocabularies
data/filter-text-conformance.ts:349 #6518 #6682 #6518: ACCEPT 5226386725 (PR #6706): the SQL family's $contains family is case-exact (GLOB on the SQLite dialects), per #4706 Q2 = A · #6682: ACCEPT 5251849449 and the memory half: the hardcoded case-folding flag came off driver-mongodb and driver-memory, the last two folding faces match. SQLite's LIKE folds ASCII — the defect #6518 replaced with GLOB on the SQLite dialects; a JS backend's equivalent is a RegExp carrying the i flag, which #6682 took off → match: the $contains family is case-sensitive on every backend, by ruling. SQLite's LIKE folds ASCII — the defect the SQL family replaced with GLOB on the SQLite dialects; a JS backend's equivalent is a RegExp carrying the i flag, since taken off driver-memory and driver-mongodb,
data/filter-text-conformance.ts:428 #4706 #4706: ruling B 5199214776: $regex retired under ADR-0049 with a loud refusal naming the replacement, $icontains added #4706 retired the operator over → $regex was retired over, by ruling, with a loud refusal naming $icontains
data/filter-text-conformance.ts:436 #5710 #6993 #5710: ACCEPT 5201171068 (PR #5812): plugin-auth's adapter stopped emitting bare $regex for better-auth contains, unblocking the $regex retirement · #6993: ACCEPT 5231388316 (PR #7054): the expired status claims were re-measured by executing each face (2026-08) and rewritten #5710 flipped that producer before any backend enrolled this case (re-verified 2026-08, #6993 → That producer was moved off $regex before any backend enrolled this case (re-verified 2026-08 by executing each face
data/filter-text-conformance.ts:452 #5240 #5240: maintainer ruling 5181107825: { field: {} } is refused loudly (INVALID_FILTER) on every backend, not read as TRUE or FALSE #5240 refused { field: {} } over → for which a field with zero operators, { field: {} }, is refused by ruling
data/filter-text-operator-declared-type.ts:392 #8296 #8296: standing ruling recorded in 5277439872: a where on a formula field is refused (INVALID_FIELD 400) because no driver materialises the column #8296 door refuses EVERY formula filter with INVALID_FIELD 400 whatever the returnType → unmaterializable-field door refuses EVERY formula filter with INVALID_FIELD 400 whatever the returnType (no driver stores a formula column)
data/filter-text-operator-declared-type.ts:553 #8371 #8371: ruling 5300373151 (option 2): a type-directed verdict on the dotted head segment; the structured/JSON head stays deliberately unjudged unjudged there, #8371 → left unjudged there, by ruling
data/filter-text-operator-declared-type.ts:554 #8296 #8296: standing ruling recorded in 5277439872: a where on a formula field is refused (INVALID_FIELD 400) because no driver materialises the column #8296 → the unmaterializable-field door
data/temporal-conformance.ts:211 #3773 #3773: defect: SQLite read an epoch-ms Field.datetime as a Julian day so date buckets were NULL; repaired by storage-aware bucketing (card body; cross-update 5099832227 on #3777) (#3773) → as SQLite bucketing once did
data/temporal-conformance.ts:215 #3777 #3777: defect: a bare-day $lte on a datetime column stopped at midnight and dropped the rest of the final day; the bound keeps the whole day (card body; 5099832227) by the #3777 bug → when a bare-day upper bound stopped at midnight
data/temporal-conformance.ts:216 #3777 #3777: defect: a bare-day $lte on a datetime column stopped at midnight and dropped the rest of the final day; the bound keeps the whole day (card body; 5099832227) by the #3777 bug → when a bare-day upper bound stopped at midnight
data/temporal-conformance.ts:220 #20600 #20600: triage direction 5886142901, landed PR #20643: the whole-day bound past 9999-12-31 is unbounded above and the drivers compile none (#20600) → and none is compiled
data/temporal-conformance.ts:270 #3777 #3777: defect: a bare-day $lte on a datetime column stopped at midnight and dropped the rest of the final day; the bound keeps the whole day (card body; 5099832227) #3777: the default dashboard window → The default dashboard window, whose bare-day $lte keeps the whole final day
data/temporal-conformance.ts:393 #3773 #3773: defect: SQLite read an epoch-ms Field.datetime as a Julian day so date buckets were NULL; repaired by storage-aware bucketing (card body; cross-update 5099832227 on #3777) #3773 famil → family of the SQLite bucketing defect that read an epoch-ms datetime as a Julian da
data/temporal-conformance.ts:448 #1874 #1874: the templates' date-equality family that surfaced ADR-0053, whose Phase 1 stores Field.date as its calendar day (ADR-0053 Surfaced-by line; card body, no comments) #1874: date == today silently matched nothing while dates were stored as instants. E → date == today silently matched nothing while dates were stored as instants; ADR-0053 stores a date as its calendar day, so e
data/temporal-conformance.ts:457 #1874 #1874: the templates' date-equality family that surfaced ADR-0053, whose Phase 1 stores Field.date as its calendar day (ADR-0053 Surfaced-by line; card body, no comments) from the #1874 family → that surfaced ADR-0053
data/temporal-conformance.ts:549 #3994 #3994: Field.time takes one canonical HH:MM:SS[.fff] text form on write, filter and read (ADR-0053 addendum D-C1..D-C3; driver-sql changeset 9774b78) #3994, measured → Measured before Field.time took one canonical HH:MM:SS[.fff] text form
data/value-roundtrip-conformance.ts:213 #11535 #11535: defect: a single-to-multi-value change kept the old text column on Postgres, so arrays came back as stringified literals; the drift detector now reports the base-type mismatch (claim 5395496220) #11535's exact shape → the exact shape a single-value column kept for a multi-value field corrupted
data/value-roundtrip-conformance.ts:225 #11782 #11782: landing 5406878917 (PR #12019): a declared boolean answers JSON booleans on every read door and dialect; MySQL's tinyint 1/0 leaked before #11782 read this back as 1 on MySQL → MySQL read this back as 1 until every read door presented a declared boolean as true/false
data/value-roundtrip-conformance.ts:226 #11782 #11782: landing 5406878917 (PR #12019): a declared boolean answers JSON booleans on every read door and dialect; MySQL's tinyint 1/0 leaked before #11782 → that MySQL read-back
lint: validate-empty-combinators.test.ts:275 #5322 #5322: maintainer ruling 5185589944: every face reduces an empty combinator to its boolean identity ($and:[] all rows, $or:[] none, {} TRUE, $not:{} none), whole tree #5322 → every face reduces an empty combinator to its boolean identity
analytics: icontains-dialect-sql.test.ts:369 #8934 #8934: ruling A 5305708745: icontains joins the view and infix vocabularies; ilike/$ilike and icontains/$icontains are never aliased onto each other #8934 → ruled never an alias of ilike

The selector seam (A3)

The filter over FILTER_LOGIC_CASES now tests (c.note ?? '').includes('every face reduces an empty combinator to its boolean identity') where it tested .includes('#5322'). The phrase is the #5322 ruling in words, and the four rewritten notes open with it. Measured with an AST extractor over the table (name and note of all 36 cases, then the selector applied exactly as the test applies it):

  • base cc645f2385 with #5322 → 4 of 36: $not of {} is FALSE — NOT TRUE, a {} branch is a TRUE disjunct and absorbs its $or, empty $and is TRUE — the AND identity, empty $or is FALSE — the OR identity.
  • head with the phrase → the same 4 of 36, the same names.
  • cross: the old selector on the head selects 0 (so the move is required), the new phrase on the base selects 0.
  • control outside the set: the broader word Ruled selects 8, the four plus $exists true selects exactly the valued rows, $ne …, $nin … and $notContains returns the rows with no value; none of those four is picked by the phrase.
  • mutation control on a scratch copy (anchor hit 1, replacement present 1, anchor left 0): dropping the phrase from the empty-$or note leaves 3, which the test's own guard (toEqual on the four sorted names, toBe(4)) turns red.

The test asserts exactly what it asserted before (the names pin, the count, and the row-set wording per case); only the selector moved. validate-empty-combinators.test.ts ran green: 21 of 21, the three identity cases included.

The name pin (A4)

'icontains (the infix/view spelling, #8934) lowers to $icontains — % stays a LITERAL through that door too' becomes 'icontains (the infix/view spelling, ruled never an alias of ilike) lowers to $icontains — % stays a LITERAL through that door too' in FILTER_TEXT_CASES, and the toEqual pin in icontains-dialect-sql.test.ts:369 moves to the new string verbatim. No other consumer keys on that name (searched the whole tree for the old name and its fragments: the source and the pin only); the other consumers use it as a test title. The file ran green: 16 of 16. The five renamed FILTER_COMPARAND_TYPE_CASES names have no pin anywhere; they are test titles only.

Text only (A5)

Stage 3's skeleton.cjs (one line changed: the TypeScript load path to this worktree), TypeScript 6.0.3: leg 1 an AST skeleton with every string's text masked (a + chain's adjacent string operands read as one string), leg 2 the text of every string group, each changed group required to carry a tracker id before and none after, every other group byte-identical. Base copies vs the committed files, 10 of 10 SAME on both legs, exit 0:

file tokens string groups changed
metadata-service-roundtrip-conformance.ts 1352 157 5
aggregation-conformance.ts 1268 133 9
filter-comparand-type-conformance.ts 1309 107 7
filter-logic-conformance.ts 1682 223 16
filter-text-conformance.ts 1093 137 6
filter-text-operator-declared-type.ts 1821 114 3
temporal-conformance.ts 2041 374 9
value-roundtrip-conformance.ts 1197 175 3
validate-empty-combinators.test.ts 2684 129 1
icontains-dialect-sql.test.ts 4345 217 1

Parse diagnostics 0 / 0 throughout. 58 changed groups in the eight modules, one per seam. No case's filter, input, expected rows, verdict, code, operator, dialect, order or count moves. Edits were applied by a script whose 62 anchors each had to hit exactly once before any write, and were read back from disk after it (each anchor gone, each replacement present once). Census after the edit: class (c) 0 / 0; non-test 118 → 60 messages, 297 → 229 ids; test strings unchanged (1804 / 1920).

Pins, titles and quotes (A6)

  • Pins moved: the two seams above, nothing else. Every old note and name was searched across the tree in 32-character windows: no test asserts a changed phrase; the remaining hits are code comments and other modules' own prose that share a phrase with an unchanged part of a note.
  • Titles that follow the text: the comparand-type and text-case names (test titles in the driver suites), and the VALUE_ROUNDTRIP_CASES notes (round-trips NAME (NOTE)). No skip list, ledger or snapshot names any of those titles.
  • Quotes: no content/docs/** page and no skills/** file quotes a changed note or name.
  • TEMPORAL_ROWS[].why is seeded into a string column (a varchar on Postgres and MySQL), so the four rewritten why texts stay short (164 characters at most).

Gates

Head 1427993cc3 (the merge of origin/main 901e7cf13a; that merge touched no file under packages/spec, packages/lint or packages/services/service-analytics, 0 diff lines there).

  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands: 11 paths vs merge base 901e7cf13, 153 changed lines; 88 derived commands, 88 exit 0 on 1427993cc3, each exit code written from $? before any pipe; --ran: "✓ dispatch-gates --ran: 88 derived famil(ies) accounted for — 88 run, 0 NOT-MEASURED".
  • pnpm --filter @objectstack/spec build exit 0 (the dependency closure is empty: no workspace dependencies); check:generated: "✓ All 15 generated artifacts are up to date".
  • pnpm --filter @objectstack/spec test: "Test Files 606 passed (606) / Tests 17952 passed | 1 todo (17953)"; typecheck exit 0 ("check:test-typecheck: OK — … 52 file(s) / 246 error(s) / 135 pinned signature(s) held").
  • pnpm --filter @objectstack/lint test: "Test Files 119 passed (119) / Tests 5620 passed (5620)"; typecheck exit 0.
  • pnpm --filter @objectstack/service-analytics test: "Test Files 175 passed (175) / Tests 4152 passed | 253 skipped (4405)"; typecheck exit 0; icontains-dialect-sql.test.ts alone 16 of 16.
  • pnpm check:doc-authoring (self-test, then the run): "✓ doc authoring guard: 17337 customer-facing string(s) across 1251 spec sources clean" and "sibling-package prose ids hold the baseline — 0 pinned site(s) … no growth, no burn-down unrecorded". pnpm check:nul-bytes: "check-nul-bytes: OK (scanned 9947 text file(s) …; no raw ASCII control bytes)".
  • Changeset gates: check-changeset-no-major.mjs, check-empty-changeset.mjs, check-adr-0087-registration.mjs (each --base origin/main) exit 0; with this body as the --event payload, see the report.
  • ESLint, a proven narrowing: eslint --no-inline-config --format json over the 10 changed TS files reads 10 files, 0 errors, 0 warnings; the population is ESLint's own (calculateConfigForFile returns a config for each, isPathIgnored false); invariance: eslint.config.mjs enables no type-aware linting (parserOptions.project / projectService null for all 10), so a string-text edit cannot move an untouched file's verdict. Repo-wide pnpm lint is CI's.
  • Spec test:repo was not run locally: none of its 51 files names a changed module or table.

Acceptance notes

Line budget

153 changed lines (+90 / -63) over 11 files vs merge base 901e7cf13 (dispatch-gates), under the 5000 human-merge threshold: the eight modules, the two test files (one line each) and the changeset. No generated file, no governed surface.


Generated by Claude Code

claude added 2 commits October 3, 2026 12:46
…rds instead of a tracker number (stage 6)

The class (c) texts of the spec lane's runtime-string burn-down: 58 case
notes and names across the eight shared conformance modules, each now
saying what the cited decision was, or dropping a citation its sentence
already explained. The lint test that selected the four empty-combinator
cases by a tracker substring now selects them by a phrase of the
rewritten notes, and the service-analytics test that pinned the infix
icontains case name pins the new name verbatim. Text only.

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/protocol/objectql/query-syntax.mdx (via FILTER_TEXT_CASES (symbol, a top-level const object))
What this run could not see
  • 2 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 — 138 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 901e7cf13abd61afb4990ebc1e3b9cd36cf9b33d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 35fc4d91777b2e146bd8e6ccc53de80ed6d704a7 — the merge of head 1427993cc332321d024701e843f92cae88b05414 into base 901e7cf13abd61afb4990ebc1e3b9cd36cf9b33d, 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 35fc4d91777b2e146bd8e6ccc53de80ed6d704a7 && git checkout 35fc4d91777b2e146bd8e6ccc53de80ed6d704a7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 901e7cf13abd61afb4990ebc1e3b9cd36cf9b33d 1427993cc332321d024701e843f92cae88b05414 && git checkout -B drift-repro 901e7cf13abd61afb4990ebc1e3b9cd36cf9b33d && git merge --no-ff 1427993cc332321d024701e843f92cae88b05414

node scripts/docs-audit/affected-docs.mjs --json 901e7cf13abd61afb4990ebc1e3b9cd36cf9b33d

⚠️ 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 901e7cf13abd61afb4990ebc1e3b9cd36cf9b33d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling labels Oct 3, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1427993cc332321d024701e843f92cae88b05414
Local-runs: none

① Derived judgments

Inputs. Read on GitHub through REST: card #20749 (body and all 21 comments, the triage 5903782079, the five earlier claims, ACCEPT and landing records, the pointer 5968465093, the stage-6 claim 5969176693 and the stage-6 dev report 5969963866 included), ruling 5902360492 on #20513, the stage-5 precedent (PR #21568 landed a3873542ca, its body and review 5967411495), PR #21593 (body, 11-file list, net diff against main at the merge base 901e7cf13a, its one bot comment), this head's check-runs at 14:14Z, 14:19Z and 14:23Z, and the 40 cited cards: 38 answered, with the 34 decision-record comments the dev names read in full, plus the bodies of #3774, #3773, #3777, #1874, #3994, #6409, #20444, #11535, #8296 and PRs #13356, #5962, #12819; #6725 and #11065 answer 404 here (confirmed), and were read from the landing records the dev names (1507ba3 / bbee302 in the objectql and spec CHANGELOGs, 2095040 in driver-memory's), with #11152's 2026-08-28 ruling read from f6fa22c in driver-sql's CHANGELOG and #1874's and #3994's from ADR-0053 (Surfaced-by line; D-C addendum). Repository reads were git show and git grep at origin/main, the merge base and this head; nothing built, run or re-run. An isolated subagent of the seat session rendered this record.

Text only; the accept set is unchanged. The GitHub net diff is byte-identical to git diff 901e7cf13a..1427993cc3 (index lines aside): 11 files, +90 / −63. Every one of the 63 removed lines and the 72 added code lines (the other 18 added lines are the new changeset) is a note:, why: or name: string literal, one operand of such a literal's + chain, the one const identityCases selector line, or the one pinned name in a toEqual list — checked mechanically over the diff text, 0 lines of any other shape. The four TEMPORAL_ROWS rows and the two VALUE_ROUNDTRIP_CASES rows that change are whole-line edits whose id / at / on / writerForm and name / column / wrote prefixes are identical on both sides; only the why / note text moves. No filter, expected, verdict, code, mustMention, function, field, kind, operator, dialect, case order or case count moves. 70 tracker-id tokens leave the removed lines (68 in the eight modules plus the two seam ids, 40 distinct cards — the dev's count); 0 id-like tokens stand on any added line. At the head no note: / why: / name: string in any of the eight files carries an id, so class (c) is 0 / 0 as the report says; the ids that remain in those files are code comments (#20234's). The head's second parent is the merge base, that merge touched nothing under packages/spec, packages/lint or packages/services/service-analytics, and main has not moved on any of the 11 files since.

Public surface. The eight tables are exported data values (@objectstack/spec's ./data entry, shipped in dist); a note / why / name is printed as an assertion message or a test title by the driver, objectql, analytics and lint suites, and TEMPORAL_ROWS[].why is seeded as data into a why: { type: 'string' } column. No non-test code reads these texts: every .note / .why read outside a test is RETIRED_FILTER_OPERATORS[op].why or the verify ledger's own rows. No export, type, key or acceptance changes, so the liveness, api-surface and type-check gates read green as expected (Spec property liveness, Type Check · source gates, consumer gates and debt ledger all success on this head).

The selector seam (A3) is right. Applying the old and the new selector as the test applies them, by text extraction over git show of FILTER_LOGIC_CASES (36 cases at both refs): base with #5322 → 4 ($not of {} is FALSE — NOT TRUE, a {} branch is a TRUE disjunct and absorbs its $or, empty $and is TRUE — the AND identity, empty $or is FALSE — the OR identity); head with the phrase → the same 4; cross: old selector on the head 0, new phrase on the base 0; control: Ruled on the head selects 8, the four plus the $exists / $ne / $nin / $notContains no-value cases, none of which the phrase picks. The test's own assertions are unchanged in the diff: the sorted four-name toEqual, toBe(4), and the per-case row-set wording; only the selector string moved. Same cases, same strength.

The name pin (A4) is right. FILTER_TEXT_CASES line 300 and icontains-dialect-sql.test.ts line 369 carry the identical new string; the toEqual list is otherwise unchanged (eight names, same order). A tree-wide search for the six old names and their distinctive fragments (infix/view spelling, #8934, #7956 control cell, dead (#7872), the three (#19757) names) finds nothing at the head; the five renamed comparand-type names are test titles only, with no skip list or ledger naming them.

The 58 rewritten notes and names, each against the cited decision. All 58 state the cited decision truly or drop a citation whose sentence already stated it. Three are drop-only (the #7378 rows, whose sentences already carried the ruling in words); 55 gain words naming the decision.

Site(s) Cited Read against Judgment
metadata-service-roundtrip-conformance.ts:194, :202 #6725 (404) objectql CHANGELOG 1507ba3: MetadataFacade.register('object', …) wrote through registerItem into the generic map while every object read resolved from objectContributors — a silent no-op; spec CHANGELOG bbee302: "the hole #6725 fell through" True, both
…roundtrip-conformance.ts:280, :296, :304 #7378 ruling 5261734580, maintainer 2026-08-12: row 1 a name / data.name disagreement is refused loudly; row 2 one answer, converged with check:meta-type-normalized; row 3 non-object data is refused (throw), fixing accept-then-drop without demanding storability True: citation dropped, each sentence already stated its row
aggregation-conformance.ts:342 #6409 card body executing the #6188 split ruling: count_distinct stays declared and gets enforced; dev report 5228634411: distinct NON-NULL values, measured, agreeing with objectql's fallback and analytics True (the shared case enforces it on every face it runs on)
aggregation-conformance.ts:398, :407 #11065 (404), #11151 driver-memory CHANGELOG 2095040: avg / sum over a boolean now answer the SQL faces' numbers instead of null / 0; PR #12819 (Fixes #11151): driver-mongodb's sum(flag) 0 → 3, avg(flag) null → 0.5, using the coercion #11065 landed on driver-memory True, both (order driver-memory then driver-mongodb is the landing order)
aggregation-conformance.ts:417 #11152, #11249 driver-sql CHANGELOG f6fa22c: maintainer ruling 2026-08-28, option A, superseding #11249's false / true — booleans aggregate as numbers, no per-aggregate exception; ruling 5386670755 on #11249 had chosen false / true True
aggregation-conformance.ts:535, :550, :561 #15546 ruling 5572006116 A: a non-empty group whose aggregand is NULL in every row sums to 0, on every face True, all three (the first is the ruling's own sentence)
aggregation-conformance.ts:577, :596 #6401 dev report 5229051388 (PR #6849): alias is enforced under ADR-0049, not removed; every SQL face projects the group under alias ?? field; the mongodb translation suite reads the group value from groupByAlias ?? groupBy too True, both
filter-comparand-type-conformance.ts:153 (name) #7956 ACCEPT 5264821447: a measurement; the control cell returning on every driver is what made the zeros real answers True
…comparand-type-conformance.ts:160 (name) #7872 ruling 5265944890: a shared comparand-type door in spec; ACCEPT 5273103599 Q1 = A: a safe-range bigint narrows to its number, "the crash cell dies at the door by conversion" True
…comparand-type-conformance.ts:178 #5234 ACCEPT 5217619370 (PR #6296): $in / $nin members judged by the same bindable-comparand allow-list as a scalar; objects refused True
…comparand-type-conformance.ts:191 #6050 ruling 5211349926 B: an undefined comparand is refused loudly; null keeps its null-predicate meaning True
…comparand-type-conformance.ts:272, :282, :289 (names) #19757 ruling 5793368540 乙: an array in the implicit or explicit equality slot is refused at the shared face, for every driver at once True, all three
filter-logic-conformance.ts:339, :362 #3774 card body: everything inside one filter object ANDs at every depth; driver-sql OR-ed a branch's own keys (a = x OR b = y → 1,2,3) and looped the operator map with the same flag True, both
filter-logic-conformance.ts:420, :426, :432, :438 #5322 (+#5134, #5297) ruling 5185589944: every face reduces an empty combinator to its boolean identity ($and:[] all rows, $or:[] none, {} TRUE, $not:{} none, whole tree); #5134 ACCEPT 5178806144: driver-sql dropped the empty group (measured full table) before applying the identity; #5297 dispatch 5184116664: read-scope-sql's { $not: {} } compiled to no WHERE (an unscoped RLS read) and dropped a {} disjunct (a narrowed scope); both aligned True, all four; the shared opening is the ruling in words
filter-logic-conformance.ts:470, :503, :509 #5298 (+#13356) ruling 5202271174 A, confirmed 5204511921 ④, re-affirmed 5238865560: $ne / $nin / $notContains NULL-safe, a no-value row is included, on every surface; PR #13356: driver-memory's matcher realigned on the missing-key $nin cell and both $notContains cells True, all three
filter-logic-conformance.ts:476 #5146 ruling 5181102507: $not NULL-safe on every driver — the first ruling of the family, which #5298 extended True
filter-logic-conformance.ts:539 #5299, #5962 ruling 5219858433 item 2, kept by 5238865560: $exists means has a value, never key-presence, because SQL cannot tell a missing key from null; PR #5962 shipped it True
filter-logic-conformance.ts:561, :567, :573, :579, :595 #20444 ruling A on #20399 (5865693155): each compile surface expands $empty by the field's declared type, with null in every type's arm; $empty: false is the exact complement (the card body carrying the ruling) True, all five
filter-text-conformance.ts:300 (name), :309 #8934 ruling 5305708745 A: icontains joins the view and infix vocabularies; constraint 1: never aliased onto ilike / $ilike True, both
filter-text-conformance.ts:349 #6518, #6682 ACCEPT 5226386725 (PR #6706): GLOB on the SQLite family, per #4706 Q2 = A; #6682 ACCEPT 5251849449 (mongodb half) and the card's memory half, closed completed 2026-08-11 after that half's claim; the case itself runs green on driver-memory True
filter-text-conformance.ts:428 #4706 ruling 5199214776 B: $regex retired under ADR-0049 with a loud refusal naming the replacement, $icontains added True
filter-text-conformance.ts:436 #5710, #6993 ACCEPT 5201171068 (PR #5812): plugin-auth's adapter moved off bare $regex; ACCEPT 5231388316 (PR #7054): the status claims re-measured by executing each of 11 faces, 2026-08 True
filter-text-conformance.ts:452 #5240 ruling 5181107825: { field: {} } is refused loudly (INVALID_FILTER) on every backend True
filter-text-operator-declared-type.ts:392, :554 #8296 card body and claim 5277439872: a standing ruling instance — a where on a formula field is refused (INVALID_FIELD 400) because no driver materialises a column for it True, both
filter-text-operator-declared-type.ts:553 #8371 ruling 5300373151 option 2: the structured/JSON head stays deliberately unjudged True
temporal-conformance.ts:211 (why), :393 #3773 card body: SQLite read an epoch-ms Field.datetime as a Julian day in buildDateBucketExpr, so buckets were NULL True, both
temporal-conformance.ts:215, :216 (why), :270 #3777 card body: a bare-day $lte on a datetime column stopped at midnight and dropped the rest of the final day; the default dashboard window hit it; cross-update 5099832227 True, all three
temporal-conformance.ts:220 (why) #20600 triage 5886142901: the whole-day bound past the last supported day is unbounded above and the drivers compile no upper bound True
temporal-conformance.ts:448, :457 #1874 ADR-0053 Surfaced-by: the templates' date-equality family (#1874), expires_on: { $in: [daysFromNow(30)] } named; Phase 1 stores Field.date as its calendar day True, both
temporal-conformance.ts:549 #3994 card body: 4 of 7 rows dropped; ADR-0053 D-C1..D-C3: Field.time takes one canonical HH:MM:SS[.fff] text form True
value-roundtrip-conformance.ts:213 #11535 card body: a single-value varchar column kept for a multi-value field; arrays stored and read back as the string literal True
value-roundtrip-conformance.ts:225, :226 #11782 landing 5406878917 (PR #12019): a declared boolean answers JSON booleans on every read door and dialect; MySQL's tinyint(1) had answered 1 / 0 True, both
validate-empty-combinators.test.ts:275 (selector) #5322 the ruling's sentence in words, as above True; same four cases
icontains-dialect-sql.test.ts:369 (pin) #8934 the renamed case, verbatim True; same strength

Census and boundary. The stage-5 landing (5967865492) left class (c) at 58 messages / 68 ids with the #5322 selector and the #8934 pin to move; the diff removes exactly those 68 ids from exactly those 58 texts and moves exactly those two seams. The claim's ⛔ Not this stage list holds: class (f) (api/error-code-ledger.zod.ts, kernel/public-auth-features.ts), every test string (the seam files' own titles (#5322/#5659) and [#16028] included), the two .mjs gate scripts, migrations/registry.ts and all comments are untouched.

② Semver level

@objectstack/spec patch, Clause-②: no — right. Text changes on exported data values with no key, shape, default, export, type or acceptance change; nothing published grows or narrows. The changeset's frontmatter is '@objectstack/spec': patch, Clause-②: no sits at a line start, and its body states what a tool or test keyed on an old note or name must do; its four example bullets and the six-name list each match the diff. The PR body carries Part of #20749 and Clause-②: no at line starts and no closing keyword (Check Changeset, "Part-of PR must not also close its card", "The card this PR closes must claim this branch" and the two single-writer guards all success). The two test files publish nowhere (@objectstack/lint and @objectstack/service-analytics ship dist only), so no second changeset is owed. Same level and clause as stages 3 to 5, whose review 5967411495 passed the identical shape.

③ Boundary flags

  1. Proof tool — stage 3's skeleton.cjs with one load-path line changed, census.cjs byte-identical; md5s stated. Not re-run here. The text-only finding above rests on reading every hunk and a mechanical shape check over the diff text; nothing in it depends on the tool.
  2. Two cited cards answer 404 (MetadataFacade.register('object', …) writes where neither of its own object reads look #6725, avg over a boolean field returns null on driver-memory and a number on sqlite — same rows, same dataset, same executor #11065) — confirmed by REST here. Read from the landing records the dev names; each rewritten sentence states what those records say (judged in ①). Clear, not unclear.
  3. Decisions in shipped records rather than card comments — [finding] AGGREGATION_ROWS has no boolean column, so the cross-driver aggregation conformance family cannot see a boolean aggregand on any face #11152 (f6fa22c, matching the source comment above the boolean cases), Field.time repeats the #3912 pattern: writes unnormalised, repaired only on read — window filters and ORDER BY are silently wrong on SQLite #3994 and [P2] Provide a declarative time-relative trigger (avoid fragile date-equality on record-change) #1874 (ADR-0053), drivers: implement count_distinct in the SQL family — enforcement half of the #6188 ruling #6409 (card body executing [spec] AggregationFunction 声明 8 个,SQL 族只实现 5 个 —— count_distinct / array_agg / string_agg 按 ADR-0049 enforce-or-remove 定去留 #6188's split), measure: how each driver answers the six non-JSON comparand types (#7872 restart condition b) #7956 (a measurement, its ACCEPT), fix(driver-memory): a no-value row satisfies $nin / $notContains in the reference matcher (#13166) #13356 / fix(drivers,analytics,formula): $ne / $nin / $notContains 在 $not 之外也 NULL-safe (#5298) #5962 / fix(driver-mongodb): a boolean aggregand answers the ruled values on this face too #12819 (PRs whose landed effect is the decision). Each read; each sentence states it.
  4. Citation form — three drop-only (MetadataFacade answers three register → get round-trip cases differently from every other shipped IMetadataService #7378), 55 with new words, as the report counts; verified site by site. The form stages 3 to 5 accepted.
  5. "on every face" on the drivers: implement count_distinct in the SQL family — enforcement half of the #6188 ruling #6409 and [finding][drivers] GroupByNode.alias 只有内存分桶认,三个 SQL 面一律忽略 —— 同一个 aggregate 走下推与走回退拿到不同的列键 #6401 notes — the cited cards' own legs reached the SQL family; the notes speak as the shared table does, which runs those cases on every face (mongodb's translation suite included, reading groupByAlias ?? groupBy and answering count_distinct(flag) = 2 in PR fix(driver-mongodb): a boolean aggregand answers the ruled values on this face too #12819). Judged true as the contract the case enforces; recorded as the reading taken.
  6. TEMPORAL_ROWS[].why as seeded data — the four rewritten texts are at most 163 characters; the harnesses declare why: { type: 'string' }, knex's varchar(255) on Postgres and MySQL. Temporal Conformance (live PG + MySQL) completed success on this head, which measures it.
  7. Check-runs — at 2026-10-03T14:23Z this head reads 25 success, 3 skipped by design (Build Docs, Console Pin Gate, the opt-in packed-tarball smoke), 5 in progress (Lint & Repo Gates, Test Core 1/6, 2/6 and 6/6, Type Check · workspace), 0 failed. Build Core, Dogfood Verify CLI, the three Dogfood Regression Gate shards, Temporal Conformance, Test Core 3/6, 4/6, 5/6 and the three other Type Check jobs completed success. The five in-progress runs are an honest reading, not a pass: this verdict rests on the diff, the cards and the completed gates, and the seat reads the five to completion before enqueue, as stage 5's did.
  8. Docs Drift Check — advisory; its one page (protocol/objectql/query-syntax.mdx) is anchored on the FILTER_TEXT_CASES symbol, not on a text. A fragment search over content/docs/**, skills/** and packages/** finds no surviving quote of any changed note or name outside the eight modules and the pin.
  9. Hot files and main movement — origin/main merged once (the head is the merge, second parent 901e7cf13a = merge base; 0 lines under the three packages); main has not moved on any of the 11 files since; GitHub reads mergeable: true. Nothing owed.
  10. Governed surface — NOT governed (Governed Surface Queue Guard success; no .claude/**, skills/**, ADR, NORTH-STAR, AGENTS.md or generated path). Ordinary queue once every check is complete. The PR is a draft; the seat flips it.
  11. Serial constraints — held, as judged in ①'s boundary paragraph; no gate is added or loosened (no scripts/** or .github/** path).
  12. Ruling order and the ledger — class (c) texts print to a developer measuring a driver, after classes (a) and (b), before (f) and the test strings, which is the ruling's order; packages/spec sits outside scripts/doc-authoring-prose-id.baseline.json, so the ledger is rightly untouched.
  13. open_questions and out_of_scope_findings — both empty; none owed. The report's "left for later stages" list matches the claim's exclusions.
  14. Trailers and the .eslint-pop-tmp.mjs helper — process notes; not a contract matter and not re-read here.

Implemented-by: claude/issue-20749-spec-strings-stage6
Reviewed-by: session_01YDt3PzwfrkuFzUBF89WPmM

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT — PR #21593 @ 1427993c (stage 6 of #20749)

domain:spec seat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of the stage-6 claim 5969176693 · 2026-10-03T14:42Z

Left on this card after this lands: class (f), the 17 internal registry rationales; the test strings (1,804); the two .mjs gate scripts; and migrations/registry.ts's live rationale numbers (5968465093, split at the step boundary). Under the maintainer's 「当前任务处理完就下班」 (5969273857), no further stage is claimed this shift.

Next: ready, auto-merge, the queue.


Generated by Claude Code

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 protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants