@canonical/pragma-cli 0.33.0 → 0.34.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 +7 -7
- package/dist/pragma +0 -0
- package/package.json +18 -15
- package/pragma.conf.ts +664 -0
- package/src/bin.ts +13 -4
- package/src/capabilities/block.parity.test.ts +250 -0
- package/src/capabilities/capabilities/capabilities.render.ts +3 -2
- package/src/capabilities/capabilities/capabilities.verb.ts +31 -13
- package/src/capabilities/capabilities/catalog.ts +18 -10
- package/src/capabilities/capabilities/hints.ts +13 -19
- package/src/capabilities/colophon/collectColophon.ts +34 -19
- package/src/capabilities/colophon/colophon.verb.ts +8 -4
- package/src/capabilities/colophon/types.ts +16 -7
- package/src/capabilities/config/collectConfigShow.test.ts +57 -0
- package/src/capabilities/config/collectConfigShow.ts +41 -4
- package/src/capabilities/config/fields.ts +0 -27
- package/src/capabilities/config/set.verb.ts +7 -3
- package/src/capabilities/config/show.render.test.ts +77 -5
- package/src/capabilities/config/show.render.ts +7 -12
- package/src/capabilities/config/show.verb.ts +4 -3
- package/src/capabilities/config/types.ts +8 -0
- package/src/capabilities/create/byteEquality.test.ts +78 -100
- package/src/capabilities/create/compiledCreate.subprocess.test.ts +52 -15
- package/src/capabilities/create/constants.ts +75 -0
- package/src/capabilities/create/create.mcp.test.ts +2 -2
- package/src/capabilities/create/create.test.ts +162 -15
- package/src/capabilities/create/create.verb.ts +140 -69
- package/src/capabilities/create/index.ts +2 -10
- package/src/capabilities/create/pickGenerator.ts +34 -28
- package/src/capabilities/create/templates.embedded.generated.ts +4 -29
- package/src/capabilities/create/types.ts +10 -6
- package/src/capabilities/{standard/parity.test.ts → distribution.parity.test.ts} +24 -14
- package/src/capabilities/distribution.test.ts +120 -0
- package/src/capabilities/distribution.ts +59 -0
- package/src/capabilities/doctor/checks/checkConfigFile.ts +6 -5
- package/src/capabilities/doctor/checks/checkKeStore.ts +2 -2
- package/src/capabilities/doctor/checks/checkMcpConfigured.ts +7 -6
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +79 -45
- package/src/capabilities/doctor/checks/checkPragmaVersion.ts +3 -2
- package/src/capabilities/doctor/checks/checkShellCompletions.test.ts +61 -11
- package/src/capabilities/doctor/checks/checkShellCompletions.ts +63 -23
- package/src/capabilities/doctor/checks/checkSkillsSymlinked.ts +3 -2
- package/src/capabilities/doctor/doctor.render.test.ts +6 -6
- package/src/capabilities/doctor/doctor.render.ts +2 -1
- package/src/capabilities/doctor/doctor.test.ts +37 -8
- package/src/capabilities/doctor/doctor.verb.ts +6 -2
- package/src/capabilities/doctor/runChecks.ts +5 -5
- package/src/capabilities/doctor/types.ts +1 -1
- package/src/capabilities/emptyRecovery.test.ts +32 -15
- package/src/capabilities/graph/inspect.verb.ts +3 -2
- package/src/capabilities/graph/query.verb.ts +3 -2
- package/src/capabilities/graph/runQuery.ts +8 -5
- package/src/capabilities/index.ts +22 -18
- package/src/capabilities/info/collectInfo.ts +10 -6
- package/src/capabilities/info/info.render.ts +3 -2
- package/src/capabilities/info/info.verb.ts +3 -2
- package/src/capabilities/lazy.test.ts +93 -2
- package/src/capabilities/ontology/verbs.ts +21 -21
- package/src/capabilities/prompt/prompt.test.ts +12 -6
- package/src/capabilities/prompt/verbs.ts +24 -11
- package/src/capabilities/resources/provider.ts +30 -11
- package/src/capabilities/resources/resources.test.ts +85 -29
- package/src/capabilities/setup/operations/setupGenerator.ts +16 -9
- package/src/capabilities/setup/operations/setupMcp.ts +2 -2
- package/src/capabilities/setup/operations/setupSkills.ts +3 -3
- package/src/capabilities/setup/setup.verb.ts +12 -5
- package/src/capabilities/setup/shell.ts +29 -3
- package/src/capabilities/skill/discover.ts +11 -5
- package/src/capabilities/skill/render.ts +4 -5
- package/src/capabilities/skill/verbs.ts +7 -7
- package/src/capabilities/sources/collectStatus.ts +55 -50
- package/src/capabilities/sources/index.ts +1 -1
- package/src/capabilities/sources/installSkills.ts +2 -2
- package/src/capabilities/sources/runUpdate.ts +113 -122
- package/src/capabilities/sources/sources.test.ts +111 -117
- package/src/capabilities/sources/status.render.test.ts +53 -24
- package/src/capabilities/sources/status.render.ts +24 -29
- package/src/capabilities/sources/status.verb.ts +15 -8
- package/src/capabilities/sources/types.ts +18 -25
- package/src/capabilities/sources/update.render.ts +9 -2
- package/src/capabilities/sources/update.verb.ts +12 -23
- package/src/capabilities/surface.test.ts +33 -10
- package/src/capabilities/tier.parity.test.ts +144 -0
- package/src/capabilities/upgrade/upgrade.verb.ts +7 -3
- package/src/constants.test.ts +8 -6
- package/src/constants.ts +47 -21
- package/src/identity.test.ts +333 -0
- package/src/index.ts +2 -2
- package/src/kernel/completion/__snapshots__/emitScripts.test.ts.snap +5 -2
- package/src/kernel/completion/emitScripts.test.ts +1 -1
- package/src/kernel/completion/emitScripts.ts +4 -2
- package/src/kernel/completion/entitySource.test.ts +185 -41
- package/src/kernel/completion/entitySource.ts +117 -107
- package/src/kernel/completion/model.test.ts +1 -1
- package/src/kernel/completion/model.ts +11 -0
- package/src/kernel/completion/safety.test.ts +82 -36
- package/src/kernel/completion/shellDrive.test.ts +592 -0
- package/src/kernel/completion/templates/fish.ts +28 -6
- package/src/kernel/completion/types.ts +3 -3
- package/src/kernel/config/__snapshots__/firstRun.test.ts.snap +1 -1
- package/src/kernel/config/defaults.test.ts +85 -0
- package/src/kernel/config/defaults.ts +12 -23
- package/src/kernel/config/evaluateProjectConfig.ts +2 -3
- package/src/kernel/config/findProjectConfig.ts +4 -3
- package/src/kernel/config/firstRun.ts +12 -5
- package/src/kernel/config/globalConfig.ts +2 -2
- package/src/kernel/config/index.ts +4 -5
- package/src/kernel/config/paths.ts +17 -17
- package/src/kernel/config/readConfig.test.ts +242 -6
- package/src/kernel/config/readConfig.ts +8 -8
- package/src/kernel/config/schema.test.ts +50 -0
- package/src/kernel/config/schema.ts +97 -10
- package/src/kernel/config/types.ts +73 -19
- package/src/kernel/copy.test.ts +453 -0
- package/src/kernel/error/PragmaError.test.ts +1 -1
- package/src/kernel/error/constants.ts +3 -2
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +22 -45
- package/src/kernel/error/recovery.ts +22 -27
- package/src/kernel/error/types.ts +5 -4
- package/src/kernel/packs/collect.test.ts +195 -11
- package/src/kernel/packs/collect.ts +213 -58
- package/src/kernel/packs/compile.test.ts +163 -7
- package/src/kernel/packs/compile.ts +15 -12
- package/src/kernel/packs/disclosure.test.ts +5 -2
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +11 -10
- package/src/kernel/packs/graphqlSource.test.ts +6 -3
- package/src/kernel/packs/iri.ts +5 -7
- package/src/kernel/packs/renderPack.ts +5 -3
- package/src/kernel/packs/resolveEntity.ts +21 -17
- package/src/kernel/packs/runBodies.ts +15 -14
- package/src/kernel/packs/schema.ts +96 -10
- package/src/kernel/packs/sparql/runSelect.test.ts +104 -10
- package/src/kernel/packs/sparql/runSelect.ts +73 -34
- package/src/kernel/packs/toolDescription.test.ts +10 -2
- package/src/kernel/packs/types.ts +39 -4
- package/src/kernel/packs/uniqueness.ts +4 -4
- package/src/kernel/project/cli/buildProgram.test.ts +9 -4
- package/src/kernel/project/cli/dispatch.test.ts +67 -0
- package/src/kernel/project/cli/dispatch.ts +34 -12
- package/src/kernel/project/cli/helpFormat.ts +1 -1
- package/src/kernel/project/cli/rootHelp.ts +105 -105
- package/src/kernel/project/mcp/buildServer.ts +11 -6
- package/src/kernel/project/mcp/instructions.ts +11 -6
- package/src/kernel/project/mcp/parity.test.ts +39 -1
- package/src/kernel/project/mcp/prompts/prompts.test.ts +120 -5
- package/src/kernel/project/mcp/prompts/provider.ts +22 -8
- package/src/kernel/project/mcp/prompts/source.ts +68 -67
- package/src/kernel/project/mcp/registerVerb.ts +25 -6
- package/src/kernel/project/mcp/serve.ts +11 -3
- package/src/kernel/project/mcp/toolDescriptions.test.ts +6 -2
- package/src/kernel/render/disclosure.ts +4 -2
- package/src/kernel/render/prefixes.test.ts +36 -0
- package/src/kernel/render/prefixes.ts +40 -13
- package/src/kernel/render/renderers.test.ts +1 -1
- package/src/kernel/runtime/graphpack/build.ts +29 -3
- package/src/kernel/runtime/graphpack/buildIndex.test.ts +46 -0
- package/src/kernel/runtime/graphpack/buildIndex.ts +52 -17
- package/src/kernel/runtime/graphpack/embedded/pack.generated.ts +5 -5
- package/src/kernel/runtime/graphpack/embedded/pack.index.generated.ts +3 -3
- package/src/kernel/runtime/graphpack/embedded/pack.stories.generated.ts +3 -0
- package/src/kernel/runtime/graphpack/embedded.ts +37 -11
- package/src/kernel/runtime/graphpack/graphpack.test.ts +205 -5
- package/src/kernel/runtime/graphpack/hash.ts +3 -2
- package/src/kernel/runtime/graphpack/index.ts +1 -1
- package/src/kernel/runtime/graphpack/manifest.ts +114 -12
- package/src/kernel/runtime/graphpack/read.ts +4 -9
- package/src/kernel/runtime/graphpack/schemas.ts +74 -0
- package/src/kernel/runtime/graphpack/stories.ts +84 -0
- package/src/kernel/runtime/graphpack/types.ts +61 -48
- package/src/kernel/runtime/paths.ts +64 -15
- package/src/kernel/runtime/probe.test.ts +8 -4
- package/src/kernel/runtime/readEntity.ts +6 -5
- package/src/kernel/runtime/refs/gitOps.ts +1 -19
- package/src/kernel/runtime/refs/parseRef.ts +7 -7
- package/src/kernel/runtime/refs/resolve.ts +78 -62
- package/src/kernel/runtime/resolveSources.test.ts +63 -24
- package/src/kernel/runtime/resolveSources.ts +36 -25
- package/src/kernel/runtime/storeReadiness.ts +9 -8
- package/src/kernel/runtime/storeless.test.ts +1 -1
- package/src/kernel/runtime/wasmEmbed.test.ts +22 -19
- package/src/kernel/spec/emitReference.test.ts +3 -3
- package/src/kernel/spec/emitReference.ts +144 -11
- package/src/kernel/spec/emitSurface.ts +9 -5
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/surfaceConformance.ts +8 -0
- package/src/kernel/spec/types.ts +29 -9
- package/src/kernel/spec/validate.ts +2 -1
- package/src/kernel/vocabulary.test.ts +164 -0
- package/src/kernel/vocabulary.ts +146 -0
- package/src/testing/PARITY_GAPS.ts +8 -2
- package/src/testing/behavioral/README.md +3 -3
- package/src/testing/behavioral/completion.test.ts +19 -6
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +4 -3
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +1 -1
- package/src/testing/behavioral/exitCodes.e2e.test.ts +6 -5
- package/src/testing/behavioral/harness.foundation.test.ts +14 -5
- package/src/testing/behavioral/journeys.cli.test.ts +116 -75
- package/src/testing/behavioral/journeys.defaultPack.test.ts +82 -176
- package/src/testing/behavioral/journeys.firstInstall.test.ts +167 -0
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +9 -6
- package/src/testing/behavioral/journeys.packageStories.test.ts +188 -0
- package/src/testing/behavioral/liveReadSurface.ts +4 -3
- package/src/testing/behavioral/mcpSurface.test.ts +9 -4
- package/src/testing/behavioral/staleCommands.test.ts +10 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +7 -7
- package/src/testing/eval/cases/assembly.ts +13 -6
- package/src/testing/eval/cases/effectful.ts +25 -13
- package/src/testing/eval/cases/readNouns.ts +22 -20
- package/src/testing/eval/cases/stable.ts +5 -1
- package/src/testing/eval/eval.test.ts +7 -1
- package/src/testing/fixtures/fixtureCapability.ts +45 -1
- package/src/testing/fixtures/graph/canonical.ts +6 -3
- package/src/testing/fixtures/graph/defaultPack.ts +6 -5
- package/src/testing/fixtures/graph/owlExport.ts +7 -5
- package/src/testing/helpers/fixtureGraph.ts +33 -16
- package/src/testing/helpers/packRuntime.ts +2 -3
- package/src/testing/helpers/projectCli.ts +2 -1
- package/src/testing/helpers/projectMcp.ts +6 -0
- package/src/testing/perf/budgets.test.ts +4 -0
- package/src/testing/perf/budgets.ts +22 -7
- package/src/testing/perf/globalSetup.ts +121 -6
- package/src/types.ts +2 -2
- package/src/capabilities/block/blockList.test.ts +0 -79
- package/src/capabilities/block/blockList.verb.ts +0 -163
- package/src/capabilities/block/index.ts +0 -25
- package/src/capabilities/block/pack.ts +0 -166
- package/src/capabilities/block/parity.test.ts +0 -187
- package/src/capabilities/block/tierChain.ts +0 -78
- package/src/capabilities/colophon/pragmaColophon.ts +0 -66
- package/src/capabilities/modifier/index.ts +0 -15
- package/src/capabilities/modifier/pack.ts +0 -64
- package/src/capabilities/standard/index.ts +0 -18
- package/src/capabilities/standard/pack.ts +0 -128
- package/src/capabilities/tier/index.ts +0 -19
- package/src/capabilities/tier/lookup.render.test.ts +0 -72
- package/src/capabilities/tier/lookup.render.ts +0 -59
- package/src/capabilities/tier/lookup.test.ts +0 -83
- package/src/capabilities/tier/lookup.verb.ts +0 -44
- package/src/capabilities/tier/pack.ts +0 -28
- package/src/capabilities/tier/runLookup.ts +0 -87
- package/src/capabilities/token/addConfig.render.ts +0 -27
- package/src/capabilities/token/addConfig.test.ts +0 -62
- package/src/capabilities/token/addConfig.verb.ts +0 -40
- package/src/capabilities/token/index.ts +0 -19
- package/src/capabilities/token/pack.ts +0 -58
- package/src/capabilities/token/runAddConfig.ts +0 -95
- package/src/kernel/completion/bashDrive.test.ts +0 -73
- package/src/kernel/runtime/graphpack/embedded/sample.ttl +0 -39
- package/src/kernel/runtime/lock.ts +0 -90
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The package tier, end to end: a noun the binary never shipped.
|
|
3
|
+
*
|
|
4
|
+
* A package that ships `stories/*.json` and is declared as a `file:` pack
|
|
5
|
+
* contributes a working command to a project that declares it — resolved by
|
|
6
|
+
* `sources update`, hashed into the pack, written into the pack directory, read
|
|
7
|
+
* back storelessly at dispatch, and answered from the fixture graph. Proven both
|
|
8
|
+
* in-process and through the COMPILED binary in a real subprocess.
|
|
9
|
+
*
|
|
10
|
+
* The same package also ships a malformed story and a schema-invalid one. Those
|
|
11
|
+
* must be DROPPED, not fatal: package stories reach dispatch before the command
|
|
12
|
+
* tree exists, so a throw there fails every command — including `sources update`
|
|
13
|
+
* and `doctor`, the only two that can recover from it. Both of those are run
|
|
14
|
+
* here after the bad stories are in the pack.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import { runTask } from "@canonical/task/node";
|
|
20
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
21
|
+
import { runChecks } from "../../capabilities/doctor/runChecks.js";
|
|
22
|
+
import { capabilities } from "../../capabilities/index.js";
|
|
23
|
+
import { buildUpdateTask } from "../../capabilities/sources/runUpdate.js";
|
|
24
|
+
import { loadEffectiveModules } from "../../kernel/packs/collect.js";
|
|
25
|
+
import { packDir, readActivePack } from "../../kernel/runtime/paths.js";
|
|
26
|
+
import type { VerbSpec } from "../../kernel/spec/types.js";
|
|
27
|
+
import {
|
|
28
|
+
bootFixtureRuntime,
|
|
29
|
+
type FixtureGraph,
|
|
30
|
+
} from "../helpers/fixtureGraph.js";
|
|
31
|
+
import { runCli } from "../helpers/runCli.js";
|
|
32
|
+
|
|
33
|
+
const TTL = `
|
|
34
|
+
@prefix ex: <https://example.org/kitchen#> .
|
|
35
|
+
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
|
36
|
+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
|
37
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
38
|
+
|
|
39
|
+
ex:Recipe a owl:Class .
|
|
40
|
+
ex:name a owl:DatatypeProperty ; rdfs:domain ex:Recipe ; rdfs:range xsd:string .
|
|
41
|
+
|
|
42
|
+
ex:soup a ex:Recipe ; ex:name "Soup" .
|
|
43
|
+
ex:stew a ex:Recipe ; ex:name "Stew" .
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
/** A valid read story — the noun the binary never shipped. */
|
|
47
|
+
const RECIPE_STORY = JSON.stringify({
|
|
48
|
+
noun: "recipe",
|
|
49
|
+
description: "List recipes.",
|
|
50
|
+
colophon: "Recipes are modelled as ex:Recipe individuals.",
|
|
51
|
+
list: {
|
|
52
|
+
query:
|
|
53
|
+
"SELECT ?uri ?name WHERE { ?uri a ex:Recipe ; ex:name ?name } ORDER BY ?name",
|
|
54
|
+
columns: [{ field: "uri" }, { field: "name" }],
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
/** Schema-invalid: a well-formed JSON document the pack grammar rejects. */
|
|
59
|
+
const INVALID_STORY = JSON.stringify({ noun: "Bad Noun", list: {} });
|
|
60
|
+
|
|
61
|
+
let graph: FixtureGraph;
|
|
62
|
+
beforeAll(async () => {
|
|
63
|
+
graph = await bootFixtureRuntime({
|
|
64
|
+
ttl: TTL,
|
|
65
|
+
stories: {
|
|
66
|
+
"recipe.json": RECIPE_STORY,
|
|
67
|
+
// Not JSON at all.
|
|
68
|
+
"broken.json": "{ this is not json",
|
|
69
|
+
"invalid.json": INVALID_STORY,
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}, 60_000);
|
|
73
|
+
afterAll(async () => {
|
|
74
|
+
await graph.dispose();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("a package's stories reach the pack", () => {
|
|
78
|
+
it("sources update carries all three story files into the pack, verbatim", () => {
|
|
79
|
+
const hash = readActivePack(graph.cwd);
|
|
80
|
+
expect(hash).toBeDefined();
|
|
81
|
+
const carried = JSON.parse(
|
|
82
|
+
readFileSync(join(packDir(hash as string), "stories.json"), "utf-8"),
|
|
83
|
+
) as { source: string; content: string }[];
|
|
84
|
+
expect(carried.map((record) => record.source)).toEqual([
|
|
85
|
+
"fixture/stories/broken.json",
|
|
86
|
+
"fixture/stories/invalid.json",
|
|
87
|
+
"fixture/stories/recipe.json",
|
|
88
|
+
]);
|
|
89
|
+
expect(
|
|
90
|
+
carried.find((r) => r.source === "fixture/stories/recipe.json")?.content,
|
|
91
|
+
).toBe(RECIPE_STORY);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("a package-declared noun answers reads (in-process)", () => {
|
|
96
|
+
it("recipe list returns rows from the package's own graph", async () => {
|
|
97
|
+
const { modules } = await loadEffectiveModules(capabilities, graph.cwd);
|
|
98
|
+
const recipe = modules.find((module) => module.name === "recipe");
|
|
99
|
+
expect(recipe?.story).toBe(true);
|
|
100
|
+
expect(recipe?.colophon).toBe(
|
|
101
|
+
"Recipes are modelled as ex:Recipe individuals.",
|
|
102
|
+
);
|
|
103
|
+
const list = recipe?.verbs.find(
|
|
104
|
+
(verb) => verb.path.join(" ") === "recipe list",
|
|
105
|
+
) as VerbSpec;
|
|
106
|
+
const rows = (await list.run({}, graph.runtime)) as { name: string }[];
|
|
107
|
+
expect(rows.map((row) => row.name)).toEqual(["Soup", "Stew"]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("names the unusable stories without failing the load", async () => {
|
|
111
|
+
const { problems } = await loadEffectiveModules(capabilities, graph.cwd);
|
|
112
|
+
expect(problems.map((problem) => problem.source).sort()).toEqual([
|
|
113
|
+
"fixture/stories/broken.json",
|
|
114
|
+
"fixture/stories/invalid.json",
|
|
115
|
+
]);
|
|
116
|
+
// Each says WHY, not just that something was wrong.
|
|
117
|
+
const invalid = problems.find((p) => p.source.endsWith("invalid.json"));
|
|
118
|
+
expect(invalid?.message).toMatch(/Invalid story/);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("the two recoveries still run with bad stories in the pack", () => {
|
|
123
|
+
it("sources update re-resolves and reports the carried story count", async () => {
|
|
124
|
+
const result = await runTask(await buildUpdateTask(graph.runtime));
|
|
125
|
+
expect(result.packs).toEqual([
|
|
126
|
+
{
|
|
127
|
+
name: "fixture",
|
|
128
|
+
resolved: expect.any(String),
|
|
129
|
+
sourceCount: 1,
|
|
130
|
+
storyCount: 3,
|
|
131
|
+
},
|
|
132
|
+
]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("doctor passes `pack refs` and lists each ignored story as a failing item", async () => {
|
|
136
|
+
const data = await runChecks(graph.runtime);
|
|
137
|
+
const check = data.checks.find((entry) => entry.name === "pack refs");
|
|
138
|
+
// The pack DOES answer reads, so the check passes…
|
|
139
|
+
expect(check?.status).toBe("pass");
|
|
140
|
+
expect(check?.detail).toContain("2 stories ignored");
|
|
141
|
+
// …with the unusable stories named underneath it.
|
|
142
|
+
expect(check?.items?.map((item) => item.label).sort()).toEqual([
|
|
143
|
+
"fixture/stories/broken.json",
|
|
144
|
+
"fixture/stories/invalid.json",
|
|
145
|
+
]);
|
|
146
|
+
for (const item of check?.items ?? []) expect(item.status).toBe("fail");
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("the compiled binary answers the package's noun (subprocess)", () => {
|
|
151
|
+
it("pragma recipe list --format json returns the package's rows", () => {
|
|
152
|
+
const result = runCli(["recipe", "list", "--format", "json"], {
|
|
153
|
+
cwd: graph.cwd,
|
|
154
|
+
});
|
|
155
|
+
expect(result.exitCode).toBe(0);
|
|
156
|
+
const rows = (JSON.parse(result.stdout) as { data: { name: string }[] })
|
|
157
|
+
.data;
|
|
158
|
+
expect(rows.map((row) => row.name)).toEqual(["Soup", "Stew"]);
|
|
159
|
+
// The bad stories are named on stderr, never on stdout — `--format json`
|
|
160
|
+
// stays machine-readable.
|
|
161
|
+
expect(result.stderr).toContain("fixture/stories/broken.json");
|
|
162
|
+
expect(result.stderr).toContain("fixture/stories/invalid.json");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("pragma capabilities lists the package's tools — the surface agents read", () => {
|
|
166
|
+
// The MCP server registers its tools from the effective modules, so a
|
|
167
|
+
// catalog built from the STATIC registry would omit exactly the nouns
|
|
168
|
+
// `tools/list` advertises. `config show` points agents here for the verbs a
|
|
169
|
+
// story produces; that pointer has to be true.
|
|
170
|
+
const result = runCli(["capabilities", "--format", "json"], {
|
|
171
|
+
cwd: graph.cwd,
|
|
172
|
+
});
|
|
173
|
+
expect(result.exitCode).toBe(0);
|
|
174
|
+
const { data } = JSON.parse(result.stdout) as {
|
|
175
|
+
data: { tools: { name: string }[] };
|
|
176
|
+
};
|
|
177
|
+
expect(data.tools.map((tool) => tool.name)).toContain("recipe_list");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("pragma --help does NOT list recipe — the documented fast-path limit", () => {
|
|
181
|
+
// `--help` reads the STATIC capability set (no config, no pack read), which
|
|
182
|
+
// is what keeps its budget. A package-declared noun is dispatch-only.
|
|
183
|
+
const help = runCli(["--help"], { cwd: graph.cwd });
|
|
184
|
+
expect(help.exitCode).toBe(0);
|
|
185
|
+
expect(help.stdout).not.toContain("recipe");
|
|
186
|
+
// …and the command it does not advertise still runs (asserted above).
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -61,9 +61,10 @@ export const liveVerbs: readonly LiveVerb[] = buildLiveVerbs();
|
|
|
61
61
|
/**
|
|
62
62
|
* Read nouns' `lookup` verbs, MCP-exposed, store-backed — restricted to the
|
|
63
63
|
* PACK-lookup contract (a variadic `<name...>` positional), which the B-tier
|
|
64
|
-
* list→lookup→batch suites drive uniformly.
|
|
65
|
-
* (`
|
|
66
|
-
*
|
|
64
|
+
* list→lookup→batch suites drive uniformly. The one bespoke single-name lookup
|
|
65
|
+
* left (`prompt_lookup`) has its own tests, so it is excluded here structurally
|
|
66
|
+
* (by param shape, not by naming the noun — R2). `tier_lookup` joined the pack
|
|
67
|
+
* contract in L-OPEN-9, so it is swept here like every other declared lookup.
|
|
67
68
|
*/
|
|
68
69
|
export const lookupVerbs: readonly LiveVerb[] = liveVerbs.filter(
|
|
69
70
|
(v) =>
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
22
22
|
import { capabilities } from "../../capabilities/index.js";
|
|
23
23
|
import { readPackIndex } from "../../kernel/completion/entitySource.js";
|
|
24
|
+
import type { PackIndex } from "../../kernel/runtime/graphpack/types.js";
|
|
25
|
+
import { resolveSources } from "../../kernel/runtime/resolveSources.js";
|
|
24
26
|
import {
|
|
25
27
|
ALL_VISIBLE_CONFIG,
|
|
26
28
|
CANONICAL_TTL,
|
|
@@ -41,6 +43,8 @@ let mcp: Awaited<ReturnType<typeof projectMcp>>;
|
|
|
41
43
|
let firstNameByNoun: Map<string, string>;
|
|
42
44
|
/** The registered tool list, fetched once (annotations are call-invariant). */
|
|
43
45
|
let registeredTools: Awaited<ReturnType<typeof mcp.listTools>>;
|
|
46
|
+
/** The index of the pack the fixture's boot decision names. */
|
|
47
|
+
let activeIndex: PackIndex | undefined;
|
|
44
48
|
|
|
45
49
|
beforeAll(async () => {
|
|
46
50
|
fixture = await bootFixtureRuntime({
|
|
@@ -49,6 +53,9 @@ beforeAll(async () => {
|
|
|
49
53
|
});
|
|
50
54
|
mcp = await projectMcp(capabilities, fixture.cwd);
|
|
51
55
|
registeredTools = await mcp.listTools();
|
|
56
|
+
activeIndex = readPackIndex(
|
|
57
|
+
resolveSources(await fixture.runtime.loadConfig(), fixture.cwd),
|
|
58
|
+
);
|
|
52
59
|
|
|
53
60
|
firstNameByNoun = new Map();
|
|
54
61
|
for (const v of listVerbs) {
|
|
@@ -81,15 +88,13 @@ function representativeValue(
|
|
|
81
88
|
param: LiveVerb["spec"]["params"][number],
|
|
82
89
|
): unknown {
|
|
83
90
|
if (param.name === "prefix") {
|
|
84
|
-
const prefix = Object.keys(
|
|
91
|
+
const prefix = Object.keys(activeIndex?.prefixes ?? {}).find(
|
|
85
92
|
(p) => p !== "owl" && p !== "rdfs" && p !== "rdf" && p !== "xsd",
|
|
86
93
|
);
|
|
87
94
|
return prefix ?? "ds";
|
|
88
95
|
}
|
|
89
96
|
if (param.name === "uri") {
|
|
90
|
-
const entity =
|
|
91
|
-
(e) => e.box === "abox",
|
|
92
|
-
);
|
|
97
|
+
const entity = activeIndex?.entities.find((e) => e.box === "abox");
|
|
93
98
|
return entity?.prefixed ?? entity?.name ?? "";
|
|
94
99
|
}
|
|
95
100
|
// An enum positional (e.g. `config set <key>`) must
|
|
@@ -51,6 +51,16 @@ const BANNED: readonly BannedPattern[] = [
|
|
|
51
51
|
reason:
|
|
52
52
|
"retired `--llm` flag (now the `--format llm` form, auto-detected when piped)",
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
pattern: /--all-tiers\b/,
|
|
56
|
+
reason:
|
|
57
|
+
"retired `--all-tiers` flag (L-OPEN-9: `block list` is the declared, unfiltered story — there is no tier filter to escape)",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
pattern: /\btoken[ _]add-config\b/,
|
|
61
|
+
reason:
|
|
62
|
+
"removed `token add-config` command / `token_add-config` tool (L-OPEN-9: a mutation the declared read grammar has no verb for)",
|
|
63
|
+
},
|
|
54
64
|
];
|
|
55
65
|
|
|
56
66
|
// Every shipped doc must be free of retired vocabulary: the README plus every
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`eval harness — seed gate > runs the full seed (stable + read-noun) cases clean 1`] = `
|
|
4
4
|
[
|
|
5
|
+
{
|
|
6
|
+
"id": "content-block-list-is-channel-independent",
|
|
7
|
+
"kind": "content",
|
|
8
|
+
"passed": true,
|
|
9
|
+
},
|
|
5
10
|
{
|
|
6
11
|
"id": "content-canonical-graph-has-4-components",
|
|
7
12
|
"kind": "content",
|
|
@@ -57,11 +62,6 @@ exports[`eval harness — seed gate > runs the full seed (stable + read-noun) ca
|
|
|
57
62
|
"kind": "content",
|
|
58
63
|
"passed": true,
|
|
59
64
|
},
|
|
60
|
-
{
|
|
61
|
-
"id": "content-normal-channel-drops-beta-only-block",
|
|
62
|
-
"kind": "content",
|
|
63
|
-
"passed": true,
|
|
64
|
-
},
|
|
65
65
|
{
|
|
66
66
|
"id": "content-standard-list-length-equals-category-count-sum",
|
|
67
67
|
"kind": "content",
|
|
@@ -113,7 +113,7 @@ exports[`eval harness — seed gate > runs the full seed (stable + read-noun) ca
|
|
|
113
113
|
"passed": true,
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
|
-
"id": "prompt-tokens-config-maps-to-
|
|
116
|
+
"id": "prompt-tokens-config-maps-to-nothing",
|
|
117
117
|
"kind": "prompt",
|
|
118
118
|
"passed": true,
|
|
119
119
|
},
|
|
@@ -183,7 +183,7 @@ exports[`eval harness — seed gate > runs the full seed (stable + read-noun) ca
|
|
|
183
183
|
"passed": true,
|
|
184
184
|
},
|
|
185
185
|
{
|
|
186
|
-
"id": "tool-token-
|
|
186
|
+
"id": "tool-token-noun-is-read-only",
|
|
187
187
|
"kind": "tool",
|
|
188
188
|
"passed": true,
|
|
189
189
|
},
|
|
@@ -171,15 +171,22 @@ export const assemblyEvalCases: readonly EvalCase[] = [
|
|
|
171
171
|
},
|
|
172
172
|
},
|
|
173
173
|
{
|
|
174
|
-
id: "prompt-tokens-config-maps-to-
|
|
174
|
+
id: "prompt-tokens-config-maps-to-nothing",
|
|
175
175
|
kind: "prompt",
|
|
176
176
|
input:
|
|
177
|
-
'"Generate a terrazzo tokens config"
|
|
177
|
+
'"Generate a terrazzo tokens config" maps to NO tool — `token_add-config` was removed in L-OPEN-9, and the token noun is read-only.',
|
|
178
178
|
async expect({ mcp }) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
179
|
+
// The inverse of an intent-mapping case, and the point of the ruling: an
|
|
180
|
+
// agent asked to write a tokens config finds nothing in the catalog that
|
|
181
|
+
// claims to do it, rather than a verb the declared grammar cannot express.
|
|
182
|
+
const tools = await mcp.listTools();
|
|
183
|
+
assert.ok(
|
|
184
|
+
!tools.some(
|
|
185
|
+
(tool) =>
|
|
186
|
+
tool.name === "token_add-config" ||
|
|
187
|
+
/terrazzo|tokens\.config/i.test(String(tool.description ?? "")),
|
|
188
|
+
),
|
|
189
|
+
"no tool may still offer to write a tokens config",
|
|
183
190
|
);
|
|
184
191
|
},
|
|
185
192
|
},
|
|
@@ -170,18 +170,23 @@ export const effectfulEvalCases: readonly EvalCase[] = [
|
|
|
170
170
|
},
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
|
-
id: "tool-token-
|
|
173
|
+
id: "tool-token-noun-is-read-only",
|
|
174
174
|
kind: "tool",
|
|
175
175
|
input:
|
|
176
|
-
"
|
|
176
|
+
"the `token` noun exposes only reads — `token_add-config` (the tokens.config.mjs writer) was REMOVED in L-OPEN-9, not ported.",
|
|
177
177
|
async expect() {
|
|
178
178
|
await withCanonicalFixture(CANONICAL_CONFIG, async (mcp) => {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
// This case used to pin `token_add-config`'s plan-first contract. The
|
|
180
|
+
// verb is gone, so it pins the ruling instead: the read surface is
|
|
181
|
+
// declared content and carries no mutation. (The plan-first MECHANIC is
|
|
182
|
+
// still covered by `tool-config-set-is-plan-first` and
|
|
183
|
+
// `tool-upgrade-is-plan-first`.)
|
|
184
|
+
const tools = (await mcp.listTools()).map((tool) => tool.name);
|
|
185
|
+
assert.ok(!tools.includes("token_add-config"));
|
|
186
|
+
assert.deepEqual(
|
|
187
|
+
tools.filter((name) => name.startsWith("token")).sort(),
|
|
188
|
+
["token_list", "token_lookup", "token_sample"],
|
|
189
|
+
);
|
|
185
190
|
});
|
|
186
191
|
},
|
|
187
192
|
},
|
|
@@ -189,14 +194,21 @@ export const effectfulEvalCases: readonly EvalCase[] = [
|
|
|
189
194
|
id: "tool-tier-lookup-lists-scoped-blocks",
|
|
190
195
|
kind: "tool",
|
|
191
196
|
input:
|
|
192
|
-
"tier_lookup {name:apps/lxd} resolves the tier and
|
|
197
|
+
"tier_lookup {name:[apps/lxd]} resolves the tier and expands the blocks scoped to it (LXD Panel).",
|
|
193
198
|
async expect() {
|
|
194
199
|
await withCanonicalFixture(ALL_VISIBLE_CONFIG, async (mcp) => {
|
|
195
|
-
|
|
200
|
+
// The declared pack lookup (L-OPEN-9): variadic names in, the uniform
|
|
201
|
+
// `{ results, errors }` lookup envelope out, blocks as an expand.
|
|
202
|
+
const result = await mcp.callTool("tier_lookup", {
|
|
203
|
+
name: ["apps/lxd"],
|
|
204
|
+
});
|
|
196
205
|
assert.equal(result.ok, true);
|
|
197
|
-
const data = result.data as {
|
|
198
|
-
|
|
199
|
-
|
|
206
|
+
const data = result.data as {
|
|
207
|
+
results: { name: string; blocks: { name: string }[] }[];
|
|
208
|
+
};
|
|
209
|
+
const tier = data.results[0];
|
|
210
|
+
assert.equal(tier?.name, "apps/lxd");
|
|
211
|
+
assert.ok(tier?.blocks.some((block) => block.name === "LXD Panel"));
|
|
200
212
|
});
|
|
201
213
|
},
|
|
202
214
|
},
|
|
@@ -90,15 +90,15 @@ export const readNounEvalCases: readonly EvalCase[] = [
|
|
|
90
90
|
id: "content-canonical-graph-has-4-components",
|
|
91
91
|
kind: "content",
|
|
92
92
|
input:
|
|
93
|
-
"the canonical fixture graph carries 4 ds:Component individuals (Button, Modal, LXD Panel, Beta Widget);
|
|
93
|
+
"the canonical fixture graph carries 4 ds:Component individuals (Button, Modal, LXD Panel, Beta Widget); block_list also surfaces the untiered Button Icon subcomponent (A2).",
|
|
94
94
|
async expect() {
|
|
95
95
|
await withCanonicalFixture(ALL_VISIBLE_CONFIG, async (mcp) => {
|
|
96
|
-
const result = await mcp.callTool("block_list"
|
|
96
|
+
const result = await mcp.callTool("block_list");
|
|
97
97
|
const names = (result.data as { name: string }[])
|
|
98
98
|
.map((r) => r.name)
|
|
99
99
|
.sort();
|
|
100
|
-
//
|
|
101
|
-
//
|
|
100
|
+
// The declared list takes no arguments and filters nothing: the 4 tiered
|
|
101
|
+
// components plus the untiered Button Icon subcomponent (A2).
|
|
102
102
|
assert.deepEqual(names, [
|
|
103
103
|
"Beta Widget",
|
|
104
104
|
"Button",
|
|
@@ -168,25 +168,27 @@ export const readNounEvalCases: readonly EvalCase[] = [
|
|
|
168
168
|
},
|
|
169
169
|
},
|
|
170
170
|
{
|
|
171
|
-
id: "content-
|
|
171
|
+
id: "content-block-list-is-channel-independent",
|
|
172
172
|
kind: "content",
|
|
173
173
|
input:
|
|
174
|
-
"the normal channel (CANONICAL_CONFIG)
|
|
174
|
+
"block_list returns the SAME rows on the normal channel (CANONICAL_CONFIG) and the prerelease one (ALL_VISIBLE_CONFIG) — including the beta-only block, which the normal channel used to hide (L-OPEN-9).",
|
|
175
175
|
async expect() {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
176
|
+
const namesUnder = async (
|
|
177
|
+
config: typeof CANONICAL_CONFIG | typeof ALL_VISIBLE_CONFIG,
|
|
178
|
+
): Promise<string[]> => {
|
|
179
|
+
let names: string[] = [];
|
|
180
|
+
await withCanonicalFixture(config, async (mcp) => {
|
|
181
|
+
const result = await mcp.callTool("block_list");
|
|
182
|
+
names = (result.data as { name: string }[]).map((r) => r.name).sort();
|
|
183
|
+
});
|
|
184
|
+
return names;
|
|
185
|
+
};
|
|
186
|
+
const normal = await namesUnder(CANONICAL_CONFIG);
|
|
187
|
+
const prerelease = await namesUnder(ALL_VISIBLE_CONFIG);
|
|
188
|
+
// The signed-off consequence: an experimental block is visible to
|
|
189
|
+
// everyone, on every channel, until filtering returns in declared form.
|
|
190
|
+
assert.ok(normal.includes("Beta Widget"));
|
|
191
|
+
assert.deepEqual(normal, prerelease);
|
|
190
192
|
},
|
|
191
193
|
},
|
|
192
194
|
{
|
|
@@ -110,7 +110,11 @@ export const stableEvalCases: readonly EvalCase[] = [
|
|
|
110
110
|
try {
|
|
111
111
|
const widgetModule: CapabilityModule = {
|
|
112
112
|
name: "widget",
|
|
113
|
-
verbs: compilePack(
|
|
113
|
+
verbs: compilePack(
|
|
114
|
+
WIDGET_PACK,
|
|
115
|
+
{ label: "test:widget", origin: "package" },
|
|
116
|
+
WIDGET_PREFIXES,
|
|
117
|
+
),
|
|
114
118
|
};
|
|
115
119
|
const mcp = await projectMcp([widgetModule], fixture.cwd);
|
|
116
120
|
try {
|
|
@@ -70,5 +70,11 @@ describe("eval harness — seed gate", () => {
|
|
|
70
70
|
expect(report.cases.length).toBeGreaterThanOrEqual(35);
|
|
71
71
|
expect(report.cases.length).toBeLessThanOrEqual(60);
|
|
72
72
|
expect(normalize(report.cases)).toMatchSnapshot();
|
|
73
|
-
|
|
73
|
+
// Explicit timeout, like the other suite-level gates: this drives every
|
|
74
|
+
// seed case through a real runtime AND a real MCP server, and since a
|
|
75
|
+
// preview now performs its reads for real rather than answering from a
|
|
76
|
+
// mock, the effectful cases pay genuine IO. Measured 3.7s before that
|
|
77
|
+
// change and 5.4s after, so the vitest 5s default fails it on cost the
|
|
78
|
+
// PR intends to pay — not on a defect. 60s leaves room for CI contention.
|
|
79
|
+
}, 60_000);
|
|
74
80
|
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
-
import { $, gen, succeed, writeFile } from "@canonical/task";
|
|
10
|
+
import { $, gen, readFile, succeed, writeFile } from "@canonical/task";
|
|
11
11
|
import type { CapabilityModule } from "../../kernel/spec/types.js";
|
|
12
12
|
|
|
13
13
|
/** A synthetic module: noun `widget`, three verbs (one hidden). */
|
|
@@ -174,6 +174,50 @@ export const fixtureEffectsModule: CapabilityModule = {
|
|
|
174
174
|
],
|
|
175
175
|
};
|
|
176
176
|
|
|
177
|
+
/**
|
|
178
|
+
* A synthetic module for the HONEST-preview guards (PR7). `probe graft` READS a
|
|
179
|
+
* file before writing beside it, so a run whose source is missing dies on its
|
|
180
|
+
* first effect. The mock previewer answered that read with a placeholder string
|
|
181
|
+
* and reported a confident plan with exit 0 — the structural false positive
|
|
182
|
+
* PRA-104 indicts. Under `runPreview` the read is real, so `--dry-run` (and the
|
|
183
|
+
* MCP plan-first twin) fail exactly where and how the run would. Test-only (D8)
|
|
184
|
+
* — never listed in `capabilities/index.ts`.
|
|
185
|
+
*/
|
|
186
|
+
export const fixturePreviewModule: CapabilityModule = {
|
|
187
|
+
name: "fixture-preview",
|
|
188
|
+
verbs: [
|
|
189
|
+
{
|
|
190
|
+
path: ["probe", "graft"],
|
|
191
|
+
summary: "Copy a file's content into a sibling.",
|
|
192
|
+
params: [
|
|
193
|
+
{
|
|
194
|
+
kind: "string",
|
|
195
|
+
name: "source",
|
|
196
|
+
doc: "The file to read.",
|
|
197
|
+
positional: true,
|
|
198
|
+
required: true,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
output: { formatters: passthroughFormatters },
|
|
202
|
+
capability: {
|
|
203
|
+
needsStore: false,
|
|
204
|
+
mutates: true,
|
|
205
|
+
mcp: {
|
|
206
|
+
expose: true,
|
|
207
|
+
annotations: { readOnlyHint: false, openWorldHint: false },
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
run: (p) =>
|
|
211
|
+
gen(function* () {
|
|
212
|
+
const { source } = p as { source: string };
|
|
213
|
+
const content = yield* $(readFile(source));
|
|
214
|
+
yield* $(writeFile(`${source}.grafted`, content));
|
|
215
|
+
return { grafted: source };
|
|
216
|
+
}),
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
|
|
177
221
|
/**
|
|
178
222
|
* A synthetic module that exercises every RENDER path of `emitReference` (D8 —
|
|
179
223
|
* never listed in `src/capabilities/index.ts`). Distinct from `fixtureModule`,
|
|
@@ -6,9 +6,12 @@
|
|
|
6
6
|
* `block` content-parity + `ontology`/`graph inspect` tests are protected
|
|
7
7
|
* against) byte-for-byte and EXTENDS it with the entities the other bundled
|
|
8
8
|
* read nouns (`standard`, `modifier`, `token`, `tier`) need, plus two more
|
|
9
|
-
* blocks that exercise tier-chain inheritance and channel visibility
|
|
10
|
-
* hand-written `block list`, Risk5).
|
|
11
|
-
*
|
|
9
|
+
* blocks that used to exercise tier-chain inheritance and channel visibility
|
|
10
|
+
* (PR3's hand-written `block list`, Risk5). Those two now serve the opposite
|
|
11
|
+
* purpose: L-OPEN-9 removed the filtering, so a tier-scoped and a
|
|
12
|
+
* channel-gated block are what prove the declared list hides NEITHER. Because
|
|
13
|
+
* `BLOCK_TTL` is reused verbatim, Button/Modal content can never drift between
|
|
14
|
+
* PR3's fixture and this one.
|
|
12
15
|
*
|
|
13
16
|
* Composed as one Turtle document, so ONE store answers every read noun:
|
|
14
17
|
* `block`/`ontology`/`graph inspect` were already provable on `BLOCK_TTL`
|
|
@@ -19,17 +19,18 @@
|
|
|
19
19
|
* 2. Two+ domain classes (`ds:Component` and `ds:Pattern`) so a read that keys
|
|
20
20
|
* off the primary domain type is exercised across more than one class.
|
|
21
21
|
* 3. An UNTIERED block (`ds:orphanWidget` — a `ds:Component` with a `ds:name`
|
|
22
|
-
* but NO `ds:tier`). `block list`'s SELECT inner-
|
|
23
|
-
*
|
|
24
|
-
* `graph query`
|
|
22
|
+
* but NO `ds:tier`). `block list`'s SELECT once inner-joined `?c ds:tier ?t`,
|
|
23
|
+
* silently dropping an untiered block from every listing (A2) even though
|
|
24
|
+
* `graph query` found it; the declared story's OPTIONAL tier join is what
|
|
25
|
+
* the fixture now holds honest.
|
|
25
26
|
* 4. A multilingual `rdfs:label "Button"@en, "Bouton"@fr` — two same-predicate
|
|
26
27
|
* labels whose winner in the single-valued index is store-order-arbitrary
|
|
27
28
|
* (A7), a shape a bare-string fixture can never produce.
|
|
28
29
|
*
|
|
29
30
|
* The vocabulary is the live `ds:` design-system namespace (same as
|
|
30
31
|
* `blockGraph.ts`/`canonical.ts`), so `block list`, `ontology list/show`, and
|
|
31
|
-
* `graph query` all run against real terms — the
|
|
32
|
-
*
|
|
32
|
+
* `graph query` all run against real terms — the `block` story's declared SELECT
|
|
33
|
+
* names `ds:Component`/`ds:Pattern`/`ds:name`/`ds:tier` directly.
|
|
33
34
|
*
|
|
34
35
|
* Stable anchors this fixture guarantees (pinned by the journey suite):
|
|
35
36
|
* - Exactly 4 blocks in the graph: Button (`ds:Component`, global), Card
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A realistic multi-typed OWL / Protégé-export fixture graph.
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
* domain class, every block tiered, every
|
|
6
|
-
*
|
|
7
|
-
* deliberately carries the shapes that only
|
|
4
|
+
* The other fixture graphs (`canonical.ts`, `defaultPack.ts` — Button/Card/
|
|
5
|
+
* Dialog, each typed by exactly ONE domain class, every block tiered, every
|
|
6
|
+
* label untagged) are TOYS: they never exercise the counters/classifiers the way
|
|
7
|
+
* a real vocabulary does. This graph deliberately carries the shapes that only
|
|
8
|
+
* bite real packs (audit backlog A):
|
|
8
9
|
*
|
|
9
10
|
* - Protégé DUAL-typing: an individual asserted as its domain class AND
|
|
10
11
|
* `owl:NamedIndividual` — the multiset that made `entityTotal` run ~2× (A1).
|
|
11
12
|
* - A MULTI-DOMAIN individual (`ex:toggle`: two domain classes) — so the
|
|
12
13
|
* primary-type tie-break must be deterministic, not store-order (A3).
|
|
13
14
|
* - An UNTIERED block (`ds:datePicker`: a `ds:Component` with no `ds:tier`) —
|
|
14
|
-
* which `block list
|
|
15
|
+
* which `block list` must surface, not hide (A2; the tier join is OPTIONAL,
|
|
16
|
+
* and since L-OPEN-9 there is no tier filter at all).
|
|
15
17
|
* - A blank-node `rdf:type` (`ex:field`'s anonymous SHACL-style class) — which
|
|
16
18
|
* must NOT become a `type:"_:b0"` primary key (A6).
|
|
17
19
|
* - An OWL-PUNNED subject (`ex:Slider`: a class that is ALSO a metaclass
|