@canonical/pragma-cli 0.35.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bundled-skills/add-standard/SKILL.md +338 -0
- package/bundled-skills/adoption-a1-styles/SKILL.md +269 -0
- package/bundled-skills/adoption-a2-components/SKILL.md +291 -0
- package/bundled-skills/adoption-a3-forms/SKILL.md +260 -0
- package/bundled-skills/anatomy-author/ANATOMY_DSL_SPEC.md +406 -0
- package/bundled-skills/anatomy-author/SKILL.md +1226 -0
- package/bundled-skills/design-auditor/SKILL.md +455 -0
- package/bundled-skills/implement-component/SKILL.md +333 -0
- package/bundled-skills/specify-component/SKILL.md +331 -0
- package/bundled-skills/specify-pattern/SKILL.md +292 -0
- package/dist/package.json +24 -20
- package/dist/pragma.conf.js +266 -34
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/bin.js +1 -1
- package/dist/src/bin.js.map +1 -1
- package/dist/src/capabilities/capabilities/hints.js +1 -1
- package/dist/src/capabilities/capabilities/hints.js.map +1 -1
- package/dist/src/capabilities/colophon/colophon.render.js +1 -1
- package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
- package/dist/src/capabilities/create/create.verb.js +2 -1
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/create/createSurface.generated.js +23 -1
- package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
- package/dist/src/capabilities/create/mount.js +4 -3
- package/dist/src/capabilities/create/mount.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkNodeVersion.js +11 -6
- package/dist/src/capabilities/doctor/checks/checkNodeVersion.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +60 -4
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkShellCompletions.js +8 -8
- package/dist/src/capabilities/doctor/checks/checkShellCompletions.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/harnessInventory.js +195 -0
- package/dist/src/capabilities/doctor/checks/harnessInventory.js.map +1 -0
- package/dist/src/capabilities/doctor/checks/index.js +6 -6
- package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js +38 -0
- package/dist/src/capabilities/doctor/checks/isSupportedNodeVersion.js.map +1 -0
- package/dist/src/capabilities/doctor/checks/targetHealth.js +194 -50
- package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.render.js +43 -64
- package/dist/src/capabilities/doctor/doctor.render.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.verb.js +6 -2
- package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
- package/dist/src/capabilities/doctor/runChecks.js +14 -11
- package/dist/src/capabilities/doctor/runChecks.js.map +1 -1
- package/dist/src/capabilities/graph/query.render.js +2 -2
- package/dist/src/capabilities/graph/query.render.js.map +1 -1
- package/dist/src/capabilities/info/collectInfo.js +8 -2
- package/dist/src/capabilities/info/collectInfo.js.map +1 -1
- package/dist/src/capabilities/info/info.render.js +6 -2
- package/dist/src/capabilities/info/info.render.js.map +1 -1
- package/dist/src/capabilities/prompt/prompt.render.js +2 -2
- package/dist/src/capabilities/prompt/prompt.render.js.map +1 -1
- package/dist/src/capabilities/setup/buildPlan.js +43 -39
- package/dist/src/capabilities/setup/buildPlan.js.map +1 -1
- package/dist/src/capabilities/setup/mode.js +2 -2
- package/dist/src/capabilities/setup/operations/index.js +4 -4
- package/dist/src/capabilities/setup/operations/index.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupCompletions.js +6 -2
- package/dist/src/capabilities/setup/operations/setupCompletions.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupConfig.js +4 -2
- package/dist/src/capabilities/setup/operations/setupConfig.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupGenerator.js +156 -58
- package/dist/src/capabilities/setup/operations/setupGenerator.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupLsp.js +136 -38
- package/dist/src/capabilities/setup/operations/setupLsp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupMcp.js +101 -52
- package/dist/src/capabilities/setup/operations/setupMcp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupSkills.js +329 -49
- package/dist/src/capabilities/setup/operations/setupSkills.js.map +1 -1
- package/dist/src/capabilities/setup/plan.js.map +1 -1
- package/dist/src/capabilities/setup/plan.render.js +144 -66
- package/dist/src/capabilities/setup/plan.render.js.map +1 -1
- package/dist/src/capabilities/setup/setup.render.js +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +165 -63
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/setup/targets.js +122 -51
- package/dist/src/capabilities/setup/targets.js.map +1 -1
- package/dist/src/capabilities/shared/index.js +7 -6
- package/dist/src/capabilities/shared/index.js.map +1 -1
- package/dist/src/capabilities/shared/packageManager.js +300 -54
- package/dist/src/capabilities/shared/packageManager.js.map +1 -1
- package/dist/src/capabilities/skill/discover.js +97 -11
- package/dist/src/capabilities/skill/discover.js.map +1 -1
- package/dist/src/capabilities/skill/render.js +2 -2
- package/dist/src/capabilities/skill/render.js.map +1 -1
- package/dist/src/capabilities/sources/installSkills.js +145 -3
- package/dist/src/capabilities/sources/installSkills.js.map +1 -1
- package/dist/src/capabilities/sources/runUpdate.js +68 -2
- package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
- package/dist/src/capabilities/sources/update.verb.js +3 -3
- package/dist/src/capabilities/sources/update.verb.js.map +1 -1
- package/dist/src/capabilities/upgrade/runUpgrade.js +43 -7
- package/dist/src/capabilities/upgrade/runUpgrade.js.map +1 -1
- package/dist/src/capabilities/upgrade/upgrade.render.js +10 -2
- package/dist/src/capabilities/upgrade/upgrade.render.js.map +1 -1
- package/dist/src/constants.js +13 -2
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/index.js +11 -2
- package/dist/src/kernel/index.js.map +1 -1
- package/dist/src/kernel/interactivity.js +68 -0
- package/dist/src/kernel/interactivity.js.map +1 -0
- package/dist/src/kernel/packs/renderPack.js +3 -3
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +63 -12
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +57 -4
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +54 -2
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/applyFilters.js +104 -5
- package/dist/src/kernel/packs/sparql/applyFilters.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +310 -43
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +46 -24
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/globalFlags.js +4 -1
- package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
- package/dist/src/kernel/project/cli/helpFormat.js +1 -2
- package/dist/src/kernel/project/cli/helpFormat.js.map +1 -1
- package/dist/src/kernel/project/cli/rootHelp.js +42 -9
- package/dist/src/kernel/project/cli/rootHelp.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +16 -1
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +1 -1
- package/dist/src/kernel/render/style.js +29 -9
- package/dist/src/kernel/render/style.js.map +1 -1
- package/dist/src/kernel/render/vocabulary.js +174 -0
- package/dist/src/kernel/render/vocabulary.js.map +1 -0
- package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js +2 -3
- package/dist/src/kernel/runtime/graphpack/embedded/pack.generated.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.index.generated.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js +4 -0
- package/dist/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.js.map +1 -0
- package/dist/src/kernel/runtime/graphpack/embedded.js +13 -6
- package/dist/src/kernel/runtime/graphpack/embedded.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/read.js +14 -9
- package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
- package/dist/src/kernel/runtime/loadSession.js +3 -1
- package/dist/src/kernel/runtime/loadSession.js.map +1 -1
- package/dist/src/kernel/runtime/probe.js +1 -1
- package/dist/src/kernel/runtime/probe.js.map +1 -1
- package/dist/src/kernel/runtime/refs/parseRef.js +17 -1
- package/dist/src/kernel/runtime/refs/parseRef.js.map +1 -1
- package/dist/src/kernel/runtime/refs/resolve.js +9 -3
- package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
- package/dist/src/kernel/spec/emitReference.js +1 -1
- package/dist/src/kernel/spec/emitReference.js.map +1 -1
- package/package.json +24 -20
- package/pragma.conf.ts +272 -34
- package/src/bin.ts +8 -1
- package/src/capabilities/block.parity.test.ts +25 -11
- package/src/capabilities/block.shipped.exec.test.ts +282 -0
- package/src/capabilities/block.tierRank.exec.test.ts +300 -0
- package/src/capabilities/capabilities/hints.ts +1 -1
- package/src/capabilities/colophon/colophon.render.ts +1 -2
- package/src/capabilities/colophon/colophon.test.ts +2 -2
- package/src/capabilities/config/collectConfigShow.test.ts +1 -0
- package/src/capabilities/create/byteEquality.test.ts +28 -1
- package/src/capabilities/create/create.test.ts +1 -0
- package/src/capabilities/create/create.verb.ts +2 -1
- package/src/capabilities/create/createGrammar.test.ts +5 -2
- package/src/capabilities/create/createSurface.generated.ts +23 -1
- package/src/capabilities/create/crossCli.subprocess.test.ts +10 -3
- package/src/capabilities/create/mount.ts +3 -3
- package/src/capabilities/distribution.parity.test.ts +4 -0
- package/src/capabilities/doctor/checks/checkNodeVersion.test.ts +49 -0
- package/src/capabilities/doctor/checks/checkNodeVersion.ts +11 -7
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +69 -4
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +6 -4
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +8 -8
- package/src/capabilities/doctor/checks/harnessInventory.test.ts +219 -0
- package/src/capabilities/doctor/checks/harnessInventory.ts +298 -0
- package/src/capabilities/doctor/checks/index.ts +6 -6
- package/src/capabilities/doctor/checks/isSupportedNodeVersion.test.ts +111 -0
- package/src/capabilities/doctor/checks/isSupportedNodeVersion.ts +34 -0
- package/src/capabilities/doctor/checks/mcpCommand.test.ts +7 -1
- package/src/capabilities/doctor/checks/targetHealth.ts +229 -59
- package/src/capabilities/doctor/doctor.render.test.ts +108 -32
- package/src/capabilities/doctor/doctor.render.ts +56 -80
- package/src/capabilities/doctor/doctor.test.ts +247 -35
- package/src/capabilities/doctor/doctor.verb.ts +7 -2
- package/src/capabilities/doctor/runChecks.ts +14 -11
- package/src/capabilities/doctor/types.ts +15 -24
- package/src/capabilities/emptyRecovery.test.ts +10 -2
- package/src/capabilities/graph/query.render.ts +2 -2
- package/src/capabilities/info/collectInfo.ts +8 -1
- package/src/capabilities/info/info.render.ts +6 -2
- package/src/capabilities/info/info.test.ts +37 -1
- package/src/capabilities/info/types.ts +10 -2
- package/src/capabilities/lazy.test.ts +26 -2
- package/src/capabilities/listLookup.shipped.exec.test.ts +340 -0
- package/src/capabilities/prompt/prompt.render.ts +2 -2
- package/src/capabilities/renderVocabularySync.test.ts +82 -0
- package/src/capabilities/resources/resources.test.ts +38 -2
- package/src/capabilities/scopeTypeSync.test.ts +29 -15
- package/src/capabilities/setup/buildPlan.ts +45 -41
- package/src/capabilities/setup/index.ts +1 -1
- package/src/capabilities/setup/mode.ts +2 -2
- package/src/capabilities/setup/operations/index.ts +6 -1
- package/src/capabilities/setup/operations/setupCompletions.ts +9 -2
- package/src/capabilities/setup/operations/setupConfig.ts +7 -2
- package/src/capabilities/setup/operations/setupGenerator.ts +227 -84
- package/src/capabilities/setup/operations/setupLsp.ts +203 -38
- package/src/capabilities/setup/operations/setupMcp.ts +139 -62
- package/src/capabilities/setup/operations/setupSkills.ts +399 -60
- package/src/capabilities/setup/plan.render.ts +171 -76
- package/src/capabilities/setup/plan.ts +17 -12
- package/src/capabilities/setup/setup.mcp.correctness.test.ts +9 -9
- package/src/capabilities/setup/setup.render.test.ts +50 -36
- package/src/capabilities/setup/setup.render.ts +1 -1
- package/src/capabilities/setup/setup.test.ts +1126 -50
- package/src/capabilities/setup/setup.verb.ts +174 -68
- package/src/capabilities/setup/targets.ts +138 -60
- package/src/capabilities/setup/types.ts +10 -18
- package/src/capabilities/shared/index.ts +18 -6
- package/src/capabilities/shared/packageManager.test.ts +421 -6
- package/src/capabilities/shared/packageManager.ts +394 -59
- package/src/capabilities/skill/bundledSkills.test.ts +347 -0
- package/src/capabilities/skill/discover.ts +100 -11
- package/src/capabilities/skill/render.ts +2 -2
- package/src/capabilities/skill/skill.test.ts +17 -4
- package/src/capabilities/sources/installSkills.ts +181 -3
- package/src/capabilities/sources/runUpdate.ts +78 -2
- package/src/capabilities/sources/sources.test.ts +228 -0
- package/src/capabilities/sources/update.verb.ts +3 -3
- package/src/capabilities/surface.test.ts +14 -6
- package/src/capabilities/upgrade/runUpgrade.ts +45 -6
- package/src/capabilities/upgrade/types.ts +10 -2
- package/src/capabilities/upgrade/upgrade.render.ts +9 -2
- package/src/capabilities/upgrade/upgrade.test.ts +72 -0
- package/src/constants.ts +14 -1
- package/src/identity.test.ts +18 -5
- package/src/kernel/completion/entitySource.test.ts +15 -2
- package/src/kernel/completion/safety.test.ts +16 -9
- package/src/kernel/config/defaults.test.ts +19 -3
- package/src/kernel/config/readConfig.test.ts +1 -1
- package/src/kernel/index.ts +11 -2
- package/src/kernel/interactivity.test.ts +76 -0
- package/src/kernel/interactivity.ts +69 -0
- package/src/kernel/packs/compile.test.ts +92 -0
- package/src/kernel/packs/renderPack.ts +3 -3
- package/src/kernel/packs/resolveEntity.test.ts +75 -14
- package/src/kernel/packs/resolveEntity.ts +88 -24
- package/src/kernel/packs/runBodies.ts +84 -19
- package/src/kernel/packs/schema.ts +60 -4
- package/src/kernel/packs/sparql/applyFilters.ts +125 -4
- package/src/kernel/packs/sparql/buildLookupQuery.test.ts +290 -0
- package/src/kernel/packs/sparql/buildLookupQuery.ts +327 -45
- package/src/kernel/packs/toolDescription.test.ts +35 -1
- package/src/kernel/packs/types.ts +125 -2
- package/src/kernel/project/cli/buildProgram.test.ts +73 -2
- package/src/kernel/project/cli/buildProgram.ts +8 -1
- package/src/kernel/project/cli/dispatch.test.ts +5 -4
- package/src/kernel/project/cli/dispatch.ts +48 -25
- package/src/kernel/project/cli/globalFlags.ts +4 -1
- package/src/kernel/project/cli/helpFormat.ts +1 -2
- package/src/kernel/project/cli/rootHelp.ts +44 -8
- package/src/kernel/project/mcp/registerVerb.ts +20 -1
- package/src/kernel/render/prefixes.test.ts +2 -0
- package/src/kernel/render/renderers.ts +1 -1
- package/src/kernel/render/style.ts +32 -9
- package/src/kernel/render/vocabulary.ts +276 -0
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +2 -3
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +1 -1
- package/src/kernel/runtime/graphpack/embedded/pack.manifest.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +14 -6
- package/src/kernel/runtime/graphpack/graphpack.test.ts +31 -5
- package/src/kernel/runtime/graphpack/read.ts +14 -12
- package/src/kernel/runtime/loadSession.ts +3 -1
- package/src/kernel/runtime/probe.ts +1 -1
- package/src/kernel/runtime/refs/parseRef.test.ts +49 -1
- package/src/kernel/runtime/refs/parseRef.ts +25 -1
- package/src/kernel/runtime/refs/resolve.ts +9 -3
- package/src/kernel/runtime/types.ts +17 -0
- package/src/kernel/spec/emitReference.ts +1 -1
- package/src/kernel/spec/types.ts +35 -6
- package/src/testing/PARITY_GAPS.ts +9 -3
- package/src/testing/behavioral/docExamples.test.ts +4 -2
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +110 -7
- package/src/testing/behavioral/journeys.firstInstall.test.ts +7 -2
- package/src/testing/behavioral/journeys.packageStories.test.ts +6 -3
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +26 -1
- package/src/testing/eval/cases/readNouns.ts +245 -9
- package/src/testing/fixtures/blockGraph.ts +28 -13
- package/src/testing/fixtures/graph/canonical.ts +95 -12
- package/src/testing/helpers/runCli.test.ts +44 -0
- package/src/testing/helpers/runCli.ts +27 -3
- package/dist/src/capabilities/shared/bands.js +0 -16
- package/dist/src/capabilities/shared/bands.js.map +0 -1
- package/src/capabilities/shared/bands.ts +0 -16
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The list→lookup grammar, EXECUTED against the shipped pack, for every noun
|
|
3
|
+
* that has both halves (PROTECTED).
|
|
4
|
+
*
|
|
5
|
+
* The tool descriptions instruct an agent to take a row's `name` VERBATIM from
|
|
6
|
+
* `<noun>_list` to `<noun>_lookup`. That promise broke for `standard` when the
|
|
7
|
+
* shipped data and the story keyed the two halves on different properties:
|
|
8
|
+
* `standard list` published names synthesized from the IRI while
|
|
9
|
+
* `standard lookup` answered only the ~13% of standards carrying an asserted
|
|
10
|
+
* `cs:name` — a closed loop where every published address missed (#1047). The
|
|
11
|
+
* fixture suites could not see it, because a fixture obligingly asserts
|
|
12
|
+
* whatever the story reads; only the corpus the release actually ships can
|
|
13
|
+
* falsify the promise the release actually makes.
|
|
14
|
+
*
|
|
15
|
+
* So this file boots the SHIPPED graph and asserts the round trip over the
|
|
16
|
+
* WHOLE corpus, not a sample, for every noun with a list+lookup pair — the
|
|
17
|
+
* defect class is not standard-specific, since every pair shares this
|
|
18
|
+
* machinery. Three guarantees per noun:
|
|
19
|
+
*
|
|
20
|
+
* - every name `list` publishes resolves through `lookup` (executed through
|
|
21
|
+
* the real verb bodies, so glob/IRI argument-shape dispatch is exercised
|
|
22
|
+
* with the very strings an agent would paste);
|
|
23
|
+
* - every published name is in the addressable population (`listEntityNames`
|
|
24
|
+
* — the ONE pool that feeds miss-suggestions, glob expansion AND `sample`'s
|
|
25
|
+
* draw), so a suggestion can never offer a name the resolve cannot answer;
|
|
26
|
+
* - for a story that derives names (`nameFallback: "iri"`), that pool covers
|
|
27
|
+
* the WHOLE class population — `sample` is presented as "see real data
|
|
28
|
+
* shapes", and drawing only from the subset carrying an asserted name would
|
|
29
|
+
* be the #1047 defect wearing sample's clothes.
|
|
30
|
+
*
|
|
31
|
+
* The final describe is the negative control: the same assertions run against
|
|
32
|
+
* a fixture compiled in the exact pre-#1047 shape (a list that synthesizes
|
|
33
|
+
* names over a lookup that requires the asserted property) and are shown to
|
|
34
|
+
* FAIL. A guard whose failing case no longer fails is worse than no guard.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
38
|
+
import { compileStoryModule } from "../kernel/packs/compile.js";
|
|
39
|
+
import { listEntityNames } from "../kernel/packs/resolveEntity.js";
|
|
40
|
+
import {
|
|
41
|
+
distributionSource,
|
|
42
|
+
type PackDefinition,
|
|
43
|
+
} from "../kernel/packs/types.js";
|
|
44
|
+
import { verbKey } from "../kernel/packs/uniqueness.js";
|
|
45
|
+
import { bootRuntime } from "../kernel/runtime/boot.js";
|
|
46
|
+
import type { PragmaRuntime } from "../kernel/runtime/types.js";
|
|
47
|
+
import type { CapabilityModule, VerbSpec } from "../kernel/spec/types.js";
|
|
48
|
+
import { buildFixtureRuntime } from "../testing/helpers/packRuntime.js";
|
|
49
|
+
import { TEST_FLAGS } from "../testing/helpers/projectCli.js";
|
|
50
|
+
import { declaredStories, storyModules } from "./distribution.js";
|
|
51
|
+
|
|
52
|
+
/** The provenance label the distribution's own compiled stories carry. */
|
|
53
|
+
const SOURCE = distributionSource("pragma.conf.ts");
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Every noun declaring BOTH halves of the grammar, derived from the config
|
|
57
|
+
* rather than listed — a story added tomorrow is covered the moment it is
|
|
58
|
+
* declared, by the author who declares it and without their help.
|
|
59
|
+
*/
|
|
60
|
+
const NOUNS: readonly string[] = [...declaredStories]
|
|
61
|
+
.filter(([, story]) => story.list !== undefined && story.lookup !== undefined)
|
|
62
|
+
.map(([noun]) => noun);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Nouns whose shipped corpus is empty TODAY (`ds:Token` has no instances in
|
|
66
|
+
* the current packs) — a data-content gap this suite does not own.
|
|
67
|
+
*
|
|
68
|
+
* The allowlist EXPIRES BY CONSTRUCTION: an entry here is asserted to have
|
|
69
|
+
* ZERO rows, so the moment upstream ships instances the entry goes red and
|
|
70
|
+
* must be deleted. A list that merely skipped the non-empty assertion would
|
|
71
|
+
* be a permanent blind spot — `token` gains data, nobody removes the entry,
|
|
72
|
+
* a later regression back to zero rows stays vacuously green — which is
|
|
73
|
+
* exactly the hidden-empty case this guard exists to prevent, sitting inside
|
|
74
|
+
* the guard itself.
|
|
75
|
+
*/
|
|
76
|
+
const EMPTY_CORPUS_TODAY: readonly string[] = ["token"];
|
|
77
|
+
|
|
78
|
+
/** The verb `<noun> <verb>` from a compiled module, or throw naming the gap. */
|
|
79
|
+
function verbOf(
|
|
80
|
+
module: CapabilityModule,
|
|
81
|
+
noun: string,
|
|
82
|
+
verb: string,
|
|
83
|
+
): VerbSpec {
|
|
84
|
+
const found = module.verbs.find((v) => verbKey(v.path) === `${noun} ${verb}`);
|
|
85
|
+
if (!found) throw new Error(`no compiled "${noun} ${verb}" verb`);
|
|
86
|
+
return found;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Run `<noun> list` and return the names it publishes, verbatim, deduped. */
|
|
90
|
+
async function publishedNames(
|
|
91
|
+
rt: PragmaRuntime,
|
|
92
|
+
module: CapabilityModule,
|
|
93
|
+
noun: string,
|
|
94
|
+
): Promise<string[]> {
|
|
95
|
+
const rows = (await verbOf(module, noun, "list").run({}, rt)) as Record<
|
|
96
|
+
string,
|
|
97
|
+
string
|
|
98
|
+
>[];
|
|
99
|
+
return [
|
|
100
|
+
...new Set(rows.map((row) => row.name ?? "").filter((name) => name !== "")),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Take every published name VERBATIM through the real lookup verb, exactly as
|
|
106
|
+
* the tool description instructs an agent to, and return the per-name errors.
|
|
107
|
+
* One batched call: the resolver treats each name independently, and a total
|
|
108
|
+
* miss throws — surfaced as one synthetic error so the assertion that reads
|
|
109
|
+
* this stays a set comparison rather than a try/catch.
|
|
110
|
+
*/
|
|
111
|
+
async function lookupErrors(
|
|
112
|
+
rt: PragmaRuntime,
|
|
113
|
+
module: CapabilityModule,
|
|
114
|
+
noun: string,
|
|
115
|
+
names: readonly string[],
|
|
116
|
+
): Promise<{ query: string; code: string }[]> {
|
|
117
|
+
if (names.length === 0) return [];
|
|
118
|
+
try {
|
|
119
|
+
const output = (await verbOf(module, noun, "lookup").run(
|
|
120
|
+
{ name: [...names] },
|
|
121
|
+
rt,
|
|
122
|
+
)) as { errors: { query: string; code: string }[] };
|
|
123
|
+
return output.errors;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
query: `(all ${names.length})`,
|
|
128
|
+
code: error instanceof Error ? error.message : String(error),
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ONE store for the file — booting the shipped pack is the expensive part, and
|
|
135
|
+
// this package already contends under parallel runs.
|
|
136
|
+
let rt: PragmaRuntime;
|
|
137
|
+
beforeAll(async () => {
|
|
138
|
+
rt = bootRuntime(TEST_FLAGS);
|
|
139
|
+
await rt.store.get();
|
|
140
|
+
}, 60_000);
|
|
141
|
+
afterAll(async () => {
|
|
142
|
+
(await rt.store.get()).store.dispose();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
/** One scalar from the shipped graph, queried directly. */
|
|
146
|
+
async function scalar(query: string): Promise<string | undefined> {
|
|
147
|
+
const result = await rt.query.sparql(query);
|
|
148
|
+
if (result.type !== "select") return undefined;
|
|
149
|
+
const row = result.bindings[0];
|
|
150
|
+
return row === undefined ? undefined : Object.values(row)[0];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
describe("every list-published name resolves through lookup, whole corpus (PROTECTED)", () => {
|
|
154
|
+
it("derives at least one list+lookup noun from the config", () => {
|
|
155
|
+
expect(NOUNS.length).toBeGreaterThan(0);
|
|
156
|
+
// The two nouns this guarantee has already been broken for / leaned on.
|
|
157
|
+
expect(NOUNS).toContain("standard");
|
|
158
|
+
expect(NOUNS).toContain("block");
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it.each(
|
|
162
|
+
NOUNS.map((noun) => [noun] as const),
|
|
163
|
+
)("%s: every published name round-trips, and its corpus is not silently empty", async (noun) => {
|
|
164
|
+
const module = storyModules.get(noun) as CapabilityModule;
|
|
165
|
+
const names = await publishedNames(rt, module, noun);
|
|
166
|
+
if (EMPTY_CORPUS_TODAY.includes(noun)) {
|
|
167
|
+
// Asserted empty, not skipped: this is what expires the allowlist.
|
|
168
|
+
expect(
|
|
169
|
+
names,
|
|
170
|
+
`${noun} is allowlisted as an empty corpus but published rows — ` +
|
|
171
|
+
"delete it from EMPTY_CORPUS_TODAY so a later regression to zero rows cannot hide behind the entry",
|
|
172
|
+
).toEqual([]);
|
|
173
|
+
} else {
|
|
174
|
+
expect(
|
|
175
|
+
names.length,
|
|
176
|
+
`${noun} list published no rows at all from the shipped pack`,
|
|
177
|
+
).toBeGreaterThan(0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const errors = await lookupErrors(rt, module, noun, names);
|
|
181
|
+
expect(
|
|
182
|
+
errors,
|
|
183
|
+
`${noun} list published ${errors.length} name(s) its own lookup cannot resolve — ` +
|
|
184
|
+
"the two-step grammar the tool descriptions document is broken for every agent following it",
|
|
185
|
+
).toEqual([]);
|
|
186
|
+
}, 120_000);
|
|
187
|
+
|
|
188
|
+
it.each(
|
|
189
|
+
NOUNS.map((noun) => [noun] as const),
|
|
190
|
+
)("%s: every published name is in the pool that feeds suggestions, globs and sample", async (noun) => {
|
|
191
|
+
const story = declaredStories.get(noun) as PackDefinition;
|
|
192
|
+
const lookup = story.lookup;
|
|
193
|
+
if (!lookup) throw new Error(`no "${noun}" lookup`);
|
|
194
|
+
const module = storyModules.get(noun) as CapabilityModule;
|
|
195
|
+
|
|
196
|
+
const names = await publishedNames(rt, module, noun);
|
|
197
|
+
const pool = await listEntityNames(rt, lookup, SOURCE);
|
|
198
|
+
// The resolve matches case-insensitively; hold the pool to the same bar.
|
|
199
|
+
const addressable = new Set(pool.map((name) => name.toLowerCase()));
|
|
200
|
+
const missing = names.filter(
|
|
201
|
+
(name) => !addressable.has(name.toLowerCase()),
|
|
202
|
+
);
|
|
203
|
+
expect(
|
|
204
|
+
missing,
|
|
205
|
+
`${noun}: published but not addressable — a miss-suggestion or glob over this pool can hand out none of these, and sample can never draw them`,
|
|
206
|
+
).toEqual([]);
|
|
207
|
+
}, 60_000);
|
|
208
|
+
|
|
209
|
+
it.each(
|
|
210
|
+
NOUNS.filter(
|
|
211
|
+
(noun) => declaredStories.get(noun)?.lookup?.nameFallback === "iri",
|
|
212
|
+
).map((noun) => [noun] as const),
|
|
213
|
+
)("%s: the derived-name pool covers the WHOLE class population (sample is representative)", async (noun) => {
|
|
214
|
+
const story = declaredStories.get(noun) as PackDefinition;
|
|
215
|
+
const lookup = story.lookup;
|
|
216
|
+
if (!lookup?.type && !lookup?.types?.length) {
|
|
217
|
+
throw new Error(`"${noun}" derives names without a class constraint`);
|
|
218
|
+
}
|
|
219
|
+
const types = (lookup.types ?? [lookup.type as string]).join(" ");
|
|
220
|
+
// The pool is DISTINCT NAMES, and a name may be legitimately shared (two
|
|
221
|
+
// shipped standards carry the same upstream rdfs:label; the ranked
|
|
222
|
+
// resolve answers a shared name with every entity it reaches). So the
|
|
223
|
+
// full-population claim is counted the way the pool is built: one name
|
|
224
|
+
// per asserted `by` value, plus one derived name per instance carrying
|
|
225
|
+
// none. A pool smaller than that is the #1047 defect — sample and
|
|
226
|
+
// suggestions drawing from the labelled subset only. (A prose label
|
|
227
|
+
// colliding with a slash-derived name would make this one too strict;
|
|
228
|
+
// that collision would itself be a data defect worth the red bar.)
|
|
229
|
+
const labelled = Number(
|
|
230
|
+
await scalar(
|
|
231
|
+
[
|
|
232
|
+
"SELECT (COUNT(DISTINCT ?name) AS ?n) WHERE {",
|
|
233
|
+
` VALUES ?class { ${types} }`,
|
|
234
|
+
` ?uri a ?class ; ${lookup.by} ?name .`,
|
|
235
|
+
"}",
|
|
236
|
+
].join("\n"),
|
|
237
|
+
),
|
|
238
|
+
);
|
|
239
|
+
const bare = Number(
|
|
240
|
+
await scalar(
|
|
241
|
+
[
|
|
242
|
+
"SELECT (COUNT(DISTINCT ?uri) AS ?n) WHERE {",
|
|
243
|
+
` VALUES ?class { ${types} }`,
|
|
244
|
+
" ?uri a ?class .",
|
|
245
|
+
` FILTER NOT EXISTS { ?uri ${lookup.by} ?any . }`,
|
|
246
|
+
"}",
|
|
247
|
+
].join("\n"),
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
const pool = await listEntityNames(rt, lookup, SOURCE);
|
|
251
|
+
|
|
252
|
+
expect(labelled + bare).toBeGreaterThan(0);
|
|
253
|
+
expect(
|
|
254
|
+
pool.length,
|
|
255
|
+
`${noun}: ${labelled} asserted name(s) + ${bare} instance(s) with none, but only ` +
|
|
256
|
+
`${pool.length} addressable names — sample's draw pool is an unrepresentative slice of the corpus`,
|
|
257
|
+
).toBe(labelled + bare);
|
|
258
|
+
}, 60_000);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe("negative control: the pre-#1047 shape fails these assertions", () => {
|
|
262
|
+
// The exact divergence that shipped: a list that COALESCEs an OPTIONAL
|
|
263
|
+
// display name with an IRI-derived one, over a lookup that requires the
|
|
264
|
+
// asserted property (no nameFallback). One fixture standard carries the
|
|
265
|
+
// property; the other is published under a name the lookup cannot reach.
|
|
266
|
+
const PREFIXES = {
|
|
267
|
+
rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
|
|
268
|
+
cs: "http://pragma.canonical.com/codestandards#",
|
|
269
|
+
};
|
|
270
|
+
const TTL = `
|
|
271
|
+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
|
272
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
273
|
+
@prefix cs: <http://pragma.canonical.com/codestandards#> .
|
|
274
|
+
|
|
275
|
+
cs:name a owl:DatatypeProperty .
|
|
276
|
+
cs:description a owl:DatatypeProperty .
|
|
277
|
+
|
|
278
|
+
cs:react.component.tsdoc a cs:CodeStandard ;
|
|
279
|
+
cs:description "Published under a derived name; carries no cs:name." .
|
|
280
|
+
cs:code.function.purity a cs:CodeStandard ;
|
|
281
|
+
cs:name "Function purity" ;
|
|
282
|
+
cs:description "Carries an asserted cs:name." .
|
|
283
|
+
`;
|
|
284
|
+
const BROKEN_STORY: PackDefinition = {
|
|
285
|
+
noun: "standard",
|
|
286
|
+
description: "pre-#1047 shape",
|
|
287
|
+
list: {
|
|
288
|
+
query: [
|
|
289
|
+
"SELECT ?uri ?name ?description WHERE {",
|
|
290
|
+
" ?uri a cs:CodeStandard ;",
|
|
291
|
+
" cs:description ?description .",
|
|
292
|
+
" OPTIONAL { ?uri cs:name ?n . }",
|
|
293
|
+
' BIND(COALESCE(?n, REPLACE(STRAFTER(STR(?uri), "#"), "\\\\.", "/")) AS ?name)',
|
|
294
|
+
"}",
|
|
295
|
+
"ORDER BY ?name",
|
|
296
|
+
].join("\n"),
|
|
297
|
+
columns: [
|
|
298
|
+
{ field: "name", label: "Name" },
|
|
299
|
+
{ field: "description", label: "Description" },
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
lookup: {
|
|
303
|
+
source: "sparql",
|
|
304
|
+
by: "cs:name", // required — no nameFallback: the pre-#1047 population
|
|
305
|
+
type: "cs:CodeStandard",
|
|
306
|
+
fields: [
|
|
307
|
+
{
|
|
308
|
+
name: "description",
|
|
309
|
+
property: "cs:description",
|
|
310
|
+
label: "Description",
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
it("publishes a name its own lookup cannot resolve, and these assertions catch it", async () => {
|
|
317
|
+
const { rt: fixtureRt } = await buildFixtureRuntime({
|
|
318
|
+
ttl: TTL,
|
|
319
|
+
prefixes: PREFIXES,
|
|
320
|
+
});
|
|
321
|
+
const module = compileStoryModule(BROKEN_STORY, SOURCE, PREFIXES);
|
|
322
|
+
|
|
323
|
+
const names = await publishedNames(fixtureRt, module, "standard");
|
|
324
|
+
expect(names).toContain("react/component/tsdoc");
|
|
325
|
+
expect(names).toContain("Function purity");
|
|
326
|
+
|
|
327
|
+
// The round-trip assertion goes RED on the synthesized name…
|
|
328
|
+
const errors = await lookupErrors(fixtureRt, module, "standard", names);
|
|
329
|
+
expect(errors.map((error) => error.query)).toEqual([
|
|
330
|
+
"react/component/tsdoc",
|
|
331
|
+
]);
|
|
332
|
+
|
|
333
|
+
// …and so does the pool-inclusion assertion: the addressable population
|
|
334
|
+
// is only the labelled subset.
|
|
335
|
+
const lookup = BROKEN_STORY.lookup;
|
|
336
|
+
if (!lookup) throw new Error("fixture story has no lookup");
|
|
337
|
+
const pool = await listEntityNames(fixtureRt, lookup, SOURCE);
|
|
338
|
+
expect(pool).toEqual(["Function purity"]);
|
|
339
|
+
}, 60_000);
|
|
340
|
+
});
|
|
@@ -19,9 +19,9 @@ function argTokens(args: readonly PromptArgument[]): string {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const promptListFormatters: Formatters<PromptListData> = {
|
|
22
|
-
// Zero prompts: plain stdout stays empty — the notice is `
|
|
22
|
+
// Zero prompts: plain stdout stays empty — the notice is `notice`,
|
|
23
23
|
// routed to stderr (exit 0) by the dispatcher so a pipe reads no prose.
|
|
24
|
-
|
|
24
|
+
notice: (data) =>
|
|
25
25
|
data.prompts.length === 0 ? "No prompts in the store." : undefined,
|
|
26
26
|
plain(data) {
|
|
27
27
|
if (data.prompts.length === 0) return "";
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render-vocabulary sync (PROTECTED) — the two halves of the "one vocabulary"
|
|
3
|
+
* guarantee that cannot be a shared module.
|
|
4
|
+
*
|
|
5
|
+
* 1. CROSS-BINARY GLYPHS. The pragma CLI and the summon-core Ink wizard
|
|
6
|
+
* narrate the same run — a ✓ per completed effect, a ✗ when it fails — but
|
|
7
|
+
* they cannot read one module: pragma's renderers are statically reachable
|
|
8
|
+
* from the `--help`/`__complete` fast paths, and THREE protected guards
|
|
9
|
+
* (`create.test.ts`'s static-graph walk, `lazy.test.ts`, the perf budgets)
|
|
10
|
+
* forbid summon-core from that graph — loading it costs a measured
|
|
11
|
+
* double-digit-MB RSS, and an eager summon-core subpath import has already
|
|
12
|
+
* cost the fast paths ~46 ms once (BUDGETS.md). So the duplication is kept
|
|
13
|
+
* and made STRUCTURAL: summon-core publishes its wizard glyphs off the
|
|
14
|
+
* light `/format` seam, and this test pins them byte-equal to the
|
|
15
|
+
* vocabulary's. A divergence is a red build, not a review hope.
|
|
16
|
+
*
|
|
17
|
+
* 2. THE RETIRED WORD STAYS RETIRED, in the model as well as the copy. The
|
|
18
|
+
* render goldens pin that no output says "band"; this pins the same for
|
|
19
|
+
* the CODE of the modules that own setup/doctor's model and rendering, so
|
|
20
|
+
* the split cannot reopen the way it did — words moved, model unmoved.
|
|
21
|
+
* Comments are stripped first: history may NAME the retired word (the
|
|
22
|
+
* vocabulary docblock does, deliberately); code may not USE it.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
26
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
import { describe, expect, it } from "vitest";
|
|
29
|
+
import { GLYPHS } from "../kernel/render/vocabulary.js";
|
|
30
|
+
|
|
31
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
32
|
+
|
|
33
|
+
describe("cross-binary glyph vocabulary (PROTECTED)", () => {
|
|
34
|
+
it("summon-core's wizard marks byte-equal the vocabulary's", async () => {
|
|
35
|
+
// Dynamic import, test-only — production pragma code never reaches
|
|
36
|
+
// summon-core statically, and this test must not become the exception.
|
|
37
|
+
const { COMPLETED_GLYPH, FAILURE_GLYPH } = await import(
|
|
38
|
+
"@canonical/summon-core/format"
|
|
39
|
+
);
|
|
40
|
+
expect(COMPLETED_GLYPH).toBe(GLYPHS.success);
|
|
41
|
+
expect(FAILURE_GLYPH).toBe(GLYPHS.failure);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** The directories whose model/rendering code the word-guard covers. */
|
|
46
|
+
const GUARDED_DIRS = [
|
|
47
|
+
"doctor",
|
|
48
|
+
"setup",
|
|
49
|
+
"shared",
|
|
50
|
+
"sources",
|
|
51
|
+
"skill",
|
|
52
|
+
"../kernel/render",
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
/** Every non-test .ts file under a directory, recursively. */
|
|
56
|
+
function sourceFiles(dir: string): string[] {
|
|
57
|
+
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
58
|
+
const path = join(dir, entry.name);
|
|
59
|
+
if (entry.isDirectory()) return sourceFiles(path);
|
|
60
|
+
if (!entry.name.endsWith(".ts") || entry.name.endsWith(".test.ts"))
|
|
61
|
+
return [];
|
|
62
|
+
return [path];
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Strip block and line comments, leaving only code that compiles to bytes. */
|
|
67
|
+
const stripComments = (source: string): string =>
|
|
68
|
+
source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/[^\n]*/g, "$1");
|
|
69
|
+
|
|
70
|
+
describe("the retired word stays retired in the type layer (PROTECTED)", () => {
|
|
71
|
+
it("no setup/doctor model or render code says `band`", () => {
|
|
72
|
+
const offenders: string[] = [];
|
|
73
|
+
for (const dir of GUARDED_DIRS) {
|
|
74
|
+
for (const file of sourceFiles(resolve(here, dir))) {
|
|
75
|
+
const code = stripComments(readFileSync(file, "utf-8"));
|
|
76
|
+
const hit = code.match(/\bbands?\b/i);
|
|
77
|
+
if (hit) offenders.push(`${relative(here, file)}: ${hit[0]}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
expect(offenders).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -61,8 +61,31 @@ const EVERYTHING: McpListable = { sources: [{ as: "entities" }] };
|
|
|
61
61
|
* only. Payload SIZE is the property that actually regressed (712 entries /
|
|
62
62
|
* ~155 KB on every connect), and it is the one an agent pays for twice: once in
|
|
63
63
|
* transfer, once in the context window it can no longer spend on the task.
|
|
64
|
+
*
|
|
65
|
+
* **Raised from 60,000 to 100,000 on 2026-09-01**, when the
|
|
66
|
+
* `@canonical/token-ontology` pack took the payload to 65,119 bytes. The
|
|
67
|
+
* listing was kept whole rather than trimmed to fit: the per-class counts are
|
|
68
|
+
* how an agent gets the shape of the graph before spending a single lookup,
|
|
69
|
+
* and that is worth more than the bytes.
|
|
70
|
+
*
|
|
71
|
+
* **Cost scales with the number of T-Box terms and their metadata, not with
|
|
72
|
+
* the number of packs.** The `ontology` noun emits one resource per matching
|
|
73
|
+
* T-Box entity (`provider.ts`), so a pack costs what its own vocabulary
|
|
74
|
+
* costs — this one happened to add ~23 KB, which is a fact about this
|
|
75
|
+
* ontology and not a rate. The headroom here does not convert into "one more
|
|
76
|
+
* pack" of any size.
|
|
77
|
+
*
|
|
78
|
+
* It is also the only listing that grows this way: every other noun lists
|
|
79
|
+
* collections with their counts, so those grow with the number of nouns. When
|
|
80
|
+
* this ceiling is next reached, the fix is to scope the T-Box slice or
|
|
81
|
+
* paginate it — not to raise the number again, which would turn a gate into a
|
|
82
|
+
* formality.
|
|
83
|
+
*
|
|
84
|
+
* It is deliberately not removed. The regression it was written for is still
|
|
85
|
+
* worth catching, and a ceiling with headroom catches it while a deleted one
|
|
86
|
+
* catches nothing.
|
|
64
87
|
*/
|
|
65
|
-
const LISTING_BUDGET_BYTES =
|
|
88
|
+
const LISTING_BUDGET_BYTES = 100_000;
|
|
66
89
|
|
|
67
90
|
describe("resource listing (storeless, over the pack index)", () => {
|
|
68
91
|
it("degrades to a recovery entry on a missing or legacy index", () => {
|
|
@@ -322,7 +345,20 @@ describe("resource surface over the server (embedded pack)", () => {
|
|
|
322
345
|
let harness: Awaited<ReturnType<typeof projectMcp>>;
|
|
323
346
|
beforeAll(async () => {
|
|
324
347
|
harness = await projectMcp(capabilities);
|
|
325
|
-
|
|
348
|
+
// Boot the store HERE, not in whichever test happens to read first.
|
|
349
|
+
//
|
|
350
|
+
// `createLazyStore` memoises the boot PROMISE, and vitest's per-test
|
|
351
|
+
// timeout aborts the test without aborting the work — so a cold boot that
|
|
352
|
+
// overruns is charged partly to the test that triggered it and partly to
|
|
353
|
+
// the next one, which merely awaits the same promise. That reads as a
|
|
354
|
+
// sudden per-read regression and is nothing of the kind; it cost a real
|
|
355
|
+
// investigation to find that out.
|
|
356
|
+
//
|
|
357
|
+
// Paying it in the hook attributes it where it belongs, keeps every `it`
|
|
358
|
+
// in this file measuring what it claims to, and bounds it by hookTimeout
|
|
359
|
+
// rather than by a 5 s default that pack growth will keep outgrowing.
|
|
360
|
+
await harness.readResource(BUTTON_URI);
|
|
361
|
+
}, 60_000);
|
|
326
362
|
afterAll(async () => {
|
|
327
363
|
await harness.cleanup();
|
|
328
364
|
});
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Type-sync pin (PROTECTED): the CLI's
|
|
3
|
-
*
|
|
2
|
+
* Type-sync pin (PROTECTED): the CLI's `Scope`/`ScopeSelection` — declared
|
|
3
|
+
* once, in the rendering vocabulary — must stay structurally EQUAL to the
|
|
4
|
+
* `@canonical/harnesses` `ScopeBand`/`ScopeSelection` types.
|
|
4
5
|
*
|
|
5
|
-
* The CLI
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `expectTypeOf` pin enforces at `tsc` time.
|
|
6
|
+
* The CLI declares these in `kernel/render/vocabulary.ts` on purpose — so the
|
|
7
|
+
* statically-reachable type modules never pull the harnesses runtime onto the
|
|
8
|
+
* fast-path module graph (the lazy-dispatch invariant). That freedom is only
|
|
9
|
+
* safe if the declaration does not DRIFT from the source of truth, which this
|
|
10
|
+
* `expectTypeOf` pin enforces at `tsc` time. The NAMES differ deliberately:
|
|
11
|
+
* "band" is the harnesses package's word (its published API), "scope" is
|
|
12
|
+
* pragma's — the word its `--global`/`--local` flags, its rendered sentences
|
|
13
|
+
* and, since the type-layer rename, its model all use.
|
|
14
|
+
*
|
|
15
|
+
* `doctor/types.ts` and `setup/types.ts` re-export the vocabulary's types, and
|
|
16
|
+
* those re-exports are pinned too, so the one-definition property cannot
|
|
17
|
+
* silently regress into a fresh local redeclaration that then drifts.
|
|
10
18
|
*
|
|
11
19
|
* Every harnesses import here is `import type` (type-only, erased at emit), so
|
|
12
20
|
* this file loads no harnesses runtime — the `lazy.test` no-runtime-import guard
|
|
@@ -19,19 +27,25 @@ import type {
|
|
|
19
27
|
ScopeSelection as HarnessScopeSelection,
|
|
20
28
|
} from "@canonical/harnesses";
|
|
21
29
|
import { describe, expectTypeOf, it } from "vitest";
|
|
22
|
-
import type {
|
|
30
|
+
import type { Scope, ScopeSelection } from "../kernel/render/vocabulary.js";
|
|
31
|
+
import type { Scope as DoctorScope } from "./doctor/types.js";
|
|
23
32
|
import type {
|
|
24
|
-
|
|
33
|
+
Scope as SetupScope,
|
|
25
34
|
ScopeSelection as SetupScopeSelection,
|
|
26
35
|
} from "./setup/types.js";
|
|
27
36
|
|
|
28
|
-
describe("scope type-sync — CLI
|
|
29
|
-
it("
|
|
30
|
-
expectTypeOf<
|
|
31
|
-
|
|
37
|
+
describe("scope type-sync — the CLI vocabulary == @canonical/harnesses", () => {
|
|
38
|
+
it("the vocabulary Scope equals the harnesses ScopeBand", () => {
|
|
39
|
+
expectTypeOf<Scope>().toEqualTypeOf<HarnessScopeBand>();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("the vocabulary ScopeSelection equals the harnesses ScopeSelection", () => {
|
|
43
|
+
expectTypeOf<ScopeSelection>().toEqualTypeOf<HarnessScopeSelection>();
|
|
32
44
|
});
|
|
33
45
|
|
|
34
|
-
it("
|
|
35
|
-
expectTypeOf<
|
|
46
|
+
it("doctor and setup re-export the one vocabulary definition", () => {
|
|
47
|
+
expectTypeOf<DoctorScope>().toEqualTypeOf<Scope>();
|
|
48
|
+
expectTypeOf<SetupScope>().toEqualTypeOf<Scope>();
|
|
49
|
+
expectTypeOf<SetupScopeSelection>().toEqualTypeOf<ScopeSelection>();
|
|
36
50
|
});
|
|
37
51
|
});
|