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

feat(spec): read-scope filter-subtree provenance mark — restore the author-facing cross-field diagnostic without re-disclosing policy (#8220)#8429
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

@hotlong
hotlong marked this pull request as ready for review August 13, 2026 13:53
@hotlong
hotlong added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit 76bcb83 Aug 13, 2026
27 checks passed
@hotlong
hotlong deleted the claude/issue-8220-read-scope-provenance-mark branch August 13, 2026 14:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/xl tests tooling

Projects

None yet

2 participants