Skip to content

test(runtime): pin standalone-stack's app-default resolution against the real helper, not a copy of it - #7252

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7092-standalone-mirror-conformance
Aug 10, 2026
Merged

test(runtime): pin standalone-stack's app-default resolution against the real helper, not a copy of it#7252
os-help merged 1 commit into
mainfrom
claude/issue-7092-standalone-mirror-conformance

Conversation

@os-help

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

Copy link
Copy Markdown
Collaborator

Fixes #7092

packages/runtime/src/standalone-stack.test.ts hand-copied the body of
appDefaultPermissionSetName and asserted against the copy in a case titled
"the surfaced config drives appDefaultPermissionSetName → the app profile
(the exact CLI wiring)". A conformance claim over a duplicate of the rule:
green through any change to the real helper, while objectstack dev/serve
did something else.

The mirror's stated reason had already expired

The comment justified the copy as:

Mirrors appDefaultPermissionSetName from @objectstack/plugin-security (not a
runtime dependency, so the resolution rule is reproduced here)

Measured on the merge base, that is false:

  • packages/runtime/package.json carries "@objectstack/plugin-security": "workspace:*"
    in dependencies — not devDependencies, a plain runtime edge.
  • Another test in the same package already imports from it:
    packages/runtime/src/domains/share-links-enforcement-context.test.ts:55
    imports PermissionDeniedError, SecurityPlugin.
  • No cycle: @objectstack/plugin-security depends only on core, formula,
    platform-objects, spec.
  • No layering/dependency gate exists in the check farm to forbid the edge.

So the card's option 1 needed no package.json change at all — the edge it
proposed adding is already there. Zero dependency edges added by this PR.

What this PR does

Deletes the mirror and imports the real helpers. The anchor is
appSecurityPluginOptions rather than the bare name helper, because that is
what serve.ts calls today (#7001):
new SecurityPlugin(appSecurityPluginOptions(config)) — which closes the
"one level below what serve.ts actually calls" staleness the card notes.

The fixture now discriminates: the non-default app_contributor set sits
first in the artifact's permissions[], so the resolution cases disagree
with "take permissions[0]" instead of accidentally agreeing with it. And the
surfaced-order assertion is no longer .sort()ed — declaration order is the
precondition the first-isDefault rule depends on, and surfacing it faithfully
is this package's half of that rule. That invariant was previously unpinned.

Why the assertion stays in packages/runtime (not moved to packages/cli)

The card recommended option 2. Measured, the CLI side of the claim is already
pinned and moving would duplicate cost without adding a claim:

  • packages/cli/src/commands/serve-verify-security-parity.contract.test.ts
    already pins that serve.ts constructs SecurityPlugin exactly once via
    appSecurityPluginOptions(config), and that it agrees with bootStack.
  • packages/plugins/plugin-security/src/app-default-permission-set.test.ts
    already pins the rule's substance against the real helper: first-isDefault
    wins, the undefined vs { fallbackPermissionSet: undefined } distinction,
    top-level-only reads, and that the option lands on the plugin's baseline
    service.

What neither can see — and what this file uniquely owns — is the
composition: that the config createStandaloneStack / createDefaultHostConfig
actually return is a config that resolution reads correctly. Moving it to
packages/cli would mean either re-running the 60s standalone boot there, or
hand-building a config — i.e. replacing a mirror of the rule with a mirror of
the surfacing. Cross-references to all three homes are now in the file's header.

Reverse verification (predicted direction: red — and it is)

Ablated the real helper in plugin-security (dropped the
ps.isDefault === true discrimination), rebuilt its dist, and ran the case:

× the surfaced config feeds the REAL appSecurityPluginOptions → the app profile
AssertionError: expected { Object (fallbackPermissionSet) } to deeply equal { Object (fallbackPermissionSet) }
 Tests  1 failed | 17 skipped (18)

Then, with the same ablation still in place, restored the pre-fix test file
from origin/main and ran its exact CLI wiring case:

 Test Files  1 passed (1)
      Tests  1 passed | 17 skipped (18)

That is the defect, measured rather than argued: the old case is green over
a real helper whose resolution has been deleted; the new case is red. Both
files and the helper were restored afterwards (via git checkout, never
git stash), and the suite re-run green.

Verification

  • pnpm --filter '@objectstack/runtime^...' build — build closure first, green.
  • @objectstack/runtime full suite: 118 test files, 1837 tests passed.
  • pnpm --filter @objectstack/runtime typecheck — clean.
  • pnpm check:type-check-debt (--self-test && --re-measure, after
    turbo run build): OK, 33 entries re-measured, "none above its recorded
    number". @objectstack/runtime measures 225 against a recorded ceiling of
    227 — unchanged by this PR: reproducing the gate's own sibling-tsconfig
    measurement on the branch and on origin/main's version of the file gave
    225 both ways, with 0 errors attributable to
    src/standalone-stack.test.ts in either. The ledger is not raised.
  • node scripts/check-nul-bytes.mjs — OK; plus a wider self-scan
    (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]') over the touched file, no hits.
  • npx eslint packages/runtime/src/standalone-stack.test.ts — clean.

Scope

One file changed: packages/runtime/src/standalone-stack.test.ts. No
package.json edit, no production code, no changeset — tests-only, nothing
user-visible ships. Needs skip-changeset.


Generated by Claude Code

…the REAL helper

`standalone-stack.test.ts` hand-copied `appDefaultPermissionSetName` and titled
the copy "the exact CLI wiring" — a conformance claim over a duplicate of the
rule, green through any change to the real helper.

The mirror's stated reason had already expired: `@objectstack/plugin-security`
is a plain `dependencies` entry of `@objectstack/runtime`, and another test in
the same package already imports from it. Import the real
`appSecurityPluginOptions` / `appDefaultPermissionSetName` instead, anchored on
`appSecurityPluginOptions` because that is what `serve.ts` calls today (#7001).

Also make the fixture discriminate: the non-default set now sits FIRST in the
artifact, and the surfaced order is asserted unsorted — the precondition the
first-`isDefault` rule depends on, and this package's half of it.

Fixes #7092

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158ZQo7LiHSxGWpYKuPq1wu
@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 3:58am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants