Skip to content

feat(spec): read-scope filter-subtree provenance mark — restore the author-facing cross-field diagnostic without re-disclosing policy (#8220) - #8429

Merged
hotlong merged 3 commits into
mainfrom
claude/issue-8220-read-scope-provenance-mark
Aug 13, 2026
Merged

hotlong merged 3 commits into
mainfrom
claude/issue-8220-read-scope-provenance-mark

Conversation

@hotlong

@hotlong hotlong commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #8220

Implements A of the #7929 maintainer ruling ("B now, A next" — B landed as #8198): a spec-declared provenance mark on filter subtrees, set at both read-scope merge boundaries, so the driver can give a vouched author back the full cross-field diagnostic while policy-injected predicates keep #8198's redaction.

(Opened by the dispatching PM on the dev's behalf — the dev's cloud session has no GitHub API egress; the branch and this description are the dev's work, relayed. Full report on #8220.)

What landed

  • DECLARED (packages/spec/src/data/filter-subtree-provenance.ts): FilterSubtreeProvenance ('author' | 'policy'), markFilterSubtreeProvenance (non-enumerable Symbol.for key; first-mark-wins; silent no-op on frozen input), filterSubtreeProvenanceOf, and resolveFilterSubtreeProvenance(root, node) — positional, innermost-mark-wins, identity-located; unreachable/conflicting ⇒ null. The mark crosses the driver boundary ON the where tree itself; DriverQuery grows no slot, and its TSDoc declares the crossing and the fail direction.
  • SET at both boundaries: plugin-security's RLS injection marks every injected scope (RLS / CBP / delegator / deny-sentinel) 'policy', and the caller's predicate 'author' only under the identity vouch opCtx.ast.where === opCtx.options.where — a sibling-rewritten tree is vouched for nobody; service-analytics' ObjectQLStrategy.withReadScope marks scope 'policy' and the strategy-built user filter 'author' (resolveFkAttr's scope arm 'policy', its internal idFilter deliberately unmarked).
  • CONSUMED: driver-sql resolves withheld refusals against the query's own where root at its two existing seams (the applyFilters catch for eager refusals; withWithheldFilterLog, now threaded with the root, for knex's lazy group callbacks) — 'author' gets a fresh INVALID_FILTER/400 carrying the full diagnostic (both columns, the operator, the list index, the boundary reason); 'policy' / unmarked / unreachable / ambiguous keep fix(driver-sql,driver-turso): a cross-field $field refusal stops naming the two columns it compared (#7929, #7988) #8198's redaction + server-log relocation. driver-turso's RemoteTransport applies the same resolution at buildWhereSQL entry under the same Symbol.for keys; driver-sqlite-wasm inherits (pinned via the runtime suite).

Fail-closed invariant

Unmarked or ambiguous ⇒ WITHHELD. The mark is permission to reveal, never a requirement to prove secrecy. Pinned at every layer, including a byte-equality unmarked==policy pin and a JSON-round-trip-drops-the-author-mark pin.

Two B-era pins rewritten deliberately (not regressions — accounted in the changeset)

  1. packages/runtime/src/cross-field-refusal-operand-withhold.test.ts — the byte-equality pin ("author and policy messages identical") was the strongest statement of the fact this card was chartered to change; its successor is a three-way split: policy withholds; the vouched author names columns on BOTH real faces (CRUD and POST /analytics/query); unmarked withholds byte-identical to policy.
  2. service-analytics cross-field-engine-fallback.test.ts — pinned B's blanket redaction on the caller's OWN where; those callers are now the vouched author, so the routed cases assert the corpus diagnosticIncludes fragments back on the wire, with the policy-scope case gaining explicit non-disclosure assertions as the fail-closed pair.

Why this is not #8116's mechanism (prior art read first, per the card's scope note)

injected-system-column-provenance (#8116/PR #8342) is COLUMN provenance — a derivation over metadata documents at rest ("is storage behind an injected column"). This is FILTER-SUBTREE provenance — an attestation about one query value in flight, knowable only at the merge moment. Folding them would give the column verdict a mutable runtime carrier it must not have, and the filter mark a document derivation it cannot have. Justified in both module headers.

Scope fences

Verification

Full suites green: spec 10346 · plugin-security 1036 · driver-sql 1431 · objectql 3496 · driver-sqlite-wasm 393 · driver-turso 973 · service-analytics 1675 · runtime 2309. Typecheck 127/127. spec check:generated 13/13 (api-surface + export-origins regenerated for the new exports). Every family from dispatch-gates.mjs green; ESLint clean on changed files. New pin files: spec (15), driver-sql (11, incl. the lazy group-callback disclosure seam), driver-turso (7), plugin-security (4), service-analytics (5); rewritten: runtime (11), service-analytics engine-fallback (93).

Changeset: minor for @objectstack/spec + patch ×4 consumers; non-breaking; adr-0087 gate green with no disposition needed.


🤖 Generated with Claude Code

https://claude.ai/code/session_01Euoy6wyfzgiWtgCg4s6JK2


Generated by Claude Code

claude added 2 commits August 13, 2026 11:26
…t it at both merge boundaries; consume it in the SQL drivers (#8220)

The mark (data/filter-subtree-provenance.ts) is set by plugin-security's CRUD
RLS injection and service-analytics' ObjectQLStrategy.withReadScope, travels
on the where tree by reference across the DriverQuery boundary, and is
consumed by driver-sql / driver-turso's RemoteTransport: a subtree positively
marked 'author' gets its full cross-field diagnostic back; 'policy', unmarked
and ambiguous all keep the #7929/#8198 redaction (fail closed).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AkiE189Csm7aBT2qCVSx3X
…te both B-era redaction pins deliberately

- spec: filter-subtree-provenance unit tests (fail-closed on every degraded shape)
- driver-sql / driver-turso: author discloses (eager + lazy seams), policy/
  unmarked/ambiguous withhold byte-identically
- plugin-security / service-analytics: both boundaries' stamping pinned,
  including the sibling-rewrite fail-closed vouch
- runtime: the byte-equality pin rewritten into its successor (author differs
  and names columns; unmarked still withholds, byte-identical to policy)
- service-analytics engine-fallback: B's blanket-redaction pin on the caller's
  own where rewritten into the author-restored assertions, with the policy-
  scope non-disclosure pinned as its fail-closed pair
- spec api-surface/export-origins regenerated; changeset added

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AkiE189Csm7aBT2qCVSx3X
@vercel

vercel Bot commented Aug 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 13, 2026 1:31pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/driver-sql, @objectstack/driver-turso, @objectstack/plugin-security, @objectstack/service-analytics, @objectstack/spec.

114 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/data-api.mdx (via @objectstack/service-analytics)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/service-analytics, @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-sql, @objectstack/driver-turso, @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/driver-turso, @objectstack/plugin-security, @objectstack/spec)
  • content/docs/deployment/environment-variables.mdx (via @objectstack/driver-turso)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/driver-turso)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-sql, @objectstack/driver-turso)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/driver-sql, @objectstack/plugin-security, @objectstack/service-analytics, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/access-recipes.mdx (via packages/plugins/plugin-security)
  • content/docs/permissions/authorization.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/permissions/explain.mdx (via @objectstack/plugin-security)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-security, @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/plugin-security, @objectstack/service-analytics, @objectstack/spec)
  • content/docs/permissions/system-context.mdx (via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-sql, @objectstack/driver-turso, @objectstack/plugin-security, @objectstack/service-analytics, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-sql, @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/audience-based-interfaces.mdx (via packages/plugins/plugin-security)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/plugin-security, @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

⛔ 7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-sql, @objectstack/plugin-security, @objectstack/service-analytics, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/service-analytics, @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/service-analytics, @objectstack/spec)

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

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

