Skip to content

refactor(plugin-approvals,plugin-reports): annotate enforcement onto the full ExecutionContext (#7135) - #7206

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7135-execution-context-annotations
Aug 10, 2026
Merged

refactor(plugin-approvals,plugin-reports): annotate enforcement onto the full ExecutionContext (#7135)#7206
os-help merged 1 commit into
mainfrom
claude/issue-7135-execution-context-annotations

Conversation

@os-help

@os-help os-help commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7135

The services half of #7070, mirroring PR #7140 (the identity half, merged 2026-08-09). #6523 / PR #7068 converged 36 contract signatures onto the full resolveAuthzContext envelope, applying the #6206 ruling — enforcement adjudicates on the whole envelope, never a per-site subset. The implementations behind those contracts still annotated their own parameters with the six-field shape the contracts used to name, so nothing they could read had widened.

Premise re-verified on origin/main @ aeadbd6

The card's counts were measured @ 445a0c2 and the dispatching seat re-measured @ 6146b67. Both still hold at my branch point:

file narrow-type refs before after
packages/plugins/plugin-approvals/src/approval-service.ts 27 0
packages/plugins/plugin-reports/src/report-service.ts 14 0
packages/plugins/plugin-approvals/src/approval-node.ts 2 0

(Two doc-comment mentions of the old name survive, in prose explaining what each cast replaced.) After this PR the only remaining references repo-wide outside packages/spec are plugin-sharing/src/index.ts's re-export and a comment in plugin-sharing's pin.

What changed

Scope held

The pins, and why they exist

A re-narrowed annotation would compile, ship and pass every test in these packages — the values were always complete at runtime, so there is no compiler pressure in either direction and nothing would notice. Each package gets an exec-context-annotation.pin.ts handing each parameter a fresh literal naming envelope-only fields (posture, accessible_org_ids, org_user_ids), which excess-property checking rejects the moment a parameter narrows back, plus @ts-expect-error negatives so a parameter erased to any cannot pass either.

Pin placement was measured per package rather than assumed, and the two differ:

Neither pin is imported by anything, so tsup (entry src/index.ts) never bundles them into dist.

Reverse verification — the predicted direction, and it held

Predicted before running: a full revert leaves the runtime suite green and turns only the pin red. That is what happened. The fix was taken out with git checkout origin/main -- ... (never git stash), pins left in place:

REVERT: plugin-reports   Test Files  2 passed (2) / Tests  54 passed (54)
REVERT: plugin-approvals Test Files 19 passed (19) / Tests 446 passed (446)
REVERT_TEST_EXIT=0

500 of 500 tests pass with the change fully reverted — confirming #7140's measurement that a green suite is not evidence for this family. Typecheck on the same reverted tree, exit 2, every error inside a pin file and none outside one:

plugin-reports   src/exec-context-annotation.pin.ts(70,50): error TS2353: Object literal may only specify known properties, and 'posture' does not exist in type 'SharingExecutionContext'.
plugin-reports   src/exec-context-annotation.pin.ts(72,38): error TS2353: ... 'org_user_ids' does not exist in type 'SharingExecutionContext'.
plugin-reports   src/exec-context-annotation.pin.ts(79,39): error TS2339: Property 'posture' does not exist on type 'SharingExecutionContext'.
plugin-approvals src/exec-context-annotation.pin.ts(66,39): error TS2353: ... 'posture' does not exist in type 'SharingExecutionContext'.
plugin-approvals src/exec-context-annotation.pin.ts(68,40): error TS2353: ... 'accessible_org_ids' does not exist in type 'SharingExecutionContext'.

Each package was also reverse-verified alone, because a parallel --workspace-concurrency=2 run can mask the second package behind the first failure. Both bite independently (6 errors in approvals, 7 in reports).

Verification

Dependency closure built first (--filter '...^...'), then:

  • pnpm --filter @objectstack/plugin-approvals --filter @objectstack/plugin-reports typecheck — green (exit 0). This is what proves the pins' positives AND their @ts-expect-error negatives are all satisfied; an unsatisfied expect-error fails the build.
  • pnpm --filter ... test19 passed (19) files / 446 passed (446) tests in approvals, 2 passed (2) / 54 passed (54) in reports.
  • npx eslint packages/plugins/plugin-approvals/src packages/plugins/plugin-reports/src — clean.
  • node scripts/check-org-identifier.mjsOK (1726 author-facing source file(s), no removed session.tenantId alias).
  • node scripts/check-nul-bytes.mjsOK (scanned 6607 text file(s) ...), plus a control-byte self-scan over every touched file.

Changeset: patch on both packages, type-side only, citing the #7140 precedent.

Out of scope, filed not fixed

#7204executeReport() in report-service.ts rebuilds a five-field subset of the envelope before the engine read that produces the report, dropping accessible_org_ids, which packages/objectql/src/engine.ts:2181 reads by name. Under the group posture that makes a report fall back to active-org equality while the same query returns the membership union interactively. The direction is fail-toward-isolation, so it under-reports rather than leaks — a correctness bug, not a security one. Same shape as #7141 (the plugin-audit twin), different consequence; it is a runtime change and belongs to its own card.


Generated by Claude Code

…the full `ExecutionContext` (#7135)

Services half of #7070. #6523 converged 36 contract signatures onto the
complete `resolveAuthzContext` envelope (the #6206 ruling: enforcement
adjudicates on the whole envelope, never a per-site subset); the
implementations still named the retired six-field shape, so nothing they could
read had widened.

`ApprovalService`, the approval flow-node provider and `ReportService` now
declare `ExecutionContext` on all 43 positions, and the casts the narrow
annotation forced are gone — including `(context as any).posture` in
`isOverrideActor()`, an erasure sitting on an enforcement input.

`organizationId` is not a field of the envelope at all and stays cast (#5858 /
`check:org-identifier`), per the #7070 boundary. A compile-time
`exec-context-annotation.pin.ts` per package catches a re-narrowing, which
would otherwise compile, ship and pass every test.

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

vercel Bot commented Aug 10, 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 10, 2026 2:38am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/plugin-approvals, @objectstack/plugin-reports.

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

  • content/docs/api/client-sdk.mdx (via @objectstack/plugin-reports)
  • content/docs/automation/approvals.mdx (via @objectstack/plugin-approvals)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-approvals)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-approvals, @objectstack/plugin-reports)

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

  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-approvals)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-approvals)

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling labels Aug 10, 2026
@os-help
os-help marked this pull request as ready for review August 10, 2026 03:03
@os-help
os-help added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit f40c5b4 Aug 10, 2026
26 checks passed
@os-help
os-help deleted the claude/issue-7135-execution-context-annotations branch August 10, 2026 03:19
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/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Re-annotate plugin-approvals + plugin-reports onto the full ExecutionContext and delete the now-redundant casts (services half of #7070)

2 participants