Skip to content

test(spec): restore the compile-time half of the ISecurityService contract test (#7831) - #8139

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-7831-security-service-contract-test
Aug 12, 2026
Merged

test(spec): restore the compile-time half of the ISecurityService contract test (#7831)#8139
huangyiirene merged 2 commits into
mainfrom
claude/issue-7831-security-service-contract-test

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes#7831

packages/spec/src/contracts/security-service.test.ts advertised itself as the exhaustive gate on ISecurityService while omitting three required members — hasWriteBypass, resolveWriteScope and describeDelegableScope — from both makeService()'s literal and the assertion list. It did not go red because the file held one entry in test-typecheck-debt.json (TS2322 at line 21), so the gate's compile-time half was asserted past a suppressed error. A gate that is itself type-suppressed does not gate.

What changed (two files, exactly the card's scope)

1. security-service.test.ts — completed, and made exhaustive by the compiler

Each stub returns the method's own documented fail-closed answer, so the file stays a contract-honest minimal implementation rather than a cast. All three match what plugin-security (the reference implementation) returns for the corresponding state:

memberstubwhy that value
hasWriteBypassfalseADR-0111 D2 fails CLOSED. It is the explicitmodifyAllRecords bit only; a stub resolving no permission set holds no such bit — the same false the implementation returns for a principal-less context, an on-behalf-of context, and any resolution failure.
resolveWriteScope'own'ADR-0111 D1 DEPTH fails CLOSED to the narrowest scope. Deliberately not'org': there 'org' means either a genuine Modify-All holder or the fail-OPEN "no permission set mentions this object" default, so it is the one value the contract says a caller may not trust on its own.
describeDelegableScopeisTenantAdmin: false + three empty listsADR-0090 D12 / ADR-0105 D8 fails CLOSED, and "no delegated authority" is a real answer, not a missing one — byte-for-byte the literal security-plugin.ts returns when no delegated-admin gate is wired.

The member list is now exhaustive in both directions, by the compiler, using the idiom the sibling sharing-service.test.ts already carries (its RequiredKeys mapped type) rather than a new framework:

  • as const satisfies readonly RequiredMember[] rejects an optional member listed as required — the cheap way to make a red go away without implementing anything.
  • UnlistedRequiredMember (an Exclude of the list from the required-key union) rejects a required member left out — the drift that produced this card.

2. test-typecheck-debt.json — ratchet 1 to 0

The file's entry is deleted, not lowered. Measured: 56 file(s) / 264 error(s) before, 55 file(s) / 263 error(s) after; the gate reports OK on both sides of the change.

Verification, including the reverse direction

Predicted before running, then observed: injecting a new required member into ISecurityService must red two places, and a new optional member must red none.

PROBE A — required member added to the interface:
security-service.test.ts(66,3): error TS2322: ... is not assignable to type 'ISecurityService'. [the literal]
security-service.test.ts(118,11): error TS2322: Type 'true' is not assignable to type 'never'. [the list]
check:test-typecheck then FAILS:
• src/contracts/security-service.test.ts: 2 type error(s) in a file the ledger does not cover.
PROBE B — optional member added to the interface:
(no errors — an optional member is correctly not demanded)

That is the card's claim proven end to end: a future required member goes red, and no suppression is available, because the ledger no longer covers the file. The interface file was restored by git checkout from the committed branch and confirmed byte-identical (git hash-object equals the committed blob) — it is not part of this diff.

Gates run locally on the merged tree:

  • pnpm --filter @objectstack/spec typecheck — green (tsc, check:scripts-typecheck, check:test-typecheck at 55/263).
  • pnpm --filter @objectstack/spec test385 files / 10195 tests passed (the whole spec suite).
  • node scripts/check-nul-bytes.mjs — OK, 7406 files, no raw control bytes; plus a direct control-byte scan of both changed files.
  • node scripts/check-empty-changeset.mjs --self-test — 118 assertions hold.

main was merged before pushing (3670cf9f2 to 37b82ed5b); the merge touched packages/specnot at all, so the two sides are disjoint and no generated artifact can have moved.

Changeset: none, skip-changeset instead

This PR releases nothing. packages/spec's build config excludes **/*.test.ts and its published files list carries neither the test nor test-typecheck-debt.json, which is a repo-internal CI ratchet — so there is no package version to bump and nothing to attach a CHANGELOG entry to. The changeset gate has no path-based exemption (it demands a changeset on every PR unless the label is present), and scripts/check-empty-changeset.mjs is explicit that the label, not an empty changeset, is the route for exactly this case: an empty changeset is a real input to changesets/action and an all-empty set stalls a release silently and greenly. Hence the skip-changeset label.


Generated by Claude Code

…tract test (#7831)
`security-service.test.ts` advertised itself as the exhaustive gate on
`ISecurityService` while omitting three REQUIRED members — `hasWriteBypass`,
`resolveWriteScope` and `describeDelegableScope` — from both `makeService()`'s
literal and the assertion list. The mismatch did not go red because the file
held one entry in `test-typecheck-debt.json` (TS2322 at line 21), so the gate
was itself suppressed: any future required member could be added with no test
noticing.
- Complete the stub with each method's own documented FAIL-CLOSED answer
(`false`, `'own'`, and the empty `DelegableScope` plugin-security returns
when no delegated-admin gate is wired) rather than a cast.
- Make the member list exhaustive BY THE COMPILER in both directions:
`satisfies readonly RequiredMember[]` rejects an optional member listed as
required, and `UnlistedRequiredMember` rejects a required member left out.
- Delete the file's `test-typecheck-debt.json` entry — ratchet 1 -> 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123k4cam2jEAkPmbJeoaY3r
@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 12, 2026 5:38pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 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/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/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/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/spec)
  • 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/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/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/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @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/spec)
  • content/docs/permissions/system-context.mdx(via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @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/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @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/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/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @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/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/spec)
  • content/docs/releases/v9.mdx(via @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.

@huangyiirenehuangyiirene added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed tests labels Aug 12, 2026 — with Claude
@huangyiirene
huangyiirene marked this pull request as ready for review August 12, 2026 18:06
@huangyiirene
huangyiirene added this pull request to the merge queueAug 12, 2026
Merged via the queue into main with commit 7cfe7adAug 12, 2026
33 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-7831-security-service-contract-test branch August 12, 2026 18:22
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 gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

security-service.test.ts's "exhaustive" ISecurityService method list omits three required members — and the gap is held open by a suppressed TS2322

2 participants

@huangyiirene@claude