Skip to content

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams - #13839

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where
Aug 31, 2026
Merged

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams#13839
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13764

The stub engine in packages/rest/src/rest-server-meta-read-org-scope.test.ts discarded
opts.where on bothsys_metadata_history seams — findOne answered null
unconditionally and find handed back every history row unfiltered — and was also
limit-blind, one of the 168 doubles check:objectql-double-limit grandfathers. Both
seams are repaired on one edit, plus the matcher's silent-combinator hole.

There was no live defect and no vacuous claim in the file as it stood: it drove only
PUT /meta/:type/:name, GET /meta/:type/:name and GET /meta/:type, and every
assertion it made its stub genuinely kept. What is repaired is a trap — the file whose
name reads like the obvious home for a /history org-scoping assertion was the one file
where that assertion could not be tested.

The deliverable is the discrimination control, not the green gate

SysMetadataRepository.history() and diffMetaItem filter organization_id by strict
equality and post-filter nothing, so over an unfiltered stub the org predicate is a no-op.
The control mutates the /history door to not forward the organization — the exact
line ...(historyOrganizationId ? ...) inside GET /meta/:type/:name/history — and reads
one assertion, serves the org-scoped change log of an item the active org authored, in
all four cells.

stuborg at the doorthat assertion
old, unfilteredforwardedPASS
old, unfiltereddroppedPASS — the vacuity, measured directly
new, partitionedforwardedPASS
new, partitioneddroppedFAILexpected +0 to be 2

Verbatim, cell 2 (old stub, org dropped):

 ✓ … #13764 the history seams of this harness honour the org partition >
serves the org-scoped change log of an item the active org authored 4ms

and cell 4 (new stub, org dropped):

 × … serves the org-scoped change log of an item the active org authored 11ms
→ the door answered an empty change log for an item whose org partition holds
two events: expected +0 to be 2

Green in both states before, red in exactly one state after. That is the discriminating
power the card is about.

rest-server.ts is held hard-serial by #13280 and nothing lands in it here. It was
mutated transiently for the measurement only, with the blob hash equal to its HEAD blob
before and after, the removed and injected text counted in both directions, and an empty
git diff HEAD on the restore leg.

The second axis of the control

The two cross-tenant cases were not writable at all against the old stub — they were
red over it with the org forwarded and with it dropped, because an unfiltered read hands
org B org A's rows whatever the door does. They are green over the new stub in both
states. That makes does not serve org A history to org B the assertion that holds the
stub partitioned
: the positive case stays green if anyone un-partitions the stub again,
and only the cross-tenant one reddens.

Was findOne's unconditional null load-bearing? Measured: no

Predicted before running, from the production call sites — the only callers of findOne
on that table are getByHash, restoreVersion and resolveMetaItemOrgScope, none of
which a PUT reaches; the write path reads history through find, in nextEventSeq and
nextItemVersion. The sibling harness landed in #13756 implements this exact seam and
drives the same PUT path green, which is the positive control for the prediction.

Confirmed: with the seam honouring the where, all 15 pre-existing assertions stay
green and the file runs 18/18. So the implement form was safe and the refusal form was not
needed on this seam.

Which form on each axis, and why

  • where on find — implemented, not refused. Refusal was not available: an
    org-scoping assertion has to be able to read the partition, and a seam that throws on
    every predicate is red in both states rather than discriminating.
  • where on findOne — implemented, matching the sibling, once the null was
    measured not load-bearing.
  • limit — applied after the filter and by presence, typeof opts?.limit === 'number',
    on both table branches, which is check:objectql-double-limit's shape rule. Every call
    this fixture makes passes no bound, so every existing assertion keeps its meaning.
  • unknown WHERE combinators — refused loudly rather than read as a column name. This
    is the one place refusal is right: a double that answers $and by looking for a column
    literally called $and returns a well-formed wrong answer, which is the same silent
    class one level down.

The door's own ?limit= contract and the /diff and non-overridable controls are pinned
by rest-server-meta-history-diff-org-scope.test.ts and are deliberately not copied
here — the gate's own probes exercise the shape rules of the double itself, and
duplicating the sibling's door pins would be redundant coverage rather than new
information.

Baseline ratchet

scripts/objectql-double-limit.baseline.json loses its rest-server-meta-read-org-scope
entry. Not a new baseline row — a ratchet down, which the gate requires in the same
PR; it reconciles in both directions and errored with
baselined file is clean or gone -- ratchet DOWN until the entry was deleted.

Verification

Union derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack after
merging origin/main and after the last edit, harvested with --commands. Everything
below ran at aab1254550, tree clean.

union named 33, ran 33, unreconciled 0

by exact comm -23 of the sorted union against the sorted ran-list — no prefix or fuzzy
matching, and no arithmetic on a hand-kept counter. comm -13 is empty too.

One non-zero exit, and it is NOT MEASURED rather than a finding, in the gate's own
words:

check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved `turbo run test`
log, and no log was named. … the local reading for this gate is NOT MEASURED.
⛔ It is not a red, and there is nothing here to fix. (exit 3, distinct from a finding's 1)

The two gates this change is really answerable to, quoting their own verdict lines:

OK ObjectQL double `limit` conformance holds: 296 double(s) graded, 97 apply the
caller's bound or refuse it loudly.
167 limit-blind, 32 shape-breaking and 55 unjudged double(s) in 252 grandfathered
file(s); none new.
baseline key set verified against 16c3601: no files added.
✓ where-matcher conformance holds: 321 matcher(s) discovered, 321 answer the combinator
battery correctly or refuse it loudly (202 refuse).

Also run, all exit 0:

  • pnpm --filter @objectstack/rest test164 files, 2764 tests, all passed
  • pnpm --filter @objectstack/rest typecheckcheck:test-typecheck: OK. The edited
    file was confirmed present in that program by tsc --listFiles (1 hit, with the sibling
    harness as the positive control), so the green really covers it. The 2 remaining tsc
    errors are the pre-existing pinned src/rest.test.ts debt held by The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454; zero come
    from this diff.
  • pnpm lint — the whole repo, eslint . --no-inline-config, exit 0 in 85s. No
    narrowing claimed and none needed.
  • pnpm check:nul-bytesOK (scanned 7598 text file(s) … no raw ASCII control bytes),
    plus a direct control-byte scan over both edited files.

Changeset

Proposed: the skip-changeset label, and this is a reading rather than a preference.
packages/rest's manifest declares files: ["dist", "README.md", "CHANGELOG.md"], and
npm pack --dry-run reports an 8-file tarball that does not contain the edited test
file, with dist/index.js present as the control that the reading is real. The other file
is a CI-internal gate ledger. So the change publishes nothing from any package.

The seat rule says to prefer release noise over a silent gate bypass when the two readings
conflict — here they do not, and the bypass surface was measured rather than assumed:
Check Changeset runs exactly three substantive steps, and this diff adds no changeset at
all (so there is no empty-frontmatter file to reject) and declares no breaking change (so
no ADR-0087 disposition is owed). The one thing that stretches the closed list is that the
baseline ledger sits in scripts/ rather than scripts/pm/; it is the gate's mandatory
same-PR companion to the test edit and cannot be split out.

Generated by Claude Code


Generated by Claude Code

…harness
Measurement checkpoint: over the CURRENT unfiltered stub the positive case is
green (vacuously) and the two cross-tenant controls are red, which is what
proves those controls are not writable against a stub that ignores the where.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
…th history seams
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
… repaired stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 86e765a7704eff4de2eb070330ff66a0e211c3aapackageMentionDocs.

@os-steve
os-steve marked this pull request as ready for review August 31, 2026 15:06
@os-steve
os-steve added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 24ac73dAug 31, 2026
37 checks passed
@os-steve
os-steve deleted the claude/issue-13764-meta-read-org-scope-stub-honours-where branch August 31, 2026 15:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams by claude[bot] · Pull Request #13839 · objectstack-ai/objectstack · GitHub
Skip to content

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams - #13839

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where
Aug 31, 2026
Merged

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams#13839
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13764

The stub engine in packages/rest/src/rest-server-meta-read-org-scope.test.ts discarded
opts.where on bothsys_metadata_history seams — findOne answered null
unconditionally and find handed back every history row unfiltered — and was also
limit-blind, one of the 168 doubles check:objectql-double-limit grandfathers. Both
seams are repaired on one edit, plus the matcher's silent-combinator hole.

There was no live defect and no vacuous claim in the file as it stood: it drove only
PUT /meta/:type/:name, GET /meta/:type/:name and GET /meta/:type, and every
assertion it made its stub genuinely kept. What is repaired is a trap — the file whose
name reads like the obvious home for a /history org-scoping assertion was the one file
where that assertion could not be tested.

The deliverable is the discrimination control, not the green gate

SysMetadataRepository.history() and diffMetaItem filter organization_id by strict
equality and post-filter nothing, so over an unfiltered stub the org predicate is a no-op.
The control mutates the /history door to not forward the organization — the exact
line ...(historyOrganizationId ? ...) inside GET /meta/:type/:name/history — and reads
one assertion, serves the org-scoped change log of an item the active org authored, in
all four cells.

stuborg at the doorthat assertion
old, unfilteredforwardedPASS
old, unfiltereddroppedPASS — the vacuity, measured directly
new, partitionedforwardedPASS
new, partitioneddroppedFAILexpected +0 to be 2

Verbatim, cell 2 (old stub, org dropped):

 ✓ … #13764 the history seams of this harness honour the org partition >
serves the org-scoped change log of an item the active org authored 4ms

and cell 4 (new stub, org dropped):

 × … serves the org-scoped change log of an item the active org authored 11ms
→ the door answered an empty change log for an item whose org partition holds
two events: expected +0 to be 2

Green in both states before, red in exactly one state after. That is the discriminating
power the card is about.

rest-server.ts is held hard-serial by #13280 and nothing lands in it here. It was
mutated transiently for the measurement only, with the blob hash equal to its HEAD blob
before and after, the removed and injected text counted in both directions, and an empty
git diff HEAD on the restore leg.

The second axis of the control

The two cross-tenant cases were not writable at all against the old stub — they were
red over it with the org forwarded and with it dropped, because an unfiltered read hands
org B org A's rows whatever the door does. They are green over the new stub in both
states. That makes does not serve org A history to org B the assertion that holds the
stub partitioned
: the positive case stays green if anyone un-partitions the stub again,
and only the cross-tenant one reddens.

Was findOne's unconditional null load-bearing? Measured: no

Predicted before running, from the production call sites — the only callers of findOne
on that table are getByHash, restoreVersion and resolveMetaItemOrgScope, none of
which a PUT reaches; the write path reads history through find, in nextEventSeq and
nextItemVersion. The sibling harness landed in #13756 implements this exact seam and
drives the same PUT path green, which is the positive control for the prediction.

Confirmed: with the seam honouring the where, all 15 pre-existing assertions stay
green and the file runs 18/18. So the implement form was safe and the refusal form was not
needed on this seam.

Which form on each axis, and why

  • where on find — implemented, not refused. Refusal was not available: an
    org-scoping assertion has to be able to read the partition, and a seam that throws on
    every predicate is red in both states rather than discriminating.
  • where on findOne — implemented, matching the sibling, once the null was
    measured not load-bearing.
  • limit — applied after the filter and by presence, typeof opts?.limit === 'number',
    on both table branches, which is check:objectql-double-limit's shape rule. Every call
    this fixture makes passes no bound, so every existing assertion keeps its meaning.
  • unknown WHERE combinators — refused loudly rather than read as a column name. This
    is the one place refusal is right: a double that answers $and by looking for a column
    literally called $and returns a well-formed wrong answer, which is the same silent
    class one level down.

The door's own ?limit= contract and the /diff and non-overridable controls are pinned
by rest-server-meta-history-diff-org-scope.test.ts and are deliberately not copied
here — the gate's own probes exercise the shape rules of the double itself, and
duplicating the sibling's door pins would be redundant coverage rather than new
information.

Baseline ratchet

scripts/objectql-double-limit.baseline.json loses its rest-server-meta-read-org-scope
entry. Not a new baseline row — a ratchet down, which the gate requires in the same
PR; it reconciles in both directions and errored with
baselined file is clean or gone -- ratchet DOWN until the entry was deleted.

Verification

Union derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack after
merging origin/main and after the last edit, harvested with --commands. Everything
below ran at aab1254550, tree clean.

union named 33, ran 33, unreconciled 0

by exact comm -23 of the sorted union against the sorted ran-list — no prefix or fuzzy
matching, and no arithmetic on a hand-kept counter. comm -13 is empty too.

One non-zero exit, and it is NOT MEASURED rather than a finding, in the gate's own
words:

check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved `turbo run test`
log, and no log was named. … the local reading for this gate is NOT MEASURED.
⛔ It is not a red, and there is nothing here to fix. (exit 3, distinct from a finding's 1)

The two gates this change is really answerable to, quoting their own verdict lines:

OK ObjectQL double `limit` conformance holds: 296 double(s) graded, 97 apply the
caller's bound or refuse it loudly.
167 limit-blind, 32 shape-breaking and 55 unjudged double(s) in 252 grandfathered
file(s); none new.
baseline key set verified against 16c3601: no files added.
✓ where-matcher conformance holds: 321 matcher(s) discovered, 321 answer the combinator
battery correctly or refuse it loudly (202 refuse).

Also run, all exit 0:

  • pnpm --filter @objectstack/rest test164 files, 2764 tests, all passed
  • pnpm --filter @objectstack/rest typecheckcheck:test-typecheck: OK. The edited
    file was confirmed present in that program by tsc --listFiles (1 hit, with the sibling
    harness as the positive control), so the green really covers it. The 2 remaining tsc
    errors are the pre-existing pinned src/rest.test.ts debt held by The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454; zero come
    from this diff.
  • pnpm lint — the whole repo, eslint . --no-inline-config, exit 0 in 85s. No
    narrowing claimed and none needed.
  • pnpm check:nul-bytesOK (scanned 7598 text file(s) … no raw ASCII control bytes),
    plus a direct control-byte scan over both edited files.

Changeset

Proposed: the skip-changeset label, and this is a reading rather than a preference.
packages/rest's manifest declares files: ["dist", "README.md", "CHANGELOG.md"], and
npm pack --dry-run reports an 8-file tarball that does not contain the edited test
file, with dist/index.js present as the control that the reading is real. The other file
is a CI-internal gate ledger. So the change publishes nothing from any package.

The seat rule says to prefer release noise over a silent gate bypass when the two readings
conflict — here they do not, and the bypass surface was measured rather than assumed:
Check Changeset runs exactly three substantive steps, and this diff adds no changeset at
all (so there is no empty-frontmatter file to reject) and declares no breaking change (so
no ADR-0087 disposition is owed). The one thing that stretches the closed list is that the
baseline ledger sits in scripts/ rather than scripts/pm/; it is the gate's mandatory
same-PR companion to the test edit and cannot be split out.

Generated by Claude Code


Generated by Claude Code

…harness
Measurement checkpoint: over the CURRENT unfiltered stub the positive case is
green (vacuously) and the two cross-tenant controls are red, which is what
proves those controls are not writable against a stub that ignores the where.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
…th history seams
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
… repaired stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 86e765a7704eff4de2eb070330ff66a0e211c3aapackageMentionDocs.

@os-steve
os-steve marked this pull request as ready for review August 31, 2026 15:06
@os-steve
os-steve added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 24ac73dAug 31, 2026
37 checks passed
@os-steve
os-steve deleted the claude/issue-13764-meta-read-org-scope-stub-honours-where branch August 31, 2026 15:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams by claude[bot] · Pull Request #13839 · objectstack-ai/objectstack · GitHub
Skip to content

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams - #13839

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where
Aug 31, 2026
Merged

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams#13839
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13764

The stub engine in packages/rest/src/rest-server-meta-read-org-scope.test.ts discarded
opts.where on bothsys_metadata_history seams — findOne answered null
unconditionally and find handed back every history row unfiltered — and was also
limit-blind, one of the 168 doubles check:objectql-double-limit grandfathers. Both
seams are repaired on one edit, plus the matcher's silent-combinator hole.

There was no live defect and no vacuous claim in the file as it stood: it drove only
PUT /meta/:type/:name, GET /meta/:type/:name and GET /meta/:type, and every
assertion it made its stub genuinely kept. What is repaired is a trap — the file whose
name reads like the obvious home for a /history org-scoping assertion was the one file
where that assertion could not be tested.

The deliverable is the discrimination control, not the green gate

SysMetadataRepository.history() and diffMetaItem filter organization_id by strict
equality and post-filter nothing, so over an unfiltered stub the org predicate is a no-op.
The control mutates the /history door to not forward the organization — the exact
line ...(historyOrganizationId ? ...) inside GET /meta/:type/:name/history — and reads
one assertion, serves the org-scoped change log of an item the active org authored, in
all four cells.

stuborg at the doorthat assertion
old, unfilteredforwardedPASS
old, unfiltereddroppedPASS — the vacuity, measured directly
new, partitionedforwardedPASS
new, partitioneddroppedFAILexpected +0 to be 2

Verbatim, cell 2 (old stub, org dropped):

 ✓ … #13764 the history seams of this harness honour the org partition >
serves the org-scoped change log of an item the active org authored 4ms

and cell 4 (new stub, org dropped):

 × … serves the org-scoped change log of an item the active org authored 11ms
→ the door answered an empty change log for an item whose org partition holds
two events: expected +0 to be 2

Green in both states before, red in exactly one state after. That is the discriminating
power the card is about.

rest-server.ts is held hard-serial by #13280 and nothing lands in it here. It was
mutated transiently for the measurement only, with the blob hash equal to its HEAD blob
before and after, the removed and injected text counted in both directions, and an empty
git diff HEAD on the restore leg.

The second axis of the control

The two cross-tenant cases were not writable at all against the old stub — they were
red over it with the org forwarded and with it dropped, because an unfiltered read hands
org B org A's rows whatever the door does. They are green over the new stub in both
states. That makes does not serve org A history to org B the assertion that holds the
stub partitioned
: the positive case stays green if anyone un-partitions the stub again,
and only the cross-tenant one reddens.

Was findOne's unconditional null load-bearing? Measured: no

Predicted before running, from the production call sites — the only callers of findOne
on that table are getByHash, restoreVersion and resolveMetaItemOrgScope, none of
which a PUT reaches; the write path reads history through find, in nextEventSeq and
nextItemVersion. The sibling harness landed in #13756 implements this exact seam and
drives the same PUT path green, which is the positive control for the prediction.

Confirmed: with the seam honouring the where, all 15 pre-existing assertions stay
green and the file runs 18/18. So the implement form was safe and the refusal form was not
needed on this seam.

Which form on each axis, and why

  • where on find — implemented, not refused. Refusal was not available: an
    org-scoping assertion has to be able to read the partition, and a seam that throws on
    every predicate is red in both states rather than discriminating.
  • where on findOne — implemented, matching the sibling, once the null was
    measured not load-bearing.
  • limit — applied after the filter and by presence, typeof opts?.limit === 'number',
    on both table branches, which is check:objectql-double-limit's shape rule. Every call
    this fixture makes passes no bound, so every existing assertion keeps its meaning.
  • unknown WHERE combinators — refused loudly rather than read as a column name. This
    is the one place refusal is right: a double that answers $and by looking for a column
    literally called $and returns a well-formed wrong answer, which is the same silent
    class one level down.

The door's own ?limit= contract and the /diff and non-overridable controls are pinned
by rest-server-meta-history-diff-org-scope.test.ts and are deliberately not copied
here — the gate's own probes exercise the shape rules of the double itself, and
duplicating the sibling's door pins would be redundant coverage rather than new
information.

Baseline ratchet

scripts/objectql-double-limit.baseline.json loses its rest-server-meta-read-org-scope
entry. Not a new baseline row — a ratchet down, which the gate requires in the same
PR; it reconciles in both directions and errored with
baselined file is clean or gone -- ratchet DOWN until the entry was deleted.

Verification

Union derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack after
merging origin/main and after the last edit, harvested with --commands. Everything
below ran at aab1254550, tree clean.

union named 33, ran 33, unreconciled 0

by exact comm -23 of the sorted union against the sorted ran-list — no prefix or fuzzy
matching, and no arithmetic on a hand-kept counter. comm -13 is empty too.

One non-zero exit, and it is NOT MEASURED rather than a finding, in the gate's own
words:

check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved `turbo run test`
log, and no log was named. … the local reading for this gate is NOT MEASURED.
⛔ It is not a red, and there is nothing here to fix. (exit 3, distinct from a finding's 1)

The two gates this change is really answerable to, quoting their own verdict lines:

OK ObjectQL double `limit` conformance holds: 296 double(s) graded, 97 apply the
caller's bound or refuse it loudly.
167 limit-blind, 32 shape-breaking and 55 unjudged double(s) in 252 grandfathered
file(s); none new.
baseline key set verified against 16c3601: no files added.
✓ where-matcher conformance holds: 321 matcher(s) discovered, 321 answer the combinator
battery correctly or refuse it loudly (202 refuse).

Also run, all exit 0:

  • pnpm --filter @objectstack/rest test164 files, 2764 tests, all passed
  • pnpm --filter @objectstack/rest typecheckcheck:test-typecheck: OK. The edited
    file was confirmed present in that program by tsc --listFiles (1 hit, with the sibling
    harness as the positive control), so the green really covers it. The 2 remaining tsc
    errors are the pre-existing pinned src/rest.test.ts debt held by The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454; zero come
    from this diff.
  • pnpm lint — the whole repo, eslint . --no-inline-config, exit 0 in 85s. No
    narrowing claimed and none needed.
  • pnpm check:nul-bytesOK (scanned 7598 text file(s) … no raw ASCII control bytes),
    plus a direct control-byte scan over both edited files.

Changeset

Proposed: the skip-changeset label, and this is a reading rather than a preference.
packages/rest's manifest declares files: ["dist", "README.md", "CHANGELOG.md"], and
npm pack --dry-run reports an 8-file tarball that does not contain the edited test
file, with dist/index.js present as the control that the reading is real. The other file
is a CI-internal gate ledger. So the change publishes nothing from any package.

The seat rule says to prefer release noise over a silent gate bypass when the two readings
conflict — here they do not, and the bypass surface was measured rather than assumed:
Check Changeset runs exactly three substantive steps, and this diff adds no changeset at
all (so there is no empty-frontmatter file to reject) and declares no breaking change (so
no ADR-0087 disposition is owed). The one thing that stretches the closed list is that the
baseline ledger sits in scripts/ rather than scripts/pm/; it is the gate's mandatory
same-PR companion to the test edit and cannot be split out.

Generated by Claude Code


Generated by Claude Code

…harness
Measurement checkpoint: over the CURRENT unfiltered stub the positive case is
green (vacuously) and the two cross-tenant controls are red, which is what
proves those controls are not writable against a stub that ignores the where.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
…th history seams
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
… repaired stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 86e765a7704eff4de2eb070330ff66a0e211c3aapackageMentionDocs.

@os-steve
os-steve marked this pull request as ready for review August 31, 2026 15:06
@os-steve
os-steve added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 24ac73dAug 31, 2026
37 checks passed
@os-steve
os-steve deleted the claude/issue-13764-meta-read-org-scope-stub-honours-where branch August 31, 2026 15:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams by claude[bot] · Pull Request #13839 · objectstack-ai/objectstack · GitHub
Skip to content

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams - #13839

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where
Aug 31, 2026
Merged

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams#13839
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13764

The stub engine in packages/rest/src/rest-server-meta-read-org-scope.test.ts discarded
opts.where on bothsys_metadata_history seams — findOne answered null
unconditionally and find handed back every history row unfiltered — and was also
limit-blind, one of the 168 doubles check:objectql-double-limit grandfathers. Both
seams are repaired on one edit, plus the matcher's silent-combinator hole.

There was no live defect and no vacuous claim in the file as it stood: it drove only
PUT /meta/:type/:name, GET /meta/:type/:name and GET /meta/:type, and every
assertion it made its stub genuinely kept. What is repaired is a trap — the file whose
name reads like the obvious home for a /history org-scoping assertion was the one file
where that assertion could not be tested.

The deliverable is the discrimination control, not the green gate

SysMetadataRepository.history() and diffMetaItem filter organization_id by strict
equality and post-filter nothing, so over an unfiltered stub the org predicate is a no-op.
The control mutates the /history door to not forward the organization — the exact
line ...(historyOrganizationId ? ...) inside GET /meta/:type/:name/history — and reads
one assertion, serves the org-scoped change log of an item the active org authored, in
all four cells.

stuborg at the doorthat assertion
old, unfilteredforwardedPASS
old, unfiltereddroppedPASS — the vacuity, measured directly
new, partitionedforwardedPASS
new, partitioneddroppedFAILexpected +0 to be 2

Verbatim, cell 2 (old stub, org dropped):

 ✓ … #13764 the history seams of this harness honour the org partition >
serves the org-scoped change log of an item the active org authored 4ms

and cell 4 (new stub, org dropped):

 × … serves the org-scoped change log of an item the active org authored 11ms
→ the door answered an empty change log for an item whose org partition holds
two events: expected +0 to be 2

Green in both states before, red in exactly one state after. That is the discriminating
power the card is about.

rest-server.ts is held hard-serial by #13280 and nothing lands in it here. It was
mutated transiently for the measurement only, with the blob hash equal to its HEAD blob
before and after, the removed and injected text counted in both directions, and an empty
git diff HEAD on the restore leg.

The second axis of the control

The two cross-tenant cases were not writable at all against the old stub — they were
red over it with the org forwarded and with it dropped, because an unfiltered read hands
org B org A's rows whatever the door does. They are green over the new stub in both
states. That makes does not serve org A history to org B the assertion that holds the
stub partitioned
: the positive case stays green if anyone un-partitions the stub again,
and only the cross-tenant one reddens.

Was findOne's unconditional null load-bearing? Measured: no

Predicted before running, from the production call sites — the only callers of findOne
on that table are getByHash, restoreVersion and resolveMetaItemOrgScope, none of
which a PUT reaches; the write path reads history through find, in nextEventSeq and
nextItemVersion. The sibling harness landed in #13756 implements this exact seam and
drives the same PUT path green, which is the positive control for the prediction.

Confirmed: with the seam honouring the where, all 15 pre-existing assertions stay
green and the file runs 18/18. So the implement form was safe and the refusal form was not
needed on this seam.

Which form on each axis, and why

  • where on find — implemented, not refused. Refusal was not available: an
    org-scoping assertion has to be able to read the partition, and a seam that throws on
    every predicate is red in both states rather than discriminating.
  • where on findOne — implemented, matching the sibling, once the null was
    measured not load-bearing.
  • limit — applied after the filter and by presence, typeof opts?.limit === 'number',
    on both table branches, which is check:objectql-double-limit's shape rule. Every call
    this fixture makes passes no bound, so every existing assertion keeps its meaning.
  • unknown WHERE combinators — refused loudly rather than read as a column name. This
    is the one place refusal is right: a double that answers $and by looking for a column
    literally called $and returns a well-formed wrong answer, which is the same silent
    class one level down.

The door's own ?limit= contract and the /diff and non-overridable controls are pinned
by rest-server-meta-history-diff-org-scope.test.ts and are deliberately not copied
here — the gate's own probes exercise the shape rules of the double itself, and
duplicating the sibling's door pins would be redundant coverage rather than new
information.

Baseline ratchet

scripts/objectql-double-limit.baseline.json loses its rest-server-meta-read-org-scope
entry. Not a new baseline row — a ratchet down, which the gate requires in the same
PR; it reconciles in both directions and errored with
baselined file is clean or gone -- ratchet DOWN until the entry was deleted.

Verification

Union derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack after
merging origin/main and after the last edit, harvested with --commands. Everything
below ran at aab1254550, tree clean.

union named 33, ran 33, unreconciled 0

by exact comm -23 of the sorted union against the sorted ran-list — no prefix or fuzzy
matching, and no arithmetic on a hand-kept counter. comm -13 is empty too.

One non-zero exit, and it is NOT MEASURED rather than a finding, in the gate's own
words:

check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved `turbo run test`
log, and no log was named. … the local reading for this gate is NOT MEASURED.
⛔ It is not a red, and there is nothing here to fix. (exit 3, distinct from a finding's 1)

The two gates this change is really answerable to, quoting their own verdict lines:

OK ObjectQL double `limit` conformance holds: 296 double(s) graded, 97 apply the
caller's bound or refuse it loudly.
167 limit-blind, 32 shape-breaking and 55 unjudged double(s) in 252 grandfathered
file(s); none new.
baseline key set verified against 16c3601: no files added.
✓ where-matcher conformance holds: 321 matcher(s) discovered, 321 answer the combinator
battery correctly or refuse it loudly (202 refuse).

Also run, all exit 0:

  • pnpm --filter @objectstack/rest test164 files, 2764 tests, all passed
  • pnpm --filter @objectstack/rest typecheckcheck:test-typecheck: OK. The edited
    file was confirmed present in that program by tsc --listFiles (1 hit, with the sibling
    harness as the positive control), so the green really covers it. The 2 remaining tsc
    errors are the pre-existing pinned src/rest.test.ts debt held by The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454; zero come
    from this diff.
  • pnpm lint — the whole repo, eslint . --no-inline-config, exit 0 in 85s. No
    narrowing claimed and none needed.
  • pnpm check:nul-bytesOK (scanned 7598 text file(s) … no raw ASCII control bytes),
    plus a direct control-byte scan over both edited files.

Changeset

Proposed: the skip-changeset label, and this is a reading rather than a preference.
packages/rest's manifest declares files: ["dist", "README.md", "CHANGELOG.md"], and
npm pack --dry-run reports an 8-file tarball that does not contain the edited test
file, with dist/index.js present as the control that the reading is real. The other file
is a CI-internal gate ledger. So the change publishes nothing from any package.

The seat rule says to prefer release noise over a silent gate bypass when the two readings
conflict — here they do not, and the bypass surface was measured rather than assumed:
Check Changeset runs exactly three substantive steps, and this diff adds no changeset at
all (so there is no empty-frontmatter file to reject) and declares no breaking change (so
no ADR-0087 disposition is owed). The one thing that stretches the closed list is that the
baseline ledger sits in scripts/ rather than scripts/pm/; it is the gate's mandatory
same-PR companion to the test edit and cannot be split out.

Generated by Claude Code


Generated by Claude Code

…harness
Measurement checkpoint: over the CURRENT unfiltered stub the positive case is
green (vacuously) and the two cross-tenant controls are red, which is what
proves those controls are not writable against a stub that ignores the where.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
…th history seams
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
… repaired stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 86e765a7704eff4de2eb070330ff66a0e211c3aapackageMentionDocs.

@os-steve
os-steve marked this pull request as ready for review August 31, 2026 15:06
@os-steve
os-steve added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 24ac73dAug 31, 2026
37 checks passed
@os-steve
os-steve deleted the claude/issue-13764-meta-read-org-scope-stub-honours-where branch August 31, 2026 15:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams by claude[bot] · Pull Request #13839 · objectstack-ai/objectstack · GitHub
Skip to content

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams - #13839

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where
Aug 31, 2026
Merged

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams#13839
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13764

The stub engine in packages/rest/src/rest-server-meta-read-org-scope.test.ts discarded
opts.where on bothsys_metadata_history seams — findOne answered null
unconditionally and find handed back every history row unfiltered — and was also
limit-blind, one of the 168 doubles check:objectql-double-limit grandfathers. Both
seams are repaired on one edit, plus the matcher's silent-combinator hole.

There was no live defect and no vacuous claim in the file as it stood: it drove only
PUT /meta/:type/:name, GET /meta/:type/:name and GET /meta/:type, and every
assertion it made its stub genuinely kept. What is repaired is a trap — the file whose
name reads like the obvious home for a /history org-scoping assertion was the one file
where that assertion could not be tested.

The deliverable is the discrimination control, not the green gate

SysMetadataRepository.history() and diffMetaItem filter organization_id by strict
equality and post-filter nothing, so over an unfiltered stub the org predicate is a no-op.
The control mutates the /history door to not forward the organization — the exact
line ...(historyOrganizationId ? ...) inside GET /meta/:type/:name/history — and reads
one assertion, serves the org-scoped change log of an item the active org authored, in
all four cells.

stuborg at the doorthat assertion
old, unfilteredforwardedPASS
old, unfiltereddroppedPASS — the vacuity, measured directly
new, partitionedforwardedPASS
new, partitioneddroppedFAILexpected +0 to be 2

Verbatim, cell 2 (old stub, org dropped):

 ✓ … #13764 the history seams of this harness honour the org partition >
serves the org-scoped change log of an item the active org authored 4ms

and cell 4 (new stub, org dropped):

 × … serves the org-scoped change log of an item the active org authored 11ms
→ the door answered an empty change log for an item whose org partition holds
two events: expected +0 to be 2

Green in both states before, red in exactly one state after. That is the discriminating
power the card is about.

rest-server.ts is held hard-serial by #13280 and nothing lands in it here. It was
mutated transiently for the measurement only, with the blob hash equal to its HEAD blob
before and after, the removed and injected text counted in both directions, and an empty
git diff HEAD on the restore leg.

The second axis of the control

The two cross-tenant cases were not writable at all against the old stub — they were
red over it with the org forwarded and with it dropped, because an unfiltered read hands
org B org A's rows whatever the door does. They are green over the new stub in both
states. That makes does not serve org A history to org B the assertion that holds the
stub partitioned
: the positive case stays green if anyone un-partitions the stub again,
and only the cross-tenant one reddens.

Was findOne's unconditional null load-bearing? Measured: no

Predicted before running, from the production call sites — the only callers of findOne
on that table are getByHash, restoreVersion and resolveMetaItemOrgScope, none of
which a PUT reaches; the write path reads history through find, in nextEventSeq and
nextItemVersion. The sibling harness landed in #13756 implements this exact seam and
drives the same PUT path green, which is the positive control for the prediction.

Confirmed: with the seam honouring the where, all 15 pre-existing assertions stay
green and the file runs 18/18. So the implement form was safe and the refusal form was not
needed on this seam.

Which form on each axis, and why

  • where on find — implemented, not refused. Refusal was not available: an
    org-scoping assertion has to be able to read the partition, and a seam that throws on
    every predicate is red in both states rather than discriminating.
  • where on findOne — implemented, matching the sibling, once the null was
    measured not load-bearing.
  • limit — applied after the filter and by presence, typeof opts?.limit === 'number',
    on both table branches, which is check:objectql-double-limit's shape rule. Every call
    this fixture makes passes no bound, so every existing assertion keeps its meaning.
  • unknown WHERE combinators — refused loudly rather than read as a column name. This
    is the one place refusal is right: a double that answers $and by looking for a column
    literally called $and returns a well-formed wrong answer, which is the same silent
    class one level down.

The door's own ?limit= contract and the /diff and non-overridable controls are pinned
by rest-server-meta-history-diff-org-scope.test.ts and are deliberately not copied
here — the gate's own probes exercise the shape rules of the double itself, and
duplicating the sibling's door pins would be redundant coverage rather than new
information.

Baseline ratchet

scripts/objectql-double-limit.baseline.json loses its rest-server-meta-read-org-scope
entry. Not a new baseline row — a ratchet down, which the gate requires in the same
PR; it reconciles in both directions and errored with
baselined file is clean or gone -- ratchet DOWN until the entry was deleted.

Verification

Union derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack after
merging origin/main and after the last edit, harvested with --commands. Everything
below ran at aab1254550, tree clean.

union named 33, ran 33, unreconciled 0

by exact comm -23 of the sorted union against the sorted ran-list — no prefix or fuzzy
matching, and no arithmetic on a hand-kept counter. comm -13 is empty too.

One non-zero exit, and it is NOT MEASURED rather than a finding, in the gate's own
words:

check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved `turbo run test`
log, and no log was named. … the local reading for this gate is NOT MEASURED.
⛔ It is not a red, and there is nothing here to fix. (exit 3, distinct from a finding's 1)

The two gates this change is really answerable to, quoting their own verdict lines:

OK ObjectQL double `limit` conformance holds: 296 double(s) graded, 97 apply the
caller's bound or refuse it loudly.
167 limit-blind, 32 shape-breaking and 55 unjudged double(s) in 252 grandfathered
file(s); none new.
baseline key set verified against 16c3601: no files added.
✓ where-matcher conformance holds: 321 matcher(s) discovered, 321 answer the combinator
battery correctly or refuse it loudly (202 refuse).

Also run, all exit 0:

  • pnpm --filter @objectstack/rest test164 files, 2764 tests, all passed
  • pnpm --filter @objectstack/rest typecheckcheck:test-typecheck: OK. The edited
    file was confirmed present in that program by tsc --listFiles (1 hit, with the sibling
    harness as the positive control), so the green really covers it. The 2 remaining tsc
    errors are the pre-existing pinned src/rest.test.ts debt held by The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454; zero come
    from this diff.
  • pnpm lint — the whole repo, eslint . --no-inline-config, exit 0 in 85s. No
    narrowing claimed and none needed.
  • pnpm check:nul-bytesOK (scanned 7598 text file(s) … no raw ASCII control bytes),
    plus a direct control-byte scan over both edited files.

Changeset

Proposed: the skip-changeset label, and this is a reading rather than a preference.
packages/rest's manifest declares files: ["dist", "README.md", "CHANGELOG.md"], and
npm pack --dry-run reports an 8-file tarball that does not contain the edited test
file, with dist/index.js present as the control that the reading is real. The other file
is a CI-internal gate ledger. So the change publishes nothing from any package.

The seat rule says to prefer release noise over a silent gate bypass when the two readings
conflict — here they do not, and the bypass surface was measured rather than assumed:
Check Changeset runs exactly three substantive steps, and this diff adds no changeset at
all (so there is no empty-frontmatter file to reject) and declares no breaking change (so
no ADR-0087 disposition is owed). The one thing that stretches the closed list is that the
baseline ledger sits in scripts/ rather than scripts/pm/; it is the gate's mandatory
same-PR companion to the test edit and cannot be split out.

Generated by Claude Code


Generated by Claude Code

…harness
Measurement checkpoint: over the CURRENT unfiltered stub the positive case is
green (vacuously) and the two cross-tenant controls are red, which is what
proves those controls are not writable against a stub that ignores the where.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
…th history seams
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
… repaired stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 86e765a7704eff4de2eb070330ff66a0e211c3aapackageMentionDocs.

@os-steve
os-steve marked this pull request as ready for review August 31, 2026 15:06
@os-steve
os-steve added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 24ac73dAug 31, 2026
37 checks passed
@os-steve
os-steve deleted the claude/issue-13764-meta-read-org-scope-stub-honours-where branch August 31, 2026 15:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams by claude[bot] · Pull Request #13839 · objectstack-ai/objectstack · GitHub
Skip to content

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams - #13839

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where
Aug 31, 2026
Merged

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams#13839
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13764

The stub engine in packages/rest/src/rest-server-meta-read-org-scope.test.ts discarded
opts.where on bothsys_metadata_history seams — findOne answered null
unconditionally and find handed back every history row unfiltered — and was also
limit-blind, one of the 168 doubles check:objectql-double-limit grandfathers. Both
seams are repaired on one edit, plus the matcher's silent-combinator hole.

There was no live defect and no vacuous claim in the file as it stood: it drove only
PUT /meta/:type/:name, GET /meta/:type/:name and GET /meta/:type, and every
assertion it made its stub genuinely kept. What is repaired is a trap — the file whose
name reads like the obvious home for a /history org-scoping assertion was the one file
where that assertion could not be tested.

The deliverable is the discrimination control, not the green gate

SysMetadataRepository.history() and diffMetaItem filter organization_id by strict
equality and post-filter nothing, so over an unfiltered stub the org predicate is a no-op.
The control mutates the /history door to not forward the organization — the exact
line ...(historyOrganizationId ? ...) inside GET /meta/:type/:name/history — and reads
one assertion, serves the org-scoped change log of an item the active org authored, in
all four cells.

stuborg at the doorthat assertion
old, unfilteredforwardedPASS
old, unfiltereddroppedPASS — the vacuity, measured directly
new, partitionedforwardedPASS
new, partitioneddroppedFAILexpected +0 to be 2

Verbatim, cell 2 (old stub, org dropped):

 ✓ … #13764 the history seams of this harness honour the org partition >
serves the org-scoped change log of an item the active org authored 4ms

and cell 4 (new stub, org dropped):

 × … serves the org-scoped change log of an item the active org authored 11ms
→ the door answered an empty change log for an item whose org partition holds
two events: expected +0 to be 2

Green in both states before, red in exactly one state after. That is the discriminating
power the card is about.

rest-server.ts is held hard-serial by #13280 and nothing lands in it here. It was
mutated transiently for the measurement only, with the blob hash equal to its HEAD blob
before and after, the removed and injected text counted in both directions, and an empty
git diff HEAD on the restore leg.

The second axis of the control

The two cross-tenant cases were not writable at all against the old stub — they were
red over it with the org forwarded and with it dropped, because an unfiltered read hands
org B org A's rows whatever the door does. They are green over the new stub in both
states. That makes does not serve org A history to org B the assertion that holds the
stub partitioned
: the positive case stays green if anyone un-partitions the stub again,
and only the cross-tenant one reddens.

Was findOne's unconditional null load-bearing? Measured: no

Predicted before running, from the production call sites — the only callers of findOne
on that table are getByHash, restoreVersion and resolveMetaItemOrgScope, none of
which a PUT reaches; the write path reads history through find, in nextEventSeq and
nextItemVersion. The sibling harness landed in #13756 implements this exact seam and
drives the same PUT path green, which is the positive control for the prediction.

Confirmed: with the seam honouring the where, all 15 pre-existing assertions stay
green and the file runs 18/18. So the implement form was safe and the refusal form was not
needed on this seam.

Which form on each axis, and why

  • where on find — implemented, not refused. Refusal was not available: an
    org-scoping assertion has to be able to read the partition, and a seam that throws on
    every predicate is red in both states rather than discriminating.
  • where on findOne — implemented, matching the sibling, once the null was
    measured not load-bearing.
  • limit — applied after the filter and by presence, typeof opts?.limit === 'number',
    on both table branches, which is check:objectql-double-limit's shape rule. Every call
    this fixture makes passes no bound, so every existing assertion keeps its meaning.
  • unknown WHERE combinators — refused loudly rather than read as a column name. This
    is the one place refusal is right: a double that answers $and by looking for a column
    literally called $and returns a well-formed wrong answer, which is the same silent
    class one level down.

The door's own ?limit= contract and the /diff and non-overridable controls are pinned
by rest-server-meta-history-diff-org-scope.test.ts and are deliberately not copied
here — the gate's own probes exercise the shape rules of the double itself, and
duplicating the sibling's door pins would be redundant coverage rather than new
information.

Baseline ratchet

scripts/objectql-double-limit.baseline.json loses its rest-server-meta-read-org-scope
entry. Not a new baseline row — a ratchet down, which the gate requires in the same
PR; it reconciles in both directions and errored with
baselined file is clean or gone -- ratchet DOWN until the entry was deleted.

Verification

Union derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack after
merging origin/main and after the last edit, harvested with --commands. Everything
below ran at aab1254550, tree clean.

union named 33, ran 33, unreconciled 0

by exact comm -23 of the sorted union against the sorted ran-list — no prefix or fuzzy
matching, and no arithmetic on a hand-kept counter. comm -13 is empty too.

One non-zero exit, and it is NOT MEASURED rather than a finding, in the gate's own
words:

check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved `turbo run test`
log, and no log was named. … the local reading for this gate is NOT MEASURED.
⛔ It is not a red, and there is nothing here to fix. (exit 3, distinct from a finding's 1)

The two gates this change is really answerable to, quoting their own verdict lines:

OK ObjectQL double `limit` conformance holds: 296 double(s) graded, 97 apply the
caller's bound or refuse it loudly.
167 limit-blind, 32 shape-breaking and 55 unjudged double(s) in 252 grandfathered
file(s); none new.
baseline key set verified against 16c3601: no files added.
✓ where-matcher conformance holds: 321 matcher(s) discovered, 321 answer the combinator
battery correctly or refuse it loudly (202 refuse).

Also run, all exit 0:

  • pnpm --filter @objectstack/rest test164 files, 2764 tests, all passed
  • pnpm --filter @objectstack/rest typecheckcheck:test-typecheck: OK. The edited
    file was confirmed present in that program by tsc --listFiles (1 hit, with the sibling
    harness as the positive control), so the green really covers it. The 2 remaining tsc
    errors are the pre-existing pinned src/rest.test.ts debt held by The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454; zero come
    from this diff.
  • pnpm lint — the whole repo, eslint . --no-inline-config, exit 0 in 85s. No
    narrowing claimed and none needed.
  • pnpm check:nul-bytesOK (scanned 7598 text file(s) … no raw ASCII control bytes),
    plus a direct control-byte scan over both edited files.

Changeset

Proposed: the skip-changeset label, and this is a reading rather than a preference.
packages/rest's manifest declares files: ["dist", "README.md", "CHANGELOG.md"], and
npm pack --dry-run reports an 8-file tarball that does not contain the edited test
file, with dist/index.js present as the control that the reading is real. The other file
is a CI-internal gate ledger. So the change publishes nothing from any package.

The seat rule says to prefer release noise over a silent gate bypass when the two readings
conflict — here they do not, and the bypass surface was measured rather than assumed:
Check Changeset runs exactly three substantive steps, and this diff adds no changeset at
all (so there is no empty-frontmatter file to reject) and declares no breaking change (so
no ADR-0087 disposition is owed). The one thing that stretches the closed list is that the
baseline ledger sits in scripts/ rather than scripts/pm/; it is the gate's mandatory
same-PR companion to the test edit and cannot be split out.

Generated by Claude Code


Generated by Claude Code

…harness
Measurement checkpoint: over the CURRENT unfiltered stub the positive case is
green (vacuously) and the two cross-tenant controls are red, which is what
proves those controls are not writable against a stub that ignores the where.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
…th history seams
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
… repaired stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 86e765a7704eff4de2eb070330ff66a0e211c3aapackageMentionDocs.

@os-steve
os-steve marked this pull request as ready for review August 31, 2026 15:06
@os-steve
os-steve added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 24ac73dAug 31, 2026
37 checks passed
@os-steve
os-steve deleted the claude/issue-13764-meta-read-org-scope-stub-honours-where branch August 31, 2026 15:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-steve@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams by claude[bot] · Pull Request #13839 · objectstack-ai/objectstack · GitHub
Skip to content

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams - #13839

Merged
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where
Aug 31, 2026
Merged

test(rest): the meta read-scope stub honours the where and the limit on both sys_metadata_history seams#13839
os-steve merged 4 commits into
mainfrom
claude/issue-13764-meta-read-org-scope-stub-honours-where

Conversation

@claude

@claudeclaudeBot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes#13764

The stub engine in packages/rest/src/rest-server-meta-read-org-scope.test.ts discarded
opts.where on bothsys_metadata_history seams — findOne answered null
unconditionally and find handed back every history row unfiltered — and was also
limit-blind, one of the 168 doubles check:objectql-double-limit grandfathers. Both
seams are repaired on one edit, plus the matcher's silent-combinator hole.

There was no live defect and no vacuous claim in the file as it stood: it drove only
PUT /meta/:type/:name, GET /meta/:type/:name and GET /meta/:type, and every
assertion it made its stub genuinely kept. What is repaired is a trap — the file whose
name reads like the obvious home for a /history org-scoping assertion was the one file
where that assertion could not be tested.

The deliverable is the discrimination control, not the green gate

SysMetadataRepository.history() and diffMetaItem filter organization_id by strict
equality and post-filter nothing, so over an unfiltered stub the org predicate is a no-op.
The control mutates the /history door to not forward the organization — the exact
line ...(historyOrganizationId ? ...) inside GET /meta/:type/:name/history — and reads
one assertion, serves the org-scoped change log of an item the active org authored, in
all four cells.

stuborg at the doorthat assertion
old, unfilteredforwardedPASS
old, unfiltereddroppedPASS — the vacuity, measured directly
new, partitionedforwardedPASS
new, partitioneddroppedFAILexpected +0 to be 2

Verbatim, cell 2 (old stub, org dropped):

 ✓ … #13764 the history seams of this harness honour the org partition >
serves the org-scoped change log of an item the active org authored 4ms

and cell 4 (new stub, org dropped):

 × … serves the org-scoped change log of an item the active org authored 11ms
→ the door answered an empty change log for an item whose org partition holds
two events: expected +0 to be 2

Green in both states before, red in exactly one state after. That is the discriminating
power the card is about.

rest-server.ts is held hard-serial by #13280 and nothing lands in it here. It was
mutated transiently for the measurement only, with the blob hash equal to its HEAD blob
before and after, the removed and injected text counted in both directions, and an empty
git diff HEAD on the restore leg.

The second axis of the control

The two cross-tenant cases were not writable at all against the old stub — they were
red over it with the org forwarded and with it dropped, because an unfiltered read hands
org B org A's rows whatever the door does. They are green over the new stub in both
states. That makes does not serve org A history to org B the assertion that holds the
stub partitioned
: the positive case stays green if anyone un-partitions the stub again,
and only the cross-tenant one reddens.

Was findOne's unconditional null load-bearing? Measured: no

Predicted before running, from the production call sites — the only callers of findOne
on that table are getByHash, restoreVersion and resolveMetaItemOrgScope, none of
which a PUT reaches; the write path reads history through find, in nextEventSeq and
nextItemVersion. The sibling harness landed in #13756 implements this exact seam and
drives the same PUT path green, which is the positive control for the prediction.

Confirmed: with the seam honouring the where, all 15 pre-existing assertions stay
green and the file runs 18/18. So the implement form was safe and the refusal form was not
needed on this seam.

Which form on each axis, and why

  • where on find — implemented, not refused. Refusal was not available: an
    org-scoping assertion has to be able to read the partition, and a seam that throws on
    every predicate is red in both states rather than discriminating.
  • where on findOne — implemented, matching the sibling, once the null was
    measured not load-bearing.
  • limit — applied after the filter and by presence, typeof opts?.limit === 'number',
    on both table branches, which is check:objectql-double-limit's shape rule. Every call
    this fixture makes passes no bound, so every existing assertion keeps its meaning.
  • unknown WHERE combinators — refused loudly rather than read as a column name. This
    is the one place refusal is right: a double that answers $and by looking for a column
    literally called $and returns a well-formed wrong answer, which is the same silent
    class one level down.

The door's own ?limit= contract and the /diff and non-overridable controls are pinned
by rest-server-meta-history-diff-org-scope.test.ts and are deliberately not copied
here — the gate's own probes exercise the shape rules of the double itself, and
duplicating the sibling's door pins would be redundant coverage rather than new
information.

Baseline ratchet

scripts/objectql-double-limit.baseline.json loses its rest-server-meta-read-org-scope
entry. Not a new baseline row — a ratchet down, which the gate requires in the same
PR; it reconciles in both directions and errored with
baselined file is clean or gone -- ratchet DOWN until the entry was deleted.

Verification

Union derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack after
merging origin/main and after the last edit, harvested with --commands. Everything
below ran at aab1254550, tree clean.

union named 33, ran 33, unreconciled 0

by exact comm -23 of the sorted union against the sorted ran-list — no prefix or fuzzy
matching, and no arithmetic on a hand-kept counter. comm -13 is empty too.

One non-zero exit, and it is NOT MEASURED rather than a finding, in the gate's own
words:

check-test-completeness: PREREQUISITE NOT MET — this gate grades a saved `turbo run test`
log, and no log was named. … the local reading for this gate is NOT MEASURED.
⛔ It is not a red, and there is nothing here to fix. (exit 3, distinct from a finding's 1)

The two gates this change is really answerable to, quoting their own verdict lines:

OK ObjectQL double `limit` conformance holds: 296 double(s) graded, 97 apply the
caller's bound or refuse it loudly.
167 limit-blind, 32 shape-breaking and 55 unjudged double(s) in 252 grandfathered
file(s); none new.
baseline key set verified against 16c3601: no files added.
✓ where-matcher conformance holds: 321 matcher(s) discovered, 321 answer the combinator
battery correctly or refuse it loudly (202 refuse).

Also run, all exit 0:

  • pnpm --filter @objectstack/rest test164 files, 2764 tests, all passed
  • pnpm --filter @objectstack/rest typecheckcheck:test-typecheck: OK. The edited
    file was confirmed present in that program by tsc --listFiles (1 hit, with the sibling
    harness as the positive control), so the green really covers it. The 2 remaining tsc
    errors are the pre-existing pinned src/rest.test.ts debt held by The 2 remaining ledgered TS2345 in packages/rest are hand-built IHttpResponse literals — they were masked by the request literals, not absent #13454; zero come
    from this diff.
  • pnpm lint — the whole repo, eslint . --no-inline-config, exit 0 in 85s. No
    narrowing claimed and none needed.
  • pnpm check:nul-bytesOK (scanned 7598 text file(s) … no raw ASCII control bytes),
    plus a direct control-byte scan over both edited files.

Changeset

Proposed: the skip-changeset label, and this is a reading rather than a preference.
packages/rest's manifest declares files: ["dist", "README.md", "CHANGELOG.md"], and
npm pack --dry-run reports an 8-file tarball that does not contain the edited test
file, with dist/index.js present as the control that the reading is real. The other file
is a CI-internal gate ledger. So the change publishes nothing from any package.

The seat rule says to prefer release noise over a silent gate bypass when the two readings
conflict — here they do not, and the bypass surface was measured rather than assumed:
Check Changeset runs exactly three substantive steps, and this diff adds no changeset at
all (so there is no empty-frontmatter file to reject) and declares no breaking change (so
no ADR-0087 disposition is owed). The one thing that stretches the closed list is that the
baseline ledger sits in scripts/ rather than scripts/pm/; it is the gate's mandatory
same-PR companion to the test edit and cannot be split out.

Generated by Claude Code


Generated by Claude Code

…harness
Measurement checkpoint: over the CURRENT unfiltered stub the positive case is
green (vacuously) and the two cross-tenant controls are red, which is what
proves those controls are not writable against a stub that ignores the where.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
…th history seams
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
… repaired stub
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UngCYXF98BVpYA9hfz6NYk
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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.

Coarse fallback — 0 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 86e765a7704eff4de2eb070330ff66a0e211c3aapackageMentionDocs.

@os-steve
os-steve marked this pull request as ready for review August 31, 2026 15:06
@os-steve
os-steve added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 24ac73dAug 31, 2026
37 checks passed
@os-steve
os-steve deleted the claude/issue-13764-meta-read-org-scope-stub-honours-where branch August 31, 2026 15:29
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-steve@claude