Skip to content

Commit 1f6b8bb

Browse files
yinlianghuiclaude
andauthored
test(mcp): gate CONNECT_AGENT_PAGE on canonical expression envelopes (#12345)
packages/mcp is the third package shipping a raw-literal Page that reaches the kernel through its own manifest bundle, and the only one without the canonical-expression-envelope gate the other two carry. CONNECT_AGENT_PAGE authors zero expression keys today, which is the argument for the gate rather than against it: the hazard is the NEXT predicate added to it, which would ship bare with every authoring-time signal green. Thin gate copying the cloud-connection shape: the shared detector from @objectstack/lint, the @objectstack/lint devDependency, and an anchored vitest source alias appended to this package's existing alias array so the gate judges lint's SOURCE rather than a possibly-stale dist. Population is discovered by export shape over src/ through the shared maskComments, never by a hard-coded name, with a floor so a scan that reads nothing cannot pass as a clean page, and a phantom-comment pin so comment-shaped text cannot delete a page from it. Door 3 has one recorded exemption: mcp:connect-agent is a console-registered widget with no ComponentPropsMap row, the same standing-exemption shape cloud-connection's two widgets were in before #11575 gave them rows. The exemption is asserted exactly, its bag is pinned empty, and a non-vacuity pin reds if the type ever stops appearing on the page. The cross-package maskComments import is declared in scripts/cross-package-test-inputs.mjs and hashed by a matching @objectstack/mcp#test entry in turbo.json. Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9646cdb commit 1f6b8bb

6 files changed

Lines changed: 451 additions & 0 deletions

File tree

packages/mcp/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"zod": "^4.4.3"
2828
},
2929
"devDependencies": {
30+
"@objectstack/lint": "workspace:*",
3031
"@objectstack/metadata-core": "workspace:*",
3132
"@types/node": "^26.2.0",
3233
"typescript": "^6.0.3",

packages/mcp/src/canonical-expression-envelopes.test.ts

Lines changed: 407 additions & 0 deletions
Large diffs are not rendered by default.

packages/mcp/vitest.config.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,21 @@ export default defineConfig({
3333
// any subpath and resolve it to `…/src/index.ts/<subpath>` (ENOTDIR) — a
3434
// config that looks right and fails at run time. Same shape as
3535
// `plugin-sharing`'s.
36+
//
37+
// The second entry is for `canonical-expression-envelopes.test.ts` (#12269)
38+
// — the only suite here that imports `@objectstack/lint` as a VALUE. It
39+
// runs the shared canonical-envelope detector
40+
// (`auditPageExpressionEnvelopes`) over this package's own `Page` export.
41+
// Unaliased, that specifier resolves through `exports` to `lint/dist`, and
42+
// the failure mode is the same silent one the note above describes: a dist
43+
// merely BEHIND lets this gate run GREEN against the detector's old
44+
// behaviour, while the gate's whole purpose is to run the CURRENT detector
45+
// over the CURRENT page. Anchored for a second reason here — `@objectstack/lint`
46+
// exports a `./runtime` subpath, and the object form would swallow it and
47+
// resolve it to `…/lint/src/index.ts/runtime` (ENOTDIR) at run time.
3648
alias: [
3749
{ find: /^@objectstack\/metadata-core$/, replacement: path.resolve(__dirname, '../metadata-core/src/index.ts') },
50+
{ find: /^@objectstack\/lint$/, replacement: path.resolve(__dirname, '../lint/src/index.ts') },
3851
],
3952
},
4053
});

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/cross-package-test-inputs.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,21 @@ export const CROSS_PACKAGE_TEST_INPUTS = {
444444
'packages/cloud-connection/src/cloud-connection-ui.ts',
445445
],
446446
},
447+
'@objectstack/mcp': {
448+
// src/canonical-expression-envelopes.test.ts (#12269) imports `maskComments`
449+
// from `js-comment-mask.mjs` to decide which text in this package's `src/` is
450+
// a comment and which is a `Page` declaration — the third package to carry
451+
// this gate, and declared for the same two reasons the two entries above
452+
// record. The import is a real coupling: that gate's POPULATION is a
453+
// function of the module's masking behaviour, so a change to it has to
454+
// re-run this package's suite. The `.d.mts` sibling is what gives
455+
// `maskComments` its type, so this package's typecheck verdict is a
456+
// function of it too.
457+
globs: [
458+
'scripts/js-comment-mask.mjs',
459+
'scripts/js-comment-mask.d.mts',
460+
],
461+
},
447462
'@objectstack/plugin-auth': {
448463
// src/managed-extension-fields.test.ts walks every `*.object.ts`, and pins
449464
// core's api-key source alongside it.

turbo.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@
156156
"$TURBO_ROOT$/packages/cloud-connection/src/cloud-connection-ui.ts"
157157
]
158158
},
159+
"@objectstack/mcp#test": {
160+
"dependsOn": ["^build"],
161+
"outputs": [],
162+
"inputs": [
163+
"$TURBO_DEFAULT$",
164+
"!dist/**",
165+
"!coverage/**",
166+
"!.turbo/**",
167+
"$TURBO_ROOT$/scripts/js-comment-mask.mjs",
168+
"$TURBO_ROOT$/scripts/js-comment-mask.d.mts"
169+
]
170+
},
159171
"@objectstack/plugin-auth#test": {
160172
"dependsOn": ["^build"],
161173
"outputs": [],

0 commit comments

Comments
 (0)