…ce test's strategy import — clears the +1 type-check-debt drift (#8220)

The new `src/__tests__/read-scope-provenance-mark.test.ts` imported
`../strategies/objectql-strategy` without the ESM file extension. The repo
resolves as NodeNext, so tsc raised TS2835 there:

  Relative import paths need explicit file extensions in ECMAScript imports
  when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean
  '../strategies/objectql-strategy.js'?

service-analytics is DEBT-ledgered and has no `typecheck` script, so the
workspace typecheck never read the file; only CI's full-closure
`check:type-check-debt --re-measure` saw the count move 10 -> 11.

Vitest resolved the extensionless specifier either way, which is why the
suite was green while the gate was red. Every other `../strategies/*` import
in this package's tests already carries `.js`.

Raw `tsc --noEmit -p packages/services/service-analytics/tsconfig.json` is
back to 10, and the surviving 10 match the ledger note exactly (TS6133 x1 +
TS2339 x7 + TS7053 x2). The DEBT entry is untouched and the pre-existing 10
are left alone.

hotlong commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Audit (spec-seat PM, session session_01Euoy6wyfzgiWtgCg4s6JK2): CI's type-check-debt red (+1 in @objectstack/service-analytics, 10→11) was fixed by a local patch surrogate (04455dc44) — the original cloud dev has no GitHub egress and cross-session messaging to it is unavailable by design.

Attribution correction for the record: the +1 was not in the provenance source edit — objectql-strategy.ts is type-clean. It was a missing .js extension on a relative import in the PR's new test file (read-scope-provenance-mark.test.ts, TS2835 under node16 resolution); vitest resolves the extensionless specifier, which is why the suite was green while the gate was red, and service-analytics is DEBT-ledgered with no typecheck script, so only CI's full-closure re-measure could see it. Fix is one line; measured 11→10 with the gate's own invocation, then the full --re-measure end-to-end: "OK — 33 ledger entries, none above its recorded number." Suite stays 1675 green. Ledger untouched.


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 size/xl tests tooling

Projects

None yet

2 participants