@canonical/pragma-cli 0.36.0 → 0.38.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 +3 -2
- package/bundled-skills/adoption-a1-styles/SKILL.md +24 -0
- package/bundled-skills/adoption-a2-components/SKILL.md +22 -6
- package/bundled-skills/anatomy-author/ANATOMY_DSL_SPEC.md +1397 -327
- package/bundled-skills/anatomy-author/SKILL.md +840 -137
- package/bundled-skills/design-auditor/SKILL.md +133 -9
- package/bundled-skills/implement-component/SKILL.md +192 -23
- package/bundled-skills/specify-component/SKILL.md +124 -12
- package/bundled-skills/specify-pattern/SKILL.md +47 -8
- package/dist/package.json +23 -20
- package/dist/pragma.conf.js +963 -40
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/capabilities/capabilities/catalog.js +14 -5
- package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
- package/dist/src/capabilities/capabilities/hints.js +33 -5
- package/dist/src/capabilities/capabilities/hints.js.map +1 -1
- package/dist/src/capabilities/config/show.render.js +30 -2
- package/dist/src/capabilities/config/show.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/doctor/checks/checkNodeVersion.js +11 -6
- package/dist/src/capabilities/doctor/checks/checkNodeVersion.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/harnessInventory.js +1 -1
- package/dist/src/capabilities/doctor/checks/harnessInventory.js.map +1 -1
- 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 +104 -14
- package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
- package/dist/src/capabilities/graph/connect.render.js +101 -0
- package/dist/src/capabilities/graph/connect.render.js.map +1 -0
- package/dist/src/capabilities/graph/connect.types.js +21 -0
- package/dist/src/capabilities/graph/connect.types.js.map +1 -0
- package/dist/src/capabilities/graph/connect.verb.js +147 -0
- package/dist/src/capabilities/graph/connect.verb.js.map +1 -0
- package/dist/src/capabilities/graph/index.js +5 -3
- package/dist/src/capabilities/graph/index.js.map +1 -1
- package/dist/src/capabilities/graph/queryText.js +206 -0
- package/dist/src/capabilities/graph/queryText.js.map +1 -0
- package/dist/src/capabilities/graph/relationIndex.js +302 -0
- package/dist/src/capabilities/graph/relationIndex.js.map +1 -0
- package/dist/src/capabilities/graph/runConnect.js +456 -0
- package/dist/src/capabilities/graph/runConnect.js.map +1 -0
- package/dist/src/capabilities/graph/runQuery.js +21 -4
- package/dist/src/capabilities/graph/runQuery.js.map +1 -1
- package/dist/src/capabilities/setup/buildPlan.js +6 -2
- package/dist/src/capabilities/setup/buildPlan.js.map +1 -1
- package/dist/src/capabilities/setup/operations/index.js +7 -2
- 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 +72 -14
- package/dist/src/capabilities/setup/operations/setupGenerator.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupLsp.js +372 -62
- package/dist/src/capabilities/setup/operations/setupLsp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupMcp.js +150 -5
- package/dist/src/capabilities/setup/operations/setupMcp.js.map +1 -1
- package/dist/src/capabilities/setup/operations/setupSkills.js +4 -2
- package/dist/src/capabilities/setup/operations/setupSkills.js.map +1 -1
- package/dist/src/capabilities/setup/operations/writability.js +114 -0
- package/dist/src/capabilities/setup/operations/writability.js.map +1 -0
- package/dist/src/capabilities/setup/plan.js.map +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +80 -31
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/setup/targets.js +124 -42
- package/dist/src/capabilities/setup/targets.js.map +1 -1
- package/dist/src/capabilities/shared/index.js +1 -1
- package/dist/src/capabilities/shared/index.js.map +1 -1
- package/dist/src/constants.js +7 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/error/renderError.js +3 -2
- package/dist/src/kernel/error/renderError.js.map +1 -1
- package/dist/src/kernel/error/validOptions.js +41 -0
- package/dist/src/kernel/error/validOptions.js.map +1 -0
- package/dist/src/kernel/packs/compile.js +90 -6
- package/dist/src/kernel/packs/compile.js.map +1 -1
- package/dist/src/kernel/packs/cursor.js +135 -0
- package/dist/src/kernel/packs/cursor.js.map +1 -0
- package/dist/src/kernel/packs/graphql/buildLookupDocument.js +9 -2
- package/dist/src/kernel/packs/graphql/buildLookupDocument.js.map +1 -1
- package/dist/src/kernel/packs/paging.js +108 -0
- package/dist/src/kernel/packs/paging.js.map +1 -0
- package/dist/src/kernel/packs/renderPack.js +154 -5
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +118 -19
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +156 -18
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +160 -12
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/authorQuery.js +227 -0
- package/dist/src/kernel/packs/sparql/authorQuery.js.map +1 -0
- package/dist/src/kernel/packs/sparql/buildListQuery.js +251 -0
- package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -0
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +125 -22
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/filterValues.js +145 -0
- package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -0
- package/dist/src/kernel/packs/sparql/index.js +24 -14
- package/dist/src/kernel/packs/sparql/index.js.map +1 -1
- package/dist/src/kernel/packs/sparql/runSelect.js +10 -4
- package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
- package/dist/src/kernel/packs/sparql/searchTerm.js +25 -0
- package/dist/src/kernel/packs/sparql/searchTerm.js.map +1 -0
- package/dist/src/kernel/packs/storyRules.js +151 -0
- package/dist/src/kernel/packs/storyRules.js.map +1 -0
- package/dist/src/kernel/packs/tierScope.js +310 -0
- package/dist/src/kernel/packs/tierScope.js.map +1 -0
- package/dist/src/kernel/packs/types.js +80 -0
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +10 -1
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +104 -3
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/rootHelp.js +8 -5
- package/dist/src/kernel/project/cli/rootHelp.js.map +1 -1
- package/dist/src/kernel/project/cli/suggestNames.js +31 -7
- package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +5 -1
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +34 -2
- package/dist/src/kernel/render/renderers.js.map +1 -1
- 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 +17 -10
- 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/readEntity.js +19 -4
- package/dist/src/kernel/runtime/readEntity.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 +38 -5
- 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 +23 -20
- package/pragma.conf.ts +983 -40
- package/src/capabilities/block.parity.test.ts +207 -27
- package/src/capabilities/block.shipped.exec.test.ts +156 -14
- package/src/capabilities/block.tierRank.exec.test.ts +46 -9
- package/src/capabilities/capabilities/catalog.ts +14 -5
- package/src/capabilities/capabilities/hints.ts +40 -5
- package/src/capabilities/config/collectConfigShow.test.ts +1 -0
- package/src/capabilities/config/set.test.ts +24 -25
- package/src/capabilities/config/show.render.test.ts +9 -4
- package/src/capabilities/config/show.render.ts +30 -2
- 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 +4 -1
- package/src/capabilities/create/createSurface.generated.ts +23 -1
- package/src/capabilities/create/crossCli.subprocess.test.ts +10 -3
- package/src/capabilities/distribution.parity.test.ts +12 -11
- package/src/capabilities/distribution.test.ts +77 -7
- package/src/capabilities/doctor/checks/checkNodeVersion.test.ts +49 -0
- package/src/capabilities/doctor/checks/checkNodeVersion.ts +11 -7
- package/src/capabilities/doctor/checks/harnessInventory.test.ts +13 -6
- package/src/capabilities/doctor/checks/harnessInventory.ts +8 -4
- 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 +114 -13
- package/src/capabilities/doctor/doctor.test.ts +327 -36
- package/src/capabilities/emptyRecovery.test.ts +84 -28
- package/src/capabilities/graph/connect.render.ts +141 -0
- package/src/capabilities/graph/connect.shipped.exec.test.ts +370 -0
- package/src/capabilities/graph/connect.test.ts +361 -0
- package/src/capabilities/graph/connect.types.ts +136 -0
- package/src/capabilities/graph/connect.verb.ts +165 -0
- package/src/capabilities/graph/index.ts +5 -3
- package/src/capabilities/graph/query.shipped.exec.test.ts +66 -0
- package/src/capabilities/graph/query.test.ts +17 -0
- package/src/capabilities/graph/queryText.test.ts +121 -0
- package/src/capabilities/graph/queryText.ts +240 -0
- package/src/capabilities/graph/relationIndex.ts +372 -0
- package/src/capabilities/graph/runConnect.ts +577 -0
- package/src/capabilities/graph/runQuery.ts +21 -6
- package/src/capabilities/lazy.test.ts +32 -2
- package/src/capabilities/listBudget.shipped.exec.test.ts +239 -0
- package/src/capabilities/listLookup.shipped.exec.test.ts +387 -0
- package/src/capabilities/listQuery.shipped.exec.test.ts +400 -0
- package/src/capabilities/resources/resources.test.ts +38 -2
- package/src/capabilities/setup/buildPlan.ts +7 -1
- package/src/capabilities/setup/operations/index.ts +14 -0
- 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 +93 -15
- package/src/capabilities/setup/operations/setupLsp.ts +544 -85
- package/src/capabilities/setup/operations/setupMcp.ts +212 -4
- package/src/capabilities/setup/operations/setupSkills.ts +7 -2
- package/src/capabilities/setup/operations/writability.test.ts +151 -0
- package/src/capabilities/setup/operations/writability.ts +135 -0
- package/src/capabilities/setup/plan.ts +11 -0
- package/src/capabilities/setup/setup.test.ts +992 -16
- package/src/capabilities/setup/setup.verb.ts +84 -36
- package/src/capabilities/setup/targets.ts +152 -48
- package/src/capabilities/setup/types.ts +41 -5
- package/src/capabilities/shared/index.ts +1 -0
- package/src/capabilities/sources/sources.test.ts +55 -0
- package/src/capabilities/surface.test.ts +59 -11
- package/src/capabilities/tier.parity.test.ts +2 -2
- package/src/capabilities/token.parity.test.ts +784 -0
- package/src/capabilities/tokenGraph.cli.test.ts +279 -0
- package/src/capabilities/tokenGraph.shipped.exec.test.ts +653 -0
- package/src/capabilities/variable.parity.test.ts +448 -0
- package/src/constants.ts +8 -0
- package/src/kernel/completion/shellDrive.test.ts +32 -30
- package/src/kernel/config/defaults.test.ts +21 -2
- package/src/kernel/config/readConfig.test.ts +1 -1
- package/src/kernel/error/renderError.ts +3 -2
- package/src/kernel/error/validOptions.test.ts +57 -0
- package/src/kernel/error/validOptions.ts +45 -0
- package/src/kernel/packs/collect.test.ts +2 -0
- package/src/kernel/packs/compile.test.ts +188 -15
- package/src/kernel/packs/compile.ts +109 -14
- package/src/kernel/packs/cursor.ts +135 -0
- package/src/kernel/packs/filteredEmpty.test.ts +213 -0
- package/src/kernel/packs/graphql/buildLookupDocument.ts +16 -9
- package/src/kernel/packs/graphqlSource.test.ts +152 -0
- package/src/kernel/packs/listNotice.test.ts +156 -0
- package/src/kernel/packs/paging.ts +110 -0
- package/src/kernel/packs/renderPack.ts +186 -13
- package/src/kernel/packs/resolveEntity.test.ts +62 -3
- package/src/kernel/packs/resolveEntity.ts +186 -26
- package/src/kernel/packs/runBodies.ts +196 -26
- package/src/kernel/packs/schema.ts +180 -13
- package/src/kernel/packs/sparql/authorQuery.test.ts +179 -0
- package/src/kernel/packs/sparql/authorQuery.ts +257 -0
- package/src/kernel/packs/sparql/buildListQuery.test.ts +344 -0
- package/src/kernel/packs/sparql/buildListQuery.ts +344 -0
- package/src/kernel/packs/sparql/buildLookupQuery.test.ts +138 -0
- package/src/kernel/packs/sparql/buildLookupQuery.ts +140 -20
- package/src/kernel/packs/sparql/filterValues.test.ts +86 -0
- package/src/kernel/packs/sparql/filterValues.ts +182 -0
- package/src/kernel/packs/sparql/index.ts +30 -14
- package/src/kernel/packs/sparql/runSelect.test.ts +63 -0
- package/src/kernel/packs/sparql/runSelect.ts +42 -2
- package/src/kernel/packs/sparql/searchTerm.ts +29 -0
- package/src/kernel/packs/storyRules.ts +180 -0
- package/src/kernel/packs/tierScope.test.ts +487 -0
- package/src/kernel/packs/tierScope.ts +409 -0
- package/src/kernel/packs/toolDescription.test.ts +13 -10
- package/src/kernel/packs/types.ts +311 -9
- package/src/kernel/project/cli/buildProgram.ts +17 -1
- package/src/kernel/project/cli/dispatch.test.ts +87 -0
- package/src/kernel/project/cli/dispatch.ts +114 -4
- package/src/kernel/project/cli/rootHelp.ts +8 -5
- package/src/kernel/project/cli/suggest.test.ts +35 -0
- package/src/kernel/project/cli/suggestNames.ts +34 -7
- package/src/kernel/project/mcp/registerVerb.ts +5 -1
- package/src/kernel/render/contracts.ts +2 -0
- package/src/kernel/render/prefixes.test.ts +2 -0
- package/src/kernel/render/renderers.test.ts +43 -0
- package/src/kernel/render/renderers.ts +34 -1
- 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 +17 -13
- package/src/kernel/runtime/loadSession.ts +3 -1
- package/src/kernel/runtime/probe.ts +1 -1
- package/src/kernel/runtime/readEntity.ts +19 -4
- 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 +39 -5
- package/src/kernel/runtime/types.ts +30 -0
- package/src/kernel/spec/emitReference.ts +1 -1
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/types.ts +17 -0
- package/src/testing/PARITY_GAPS.ts +4 -4
- package/src/testing/behavioral/agentSession.mcp.test.ts +43 -39
- package/src/testing/behavioral/completion.test.ts +7 -6
- package/src/testing/behavioral/disclosure.test.ts +39 -37
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +82 -64
- package/src/testing/behavioral/errorMatrix.storeless.test.ts +11 -10
- package/src/testing/behavioral/journeys.cli.test.ts +71 -32
- package/src/testing/behavioral/journeys.defaultPack.test.ts +6 -4
- package/src/testing/behavioral/journeys.firstInstall.test.ts +70 -5
- package/src/testing/behavioral/journeys.mcpLifecycle.mcp.test.ts +4 -3
- package/src/testing/behavioral/journeys.packageStories.test.ts +2 -1
- package/src/testing/behavioral/mcpSurface.test.ts +28 -25
- package/src/testing/behavioral/parity.test.ts +25 -23
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +20 -0
- package/src/testing/eval/cases/effectful.ts +7 -1
- package/src/testing/eval/cases/readNouns.ts +100 -11
- package/src/testing/fixtures/blockGraph.ts +89 -5
- package/src/testing/fixtures/connectGraph.ts +159 -0
- package/src/testing/fixtures/graph/canonical.ts +128 -32
- package/src/testing/helpers/fsProbe.ts +88 -0
- package/src/testing/helpers/runCli.test.ts +44 -0
- package/src/testing/helpers/runCli.ts +27 -3
- package/src/testing/perf/budgets.test.ts +3 -1
- package/src/testing/perf/budgets.ts +138 -54
- package/dist/src/kernel/packs/sparql/applyFilters.js +0 -167
- package/dist/src/kernel/packs/sparql/applyFilters.js.map +0 -1
- package/dist/src/kernel/packs/sparql/applySearch.js +0 -25
- package/dist/src/kernel/packs/sparql/applySearch.js.map +0 -1
- package/src/kernel/packs/sparql/applyFilters.ts +0 -203
- package/src/kernel/packs/sparql/applySearch.ts +0 -33
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// AUTO-GENERATED by scripts/bundle.ts — do not edit by hand.
|
|
2
|
+
// Regenerate from the packs pragma.conf.ts declares: `bun run bundle`.
|
|
3
|
+
export const manifestJson = "{\"name\":\"pragma\",\"version\":\"0.37.0\",\"sourceRef\":\"@canonical/design-system@git:d65f8bacdf2a209b1b33746893c807e5370e1042, @canonical/anatomy-dsl@npm:0.6.0, @canonical/token-ontology@npm:0.10.1, @canonical/code-standards@git:fcd3ac2dea458775e5b79344b3237a3fcb37add9, @canonical/ds-implementations@self:v0.37.0\",\"contentHash\":\"e54849397af95dadd4b37e7d6ddf1d984c92ac27cac4c943db694c361feeb2c5\",\"prefixes\":{\"rdf\":\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\",\"rdfs\":\"http://www.w3.org/2000/01/rdf-schema#\",\"owl\":\"http://www.w3.org/2002/07/owl#\",\"xsd\":\"http://www.w3.org/2001/XMLSchema#\",\"skos\":\"http://www.w3.org/2004/02/skos/core#\",\"sh\":\"http://www.w3.org/ns/shacl#\",\"dcterms\":\"http://purl.org/dc/terms/\",\"ds\":\"https://ds.canonical.com/\",\"anatomy\":\"https://anatomy.canonical.com/\",\"dt\":\"https://dt.canonical.com/\",\"graphql\":\"https://pragma.canonical.com/graphql#\",\"w3c-tokens\":\"https://dt.canonical.com/w3c-tokens/\",\"dt-web\":\"https://dt.canonical.com/platform/web/\",\"cs\":\"http://pragma.canonical.com/codestandards#\"},\"createdAt\":\"2026-09-16T10:11:25.934Z\",\"tripleCount\":65298,\"entityCount\":4377}";
|
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
*
|
|
19
19
|
* The inlined strings are materialized into the ordinary content-addressed pack
|
|
20
20
|
* cache on first use and then read back through {@link readPack}, so the
|
|
21
|
-
* embedded pack and a built pack share one boot path. The story records
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* embedded pack and a built pack share one boot path. The story records, the
|
|
22
|
+
* index and the MANIFEST each live in their own generated module: every one of
|
|
23
|
+
* them is read on a path that does not need the n-quads, and leaving any of
|
|
24
|
+
* them beside `pack.generated.ts` would load its ~2.1 MB with them — a measured
|
|
25
|
+
* +28 ms on every invocation for the stories, and ~370 ms per CLI invocation
|
|
26
|
+
* for the manifest at a 30,340-triple pack. Inlining as JS strings
|
|
25
27
|
* (rather than file assets) means the pack travels with the emitted modules —
|
|
26
28
|
* it needs no asset step in any build, and no file lookup at run time.
|
|
27
29
|
*/
|
|
@@ -36,8 +38,8 @@ import {
|
|
|
36
38
|
} from "node:fs";
|
|
37
39
|
import { join } from "node:path";
|
|
38
40
|
import { packDir, packsCacheDir } from "../paths.js";
|
|
39
|
-
import { dataNq, manifestJson, schemaJson } from "./embedded/pack.generated.js";
|
|
40
41
|
import { indexJson } from "./embedded/pack.index.generated.js";
|
|
42
|
+
import { manifestJson } from "./embedded/pack.manifest.generated.js";
|
|
41
43
|
import { storiesJson } from "./embedded/pack.stories.generated.js";
|
|
42
44
|
import { packIsComplete } from "./manifest.js";
|
|
43
45
|
import { manifestSchema } from "./schemas.js";
|
|
@@ -72,10 +74,16 @@ export function embeddedManifest(): Manifest {
|
|
|
72
74
|
* @returns The pack directory, ready for {@link readPack}.
|
|
73
75
|
* @note Impure — writes the inlined pack files into the cache.
|
|
74
76
|
*/
|
|
75
|
-
export function materializeEmbeddedPack(): string {
|
|
77
|
+
export async function materializeEmbeddedPack(): Promise<string> {
|
|
76
78
|
const dir = packDir(embeddedManifest().contentHash);
|
|
77
79
|
if (packIsComplete(dir)) return dir;
|
|
78
80
|
|
|
81
|
+
// Loaded HERE, not at module scope: this is the only branch that needs the
|
|
82
|
+
// payload, and it is the branch that does NOT run once the pack is cached.
|
|
83
|
+
// A static import would parse ~2.1 MB of inlined n-quads on every boot to
|
|
84
|
+
// reach a return statement six lines above.
|
|
85
|
+
const { dataNq, schemaJson } = await import("./embedded/pack.generated.js");
|
|
86
|
+
|
|
79
87
|
mkdirSync(packsCacheDir(), { recursive: true });
|
|
80
88
|
const temp = mkdtempSync(join(packsCacheDir(), ".embed-"));
|
|
81
89
|
try {
|
|
@@ -144,15 +144,21 @@ describe("the committed embedded pack (PROTECTED)", () => {
|
|
|
144
144
|
// directory holds — which the next build then reuses, silently dropping the
|
|
145
145
|
// difference. Comparing the two directories catches that on the day it lands.
|
|
146
146
|
const built = await build([{ path: "a.ttl", content: TTL }]);
|
|
147
|
-
expect(readdirSync(materializeEmbeddedPack()).sort()).toEqual(
|
|
147
|
+
expect(readdirSync(await materializeEmbeddedPack()).sort()).toEqual(
|
|
148
148
|
readdirSync(built.dir).sort(),
|
|
149
149
|
);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
// 60s, not the 5s default. This test does real work twice: it BUILDS a
|
|
151
|
+
// pack from source, and it materialises the embedded one — which on a cold
|
|
152
|
+
// cache takes the miss branch and imports the multi-megabyte payload. Both
|
|
153
|
+
// costs grow with the pack, and the pack grows whenever a source is added.
|
|
154
|
+
// A default that a pack size can outgrow turns a real assertion into an
|
|
155
|
+
// intermittent one.
|
|
156
|
+
}, 60_000);
|
|
157
|
+
|
|
158
|
+
it("is self-consistent: complete, content-addressed, and non-empty", async () => {
|
|
153
159
|
// No network, so CI runs it: the committed strings really do materialize a
|
|
154
160
|
// bootable pack whose parts agree with each other.
|
|
155
|
-
const dir = materializeEmbeddedPack();
|
|
161
|
+
const dir = await materializeEmbeddedPack();
|
|
156
162
|
const manifest = embeddedManifest();
|
|
157
163
|
expect(packIsComplete(dir)).toBe(true);
|
|
158
164
|
expect(basename(dir)).toBe(manifest.contentHash);
|
|
@@ -162,6 +168,26 @@ describe("the committed embedded pack (PROTECTED)", () => {
|
|
|
162
168
|
expect(index.contentHash).toBe(manifest.contentHash);
|
|
163
169
|
expect(manifest.tripleCount ?? 0).toBeGreaterThan(0);
|
|
164
170
|
expect(manifest.entityCount ?? 0).toBeGreaterThan(0);
|
|
171
|
+
|
|
172
|
+
// EXACT, not "> 0", and it earns its keep twice.
|
|
173
|
+
//
|
|
174
|
+
// The manifest and the n-quads now live in SEPARATE generated modules, so
|
|
175
|
+
// a partial regeneration or a one-file `git add` can leave them describing
|
|
176
|
+
// different packs — a skew that could not exist while they shared a
|
|
177
|
+
// module. `read.ts` tolerates a superset deliberately, so it would serve
|
|
178
|
+
// the skew rather than refuse it.
|
|
179
|
+
//
|
|
180
|
+
// It also pins the equivalence `read.ts` depends on but cannot state:
|
|
181
|
+
// `store.size` (quads) against a `tripleCount` that `build.ts` computes
|
|
182
|
+
// with `SELECT (COUNT(*))` over the union graph. They agree on oxigraph
|
|
183
|
+
// 0.5.6; oxigraph is pinned by caret, so this is where a future version
|
|
184
|
+
// that deduplicates the union would be caught.
|
|
185
|
+
const session = await readPack(dir);
|
|
186
|
+
try {
|
|
187
|
+
expect(session.store.size).toBe(manifest.tripleCount);
|
|
188
|
+
} finally {
|
|
189
|
+
session.store.dispose();
|
|
190
|
+
}
|
|
165
191
|
});
|
|
166
192
|
});
|
|
167
193
|
|
|
@@ -39,7 +39,8 @@ function packUnavailable(reason: string): PragmaError {
|
|
|
39
39
|
* Read a pack directory into a bootable store session.
|
|
40
40
|
*
|
|
41
41
|
* @param dir - The pack directory (must hold a complete pack).
|
|
42
|
-
* @returns The store session: store, schema, context factory, prefixes, index
|
|
42
|
+
* @returns The store session: store, schema, context factory, prefixes, index,
|
|
43
|
+
* and the pack's own manifest (its provenance, carried rather than re-read).
|
|
43
44
|
* @throws PragmaError STORE_UNAVAILABLE when the pack is incomplete.
|
|
44
45
|
* @note Impure — creates a store from the cached n-quads dump.
|
|
45
46
|
*/
|
|
@@ -80,15 +81,25 @@ export async function readPack(dir: string): Promise<StoreSession> {
|
|
|
80
81
|
// "populated index but empty store" check (via the now-empty dump,
|
|
81
82
|
// `packIsComplete` turns false so recovery rebuilds) rather than serving an
|
|
82
83
|
// empty graph as the pack.
|
|
83
|
-
|
|
84
|
+
// `store.size` is a field oxigraph maintains. The equivalent query —
|
|
85
|
+
// `SELECT (COUNT(*)) WHERE { ?s ?p ?o }`, which is what `build.ts` still runs
|
|
86
|
+
// to WRITE `tripleCount` — is a scan, and it ran on every boot to read a
|
|
87
|
+
// number the store already knew.
|
|
88
|
+
//
|
|
89
|
+
// The two agree: ke queries with `use_default_graph_as_union`, and oxigraph
|
|
90
|
+
// does not deduplicate across that union, so a quad count and the union's
|
|
91
|
+
// solution count are the same number even for a triple repeated across
|
|
92
|
+
// graphs. Verified against 0.5.6 rather than assumed — and the guard is `<`,
|
|
93
|
+
// which tolerates the direction a set-semantic union would drift.
|
|
94
|
+
const actualQuads = store.size;
|
|
84
95
|
if (manifest.tripleCount !== undefined) {
|
|
85
|
-
if (
|
|
96
|
+
if (actualQuads < manifest.tripleCount) {
|
|
86
97
|
store.dispose();
|
|
87
98
|
throw packUnavailable(
|
|
88
|
-
`The pack at ${dir} has a corrupt data cache (expected at least ${manifest.tripleCount} triples, loaded ${
|
|
99
|
+
`The pack at ${dir} has a corrupt data cache (expected at least ${manifest.tripleCount} triples, loaded ${actualQuads}).`,
|
|
89
100
|
);
|
|
90
101
|
}
|
|
91
|
-
} else if (index.entities.length > 0 &&
|
|
102
|
+
} else if (index.entities.length > 0 && actualQuads === 0) {
|
|
92
103
|
store.dispose();
|
|
93
104
|
throw packUnavailable(`The pack at ${dir} has a corrupt data cache.`);
|
|
94
105
|
}
|
|
@@ -99,13 +110,6 @@ export async function readPack(dir: string): Promise<StoreSession> {
|
|
|
99
110
|
createContext: compiled.createContext,
|
|
100
111
|
prefixes: manifest.prefixes,
|
|
101
112
|
index,
|
|
113
|
+
manifest,
|
|
102
114
|
};
|
|
103
115
|
}
|
|
104
|
-
|
|
105
|
-
/** Count the booted store's triples (a cheap aggregate over the union graph). */
|
|
106
|
-
async function countTriples(store: StoreSession["store"]): Promise<number> {
|
|
107
|
-
const result = (await store.query(
|
|
108
|
-
"SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }" as never,
|
|
109
|
-
)) as import("@canonical/ke").SelectResult;
|
|
110
|
-
return Number(result.bindings.at(0)?.n ?? 0);
|
|
111
|
-
}
|
|
@@ -21,7 +21,7 @@ export async function runStoreProbe(): Promise<string> {
|
|
|
21
21
|
import("./graphpack/embedded.js"),
|
|
22
22
|
import("./graphpack/read.js"),
|
|
23
23
|
]);
|
|
24
|
-
const session = await readPack(materializeEmbeddedPack());
|
|
24
|
+
const session = await readPack(await materializeEmbeddedPack());
|
|
25
25
|
try {
|
|
26
26
|
const result = await session.store.query(
|
|
27
27
|
"SELECT (COUNT(*) AS ?n) WHERE { ?s ?p ?o }" as never,
|
|
@@ -82,8 +82,13 @@ type Term = import("@canonical/ke").Term;
|
|
|
82
82
|
* Membership edges behave identically — this graph's `ds:tier` fans in 141 deep
|
|
83
83
|
* — and a rule keyed on `rdf:type` would have left a `detailed` read of one tier
|
|
84
84
|
* at 20.9 KB while congratulating itself on having fixed classes.
|
|
85
|
+
*
|
|
86
|
+
* EXPORTED because it has a second consumer: `graph connect` decides which
|
|
87
|
+
* edges it may WALK by the same rule this reader decides what to list. One
|
|
88
|
+
* constant with two consumers, never two constants that agree today — a copied
|
|
89
|
+
* `20` would let the traversal drift from the doctrine it claims to obey.
|
|
85
90
|
*/
|
|
86
|
-
const ROSTER_THRESHOLD = 20;
|
|
91
|
+
export const ROSTER_THRESHOLD = 20;
|
|
87
92
|
|
|
88
93
|
/** How many subjects each level lists for a genuine ANSWER (fan-in ≤ threshold). */
|
|
89
94
|
const ANSWER_CAP: Readonly<Record<DetailLevel, number>> = {
|
|
@@ -254,16 +259,26 @@ export interface InspectResult {
|
|
|
254
259
|
* word twice. A graph whose labels are real prose (a concept at `ds:concept.pd`
|
|
255
260
|
* named "Progressive disclosure") still keeps them, so this thins noise without
|
|
256
261
|
* deciding that labels are worthless.
|
|
262
|
+
*
|
|
263
|
+
* Exported beside {@link nameIndex}: the two are ONE rule for naming a
|
|
264
|
+
* neighbour, and a consumer that took the map without the filter would print
|
|
265
|
+
* exactly the noise this function exists to drop.
|
|
257
266
|
*/
|
|
258
|
-
function isInformativeName(name: string, short: string): boolean {
|
|
267
|
+
export function isInformativeName(name: string, short: string): boolean {
|
|
259
268
|
const normalize = (value: string): string =>
|
|
260
269
|
value.replace(/[\s._:-]/g, "").toLowerCase();
|
|
261
270
|
const local = short.split(/[:.]/).pop() ?? short;
|
|
262
271
|
return normalize(name) !== normalize(local);
|
|
263
272
|
}
|
|
264
273
|
|
|
265
|
-
/**
|
|
266
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Index lookup for a neighbour's human name (`label`, else the first alt name).
|
|
276
|
+
*
|
|
277
|
+
* EXPORTED for the same reason as {@link ROSTER_THRESHOLD}: `graph connect`
|
|
278
|
+
* names the nodes on a path, and it must name them the way this reader does or
|
|
279
|
+
* the same neighbour reads differently depending on which verb showed it.
|
|
280
|
+
*/
|
|
281
|
+
export function nameIndex(index: PackIndex): ReadonlyMap<string, string> {
|
|
267
282
|
const names = new Map<string, string>();
|
|
268
283
|
for (const entity of index.entities) {
|
|
269
284
|
if (!entity.uri) continue;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { redactUrl } from "./parseRef.js";
|
|
2
|
+
import { parsePackDeclaration, redactUrl } from "./parseRef.js";
|
|
3
3
|
|
|
4
4
|
describe("redactUrl", () => {
|
|
5
5
|
it("strips user:password userinfo so an inlined token never reaches output", () => {
|
|
@@ -32,3 +32,51 @@ describe("redactUrl", () => {
|
|
|
32
32
|
);
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
describe("parsePackDeclaration — a git source may name a subdirectory", () => {
|
|
37
|
+
const url = "git+https://github.com/org/repo.git";
|
|
38
|
+
|
|
39
|
+
it("reaches a package nested in a monorepo", () => {
|
|
40
|
+
expect(
|
|
41
|
+
parsePackDeclaration({
|
|
42
|
+
name: "@org/thing",
|
|
43
|
+
source: `${url}#main:pkgs/thing`,
|
|
44
|
+
}),
|
|
45
|
+
).toEqual({
|
|
46
|
+
kind: "git",
|
|
47
|
+
pkg: "@org/thing",
|
|
48
|
+
url: "https://github.com/org/repo.git",
|
|
49
|
+
ref: "main",
|
|
50
|
+
subdir: "pkgs/thing",
|
|
51
|
+
source: `${url}#main:pkgs/thing`,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("leaves a plain ref untouched, with no subdir key", () => {
|
|
56
|
+
const ref = parsePackDeclaration({
|
|
57
|
+
name: "@org/thing",
|
|
58
|
+
source: `${url}#main`,
|
|
59
|
+
});
|
|
60
|
+
expect(ref).toEqual({
|
|
61
|
+
kind: "git",
|
|
62
|
+
pkg: "@org/thing",
|
|
63
|
+
url: "https://github.com/org/repo.git",
|
|
64
|
+
ref: "main",
|
|
65
|
+
source: `${url}#main`,
|
|
66
|
+
});
|
|
67
|
+
expect("subdir" in ref).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("splits on the FIRST colon, so a nested path survives", () => {
|
|
71
|
+
// The ref cannot contain a colon; the subdirectory can contain slashes.
|
|
72
|
+
expect(
|
|
73
|
+
parsePackDeclaration({ name: "@org/t", source: `${url}#v1.2:a/b/c` }),
|
|
74
|
+
).toMatchObject({ ref: "v1.2", subdir: "a/b/c" });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("rejects an empty subdirectory rather than silently ignoring it", () => {
|
|
78
|
+
expect(() =>
|
|
79
|
+
parsePackDeclaration({ name: "@org/thing", source: `${url}#main:` }),
|
|
80
|
+
).toThrow(/subdirectory after : is empty/);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -25,6 +25,8 @@ export type PackageRef =
|
|
|
25
25
|
readonly pkg: string;
|
|
26
26
|
readonly url: string;
|
|
27
27
|
readonly ref: string;
|
|
28
|
+
/** Package root WITHIN the clone, for a package nested in a monorepo. */
|
|
29
|
+
readonly subdir?: string;
|
|
28
30
|
readonly source: string;
|
|
29
31
|
};
|
|
30
32
|
|
|
@@ -72,11 +74,33 @@ export function parsePackDeclaration(entry: PackDeclaration): PackageRef {
|
|
|
72
74
|
},
|
|
73
75
|
);
|
|
74
76
|
}
|
|
77
|
+
// `#<ref>` or `#<ref>:<subdir>`. A git ref cannot contain a colon
|
|
78
|
+
// (git check-ref-format), so the split is unambiguous.
|
|
79
|
+
const fragment = source.slice(hashIdx + 1);
|
|
80
|
+
const colonIdx = fragment.indexOf(":");
|
|
81
|
+
if (colonIdx !== -1 && colonIdx === fragment.length - 1) {
|
|
82
|
+
throw PragmaError.configError(
|
|
83
|
+
`Invalid source for "${name}": the subdirectory after : is empty.`,
|
|
84
|
+
{
|
|
85
|
+
recovery: {
|
|
86
|
+
message:
|
|
87
|
+
"Example: git+https://github.com/org/repo.git#main:packages/thing",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const ref = colonIdx === -1 ? fragment : fragment.slice(0, colonIdx);
|
|
93
|
+
if (ref.length === 0) {
|
|
94
|
+
throw PragmaError.configError(
|
|
95
|
+
`Invalid source for "${name}": git URL must include a ref after #.`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
75
98
|
return {
|
|
76
99
|
kind: "git",
|
|
77
100
|
pkg: name,
|
|
78
101
|
url: source.slice("git+".length, hashIdx),
|
|
79
|
-
ref
|
|
102
|
+
ref,
|
|
103
|
+
...(colonIdx === -1 ? {} : { subdir: fragment.slice(colonIdx + 1) }),
|
|
80
104
|
source,
|
|
81
105
|
};
|
|
82
106
|
}
|
|
@@ -184,6 +184,34 @@ function readStorySources(
|
|
|
184
184
|
*/
|
|
185
185
|
const PREFIX_DECL = /(?:^|\s)@?prefix\s+([^\s:]+):\s*<([^>]*)>/gi;
|
|
186
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Blank out every Turtle string literal, so a prefix scan reads declarations
|
|
189
|
+
* and not prose.
|
|
190
|
+
*
|
|
191
|
+
* A long literal can legally contain the text `@prefix ex: <iri> .`, and one
|
|
192
|
+
* genuinely does: the code-standards pack ships guidance ABOUT Turtle, whose
|
|
193
|
+
* `cs:code` examples include a deliberately discouraged declaration. Scanning
|
|
194
|
+
* raw text saw it as real, which made `sources update` report a `ds:` clash on
|
|
195
|
+
* every run for a file that asserts no `ds:` triple at all — and worse, let a
|
|
196
|
+
* code sample win the last-wins race in {@link harvestPrefixes} and rebind a
|
|
197
|
+
* real prefix.
|
|
198
|
+
*
|
|
199
|
+
* Long forms are blanked before short ones, so the `"""` delimiters are not
|
|
200
|
+
* mistaken for an empty `""` followed by prose. Newlines inside a literal are
|
|
201
|
+
* preserved so a later `^`-anchored match still sees the true line structure.
|
|
202
|
+
*
|
|
203
|
+
* @param content - Turtle source text.
|
|
204
|
+
* @returns The same text with literal contents replaced by blanks.
|
|
205
|
+
*/
|
|
206
|
+
function blankLiterals(content: string): string {
|
|
207
|
+
const keepNewlines = (text: string): string => text.replace(/[^\n]/g, " ");
|
|
208
|
+
return content
|
|
209
|
+
.replace(/"""[\s\S]*?"""/g, keepNewlines)
|
|
210
|
+
.replace(/'''[\s\S]*?'''/g, keepNewlines)
|
|
211
|
+
.replace(/"(?:[^"\\\n]|\\.)*"/g, keepNewlines)
|
|
212
|
+
.replace(/'(?:[^'\\\n]|\\.)*'/g, keepNewlines);
|
|
213
|
+
}
|
|
214
|
+
|
|
187
215
|
/**
|
|
188
216
|
* Harvest a package's own `@prefix` / `PREFIX` declarations from its TTL text.
|
|
189
217
|
*
|
|
@@ -201,7 +229,7 @@ export function harvestPrefixes(
|
|
|
201
229
|
): Record<string, string> {
|
|
202
230
|
const prefixes: Record<string, string> = {};
|
|
203
231
|
for (const { content } of sources) {
|
|
204
|
-
for (const match of content.matchAll(PREFIX_DECL)) {
|
|
232
|
+
for (const match of blankLiterals(content).matchAll(PREFIX_DECL)) {
|
|
205
233
|
const [, label, iri] = match;
|
|
206
234
|
if (label !== undefined && iri !== undefined) prefixes[label] = iri;
|
|
207
235
|
}
|
|
@@ -234,7 +262,7 @@ export function detectPrefixClashes(
|
|
|
234
262
|
): PrefixClash[] {
|
|
235
263
|
const byLabel = new Map<string, string[]>();
|
|
236
264
|
for (const { content } of sources) {
|
|
237
|
-
for (const match of content.matchAll(PREFIX_DECL)) {
|
|
265
|
+
for (const match of blankLiterals(content).matchAll(PREFIX_DECL)) {
|
|
238
266
|
const [, label, iri] = match;
|
|
239
267
|
if (label === undefined || iri === undefined) continue;
|
|
240
268
|
const iris = byLabel.get(label) ?? [];
|
|
@@ -383,13 +411,19 @@ export async function resolvePackage(
|
|
|
383
411
|
},
|
|
384
412
|
);
|
|
385
413
|
}
|
|
414
|
+
// The clone lives at `dir`; the PACKAGE may be nested within it. A git
|
|
415
|
+
// source names a repository, so a package inside a monorepo is reached
|
|
416
|
+
// by its subdirectory — without which `readTtlSources` would scan a
|
|
417
|
+
// repository root that has no `definitions/` or `data/` and contribute
|
|
418
|
+
// nothing at all.
|
|
419
|
+
const packageRoot = ref.subdir ? join(dir, ref.subdir) : dir;
|
|
386
420
|
return {
|
|
387
421
|
name: ref.pkg,
|
|
388
422
|
source: ref.source,
|
|
389
423
|
resolved,
|
|
390
|
-
root:
|
|
391
|
-
sources: readTtlSources(
|
|
392
|
-
stories: readStorySources(
|
|
424
|
+
root: packageRoot,
|
|
425
|
+
sources: readTtlSources(packageRoot, ref.pkg),
|
|
426
|
+
stories: readStorySources(packageRoot, ref.pkg),
|
|
393
427
|
};
|
|
394
428
|
}
|
|
395
429
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* that are actually populated by the time it runs.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type { UndoOutcome } from "@canonical/task/node";
|
|
11
12
|
import type { DetailLevel, OutputFormat } from "../../constants.js";
|
|
12
13
|
import type { ConfigLayers } from "../config/types.js";
|
|
13
14
|
|
|
@@ -53,6 +54,19 @@ export interface StoreSession {
|
|
|
53
54
|
readonly prefixes: Readonly<Record<string, string>>;
|
|
54
55
|
/** The storeless entity index shipped with the pack. */
|
|
55
56
|
readonly index: import("./graphpack/types.js").PackIndex;
|
|
57
|
+
/**
|
|
58
|
+
* The provenance of the pack this session was booted from — the parsed
|
|
59
|
+
* `manifest.json` the boot ALREADY read to get {@link prefixes}.
|
|
60
|
+
*
|
|
61
|
+
* Carried rather than re-read because an answer that depends on WHICH packs
|
|
62
|
+
* were searched has to be able to say so from the session it was answered
|
|
63
|
+
* from. `graph connect`'s most common answer is "these two are not
|
|
64
|
+
* connected", and a missing pack makes the honest and the dishonest form of
|
|
65
|
+
* that answer byte-identical — so the answer names `sourceRef`, and it must
|
|
66
|
+
* be the loaded session's, not one re-resolved from config a caller could
|
|
67
|
+
* have changed since.
|
|
68
|
+
*/
|
|
69
|
+
readonly manifest: import("./graphpack/types.js").Manifest;
|
|
56
70
|
}
|
|
57
71
|
|
|
58
72
|
/**
|
|
@@ -221,4 +235,20 @@ export interface PragmaRuntime {
|
|
|
221
235
|
* plan of its own, which is what keeps the raw-effect render the default.
|
|
222
236
|
*/
|
|
223
237
|
planData?: unknown;
|
|
238
|
+
/**
|
|
239
|
+
* The post-undo projection seam. `--undo` is two-phase: the undo
|
|
240
|
+
* interpreter WALKS the verb's task with every effect mocked to collect the
|
|
241
|
+
* reversals, and only then executes what it collected — so nothing the task
|
|
242
|
+
* body says while it is interpreted describes what the reversals did (the
|
|
243
|
+
* body only ever runs under the mocked walk). What DOES describe them is
|
|
244
|
+
* the interpreter's own per-undo outcomes, correlated by the `undoKey` each
|
|
245
|
+
* reversal was declared with. A verb that reports per-target results sets
|
|
246
|
+
* this (the same way it sets `exec`/`planData`, before returning its Task);
|
|
247
|
+
* the projector's undo branch awaits it with those outcomes AFTER the
|
|
248
|
+
* collected undos executed and before the `Undid N step(s).` line, so the
|
|
249
|
+
* verb projects them onto its own rows through the same path its forward
|
|
250
|
+
* run uses. A throw here is rendered as the run's error (nonzero exit) —
|
|
251
|
+
* the honest outcome when a reversal did not take.
|
|
252
|
+
*/
|
|
253
|
+
undoReport?: (outcomes: readonly UndoOutcome[]) => void | Promise<void>;
|
|
224
254
|
}
|
|
@@ -556,7 +556,7 @@ const CONFIG_FIELD_DOCS: Record<keyof RawConfig, ConfigFieldDoc> = {
|
|
|
556
556
|
tier: {
|
|
557
557
|
type: "string (optional)",
|
|
558
558
|
notes:
|
|
559
|
-
"
|
|
559
|
+
"The DEFAULT TIER SCOPE for every read of a tiered entity (blocks, modifier families, concepts): reads answer from this tier and its ancestors — `apps_lxd` reads `global`, `apps` and `apps_lxd`. Unset, they answer from the top-level tiers; set to `all`, from every tier. A per-call `--tier <name>` (the `tier` tool parameter) replaces it for that call, and every scoped answer states the scope it used. Name a tier the way `tier list` or a `Tier` column spells it (`apps_lxd`, `Apps/LXD` and `apps/lxd` all resolve); an unknown name is an `INVALID_INPUT` listing the tiers that exist. Set it with `config set tier <name>` and clear it with `config unset tier`; `none`, `default` and `-` are refused as values, so no string doubles as a remove-marker.",
|
|
560
560
|
},
|
|
561
561
|
channel: {
|
|
562
562
|
type: "`normal` | `experimental` | `prerelease` (optional)",
|
|
@@ -84,8 +84,8 @@ describe("surface conformance (PROTECTED)", () => {
|
|
|
84
84
|
);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
it("freezes exactly
|
|
88
|
-
expect(golden.mcpSurface.tools).toHaveLength(
|
|
87
|
+
it("freezes exactly 49 designed MCP tools including info, the config family, colophon", () => {
|
|
88
|
+
expect(golden.mcpSurface.tools).toHaveLength(49);
|
|
89
89
|
expect(golden.mcpSurface.tools).toContain("info");
|
|
90
90
|
expect(golden.mcpSurface.tools).toContain("config_show");
|
|
91
91
|
expect(golden.mcpSurface.tools).toContain("config_get");
|
package/src/kernel/spec/types.ts
CHANGED
|
@@ -192,6 +192,23 @@ export interface Formatters<T> {
|
|
|
192
192
|
readonly llm: (d: T) => string;
|
|
193
193
|
readonly json: (d: T) => string;
|
|
194
194
|
readonly notice?: (d: T) => string | undefined;
|
|
195
|
+
/**
|
|
196
|
+
* The MACHINE half of the notice seam: facts about the read that belong in
|
|
197
|
+
* the envelope's `meta` as DATA, merged there by both projectors under the
|
|
198
|
+
* keys this returns.
|
|
199
|
+
*
|
|
200
|
+
* `notice` is a sentence, and a sentence is the right shape for a human
|
|
201
|
+
* reading stderr and the wrong shape for an agent deciding what to do next.
|
|
202
|
+
* The standing case is the tier scope: `meta.scope` names the tiers a list
|
|
203
|
+
* answered from, so a caller that wants the rest can widen the read without
|
|
204
|
+
* parsing prose for tier names. Both machine surfaces read this ONE seam, the
|
|
205
|
+
* way they already read one `notice`, so CLI-JSON and MCP stay byte-equal.
|
|
206
|
+
*
|
|
207
|
+
* Plain and llm ignore it — they carry the same facts in their own register
|
|
208
|
+
* (the notice on stderr, the heading in the condensed form). Return
|
|
209
|
+
* `undefined` (or omit the member) when the read has nothing to add.
|
|
210
|
+
*/
|
|
211
|
+
readonly meta?: (d: T) => Record<string, unknown> | undefined;
|
|
195
212
|
}
|
|
196
213
|
|
|
197
214
|
/** A usage example shown in verb help. */
|
|
@@ -48,7 +48,7 @@ export const PARITY_GAPS: readonly ParityGapEntry[] = [
|
|
|
48
48
|
id: "no-empty-hook-on-free-filter",
|
|
49
49
|
area: "pack list / empty-state",
|
|
50
50
|
description:
|
|
51
|
-
"STILL TRUE OF EMPTINESS, NARROWED ON BAD ARGUMENTS. A zero-row pack list — narrowed by a declared filter, by an unmatched `--search`, or unconditionally empty — is a SUCCESS: `ok:true, data:[]` at exit 0 with a calm notice (stderr in plain mode, `meta.notice` on the machine surfaces). pragma v2 still never raises EMPTY_RESULTS for an empty list, and U5's removal of the enum-filter-only `buildListEmptyError` hook stands. What is no longer true is that an UNMATCHED free-filter value stays successful: a value-free filter now rejects a value its vocabulary does not admit with INVALID_INPUT and the admissible values as `validOptions`, the same courtesy a declared-`values` filter always extended (`packs/sparql/
|
|
51
|
+
"STILL TRUE OF EMPTINESS, NARROWED ON BAD ARGUMENTS. A zero-row pack list — narrowed by a declared filter, by an unmatched `--search`, or unconditionally empty — is a SUCCESS: `ok:true, data:[]` at exit 0 with a calm notice (stderr in plain mode, `meta.notice` on the machine surfaces). pragma v2 still never raises EMPTY_RESULTS for an empty list, and U5's removal of the enum-filter-only `buildListEmptyError` hook stands. What is no longer true is that an UNMATCHED free-filter value stays successful: a value-free filter now rejects a value its vocabulary does not admit with INVALID_INPUT and the admissible values as `validOptions`, the same courtesy a declared-`values` filter always extended (`packs/sparql/filterValues.ts`). The vocabulary is the graph's, not the returned rows' — a filter declares where it lives (`PackFilter.vocabulary`), so a real value that happens to match no row (a category with zero standards, a `ds:ConceptType` no concept uses) is a calm empty list, and only a value the graph does not know is an error.",
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
id: "raw-iri-in-data",
|
|
@@ -84,13 +84,13 @@ export const PARITY_GAPS: readonly ParityGapEntry[] = [
|
|
|
84
84
|
id: "block-lookup-not-tier-scoped",
|
|
85
85
|
area: "block",
|
|
86
86
|
description:
|
|
87
|
-
"CLOSED, by REVERSAL of the entry it used to be. This gap read: `block lookup` resolves by `ds:name` globally, a same-named block at two tiers cannot be disambiguated by plain name, address it by prefixed IRI instead. That was accepted while the answer was merely arbitrary; it stopped being acceptable once measured — 25 live block names are carried by 2–3 blocks, and EVERY one of them was answered with whichever IRI sorted first and no sign the others existed (`block lookup button` returned Launchpad's Button, because `apps_launchpad…` precedes `global…`). 'Address it by IRI' is not a recovery an agent can reach when nothing tells it there is a second block to address. Two things replace it, and neither is a change of arity — one name still answers with one entity, and a glob is still the declared multi-entity form. (1) The discard is no longer SILENT: the resolve is unlimited, the winner is answered with, and the IRIs it outranked ride the verb's `notice` — the same seam a zero-record read uses (stderr in plain mode, `meta.notice` on `--format json` and MCP), because an ambiguous hit is the same failure as an empty one, a result that misrepresents itself. `LookupOutput.ambiguous` carries them in `data` for the machine surfaces, and is ABSENT (not `[]`) when nothing was ambiguous, so every unambiguous read keeps the payload it had. Note the recovery is not reachable any other way: a glob expands over DISTINCT names, so `block lookup 'Butt*'` returned Launchpad's Button and ButtonLink and the global Button appeared nowhere. (2) The order is a declared, derived RANKING (`PackLookup.scopeWeight` in `pragma.conf.ts`, generated by `packs/sparql/buildLookupQuery.ts#rankingClause`): a tier's depth comes from the `/` count in its OWN `ds:name` (`\"Apps/Launchpad\"` → 2, never from the `_` in `ds:apps_launchpad`), and it MULTIPLIES the existing type weight rather than tiebreaking above it, so a whole component in a nested tier still outranks a global SUBcomponent (`TextInput`). Nothing is enumerated, so a tier added upstream is placed without an edit here; an asserted `ds:tierRank` would take precedence over the derived depth and retire the config declaration with no code change. WHAT
|
|
87
|
+
"CLOSED, by REVERSAL of the entry it used to be. This gap read: `block lookup` resolves by `ds:name` globally, a same-named block at two tiers cannot be disambiguated by plain name, address it by prefixed IRI instead. That was accepted while the answer was merely arbitrary; it stopped being acceptable once measured — 25 live block names are carried by 2–3 blocks, and EVERY one of them was answered with whichever IRI sorted first and no sign the others existed (`block lookup button` returned Launchpad's Button, because `apps_launchpad…` precedes `global…`). 'Address it by IRI' is not a recovery an agent can reach when nothing tells it there is a second block to address. Two things replace it, and neither is a change of arity — one name still answers with one entity, and a glob is still the declared multi-entity form. (1) The discard is no longer SILENT: the resolve is unlimited, the winner is answered with, and the IRIs it outranked ride the verb's `notice` — the same seam a zero-record read uses (stderr in plain mode, `meta.notice` on `--format json` and MCP), because an ambiguous hit is the same failure as an empty one, a result that misrepresents itself. `LookupOutput.ambiguous` carries them in `data` for the machine surfaces, and is ABSENT (not `[]`) when nothing was ambiguous, so every unambiguous read keeps the payload it had. Note the recovery is not reachable any other way: a glob expands over DISTINCT names, so `block lookup 'Butt*'` returned Launchpad's Button and ButtonLink and the global Button appeared nowhere. (2) The order is a declared, derived RANKING (`PackLookup.scopeWeight` in `pragma.conf.ts`, generated by `packs/sparql/buildLookupQuery.ts#rankingClause`): a tier's depth comes from the `/` count in its OWN `ds:name` (`\"Apps/Launchpad\"` → 2, never from the `_` in `ds:apps_launchpad`), and it MULTIPLIES the existing type weight rather than tiebreaking above it, so a whole component in a nested tier still outranks a global SUBcomponent (`TextInput`). Nothing is enumerated, so a tier added upstream is placed without an edit here; an asserted `ds:tierRank` would take precedence over the derived depth and retire the config declaration with no code change. WHAT USED TO REMAIN, now closed: `lookup` was not tier-SCOPED — the ranking decided which answer came first, and there was no way to ask for one tier's answer only. The TIER SCOPE is that ask: `block lookup button` answers with the global Button alone under the default scope, `--tier apps_launchpad` answers with Launchpad's, `--tier all` answers with both (the ranking still orders whatever the scope admits, so the two rules compose rather than compete). A name whose every match is out of scope is still ANSWERED, from the tiers it does live in, with one line saying so — a scope decides which of several blocks a name means, never whether the block exists. `config.channel` is still not a filter. Two top-level tiers still cannot be ordered against each other by depth alone, which today leaves `Navigation` (`global` vs `apps`) and `InfoTable` (`global` vs `sites`) decided by the IRI fallback; naming a precedence among the depth-1 tiers is an ontology question, deliberately NOT invented here.",
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
id: "block-list-unfiltered",
|
|
91
91
|
area: "block",
|
|
92
92
|
description:
|
|
93
|
-
"
|
|
93
|
+
"HALF CLOSED, and the half that closed is the tier. This gap read: `block list` is the block story's compiled, UNFILTERED list — every block, experimental ones included, for everyone, until filtering returns in declared form; the old tier-chain inheritance, channel visibility and `--all-tiers` escape were hand-written filtering the pack grammar had no term for, and the ruling removed them rather than grow the grammar to keep them. Filtering has now returned for the TIER, and in declared form: the `block` story declares its tier hierarchy (`PackDefinition.tierScope`) and the kernel compiles a scope into the same query the filters ride, so the list answers from the top-level tiers by default, from a chosen tier plus its ancestors with `--tier <name>`, and from every tier with `--tier all` (which is this entry's old behaviour, on request). `config.tier` is what sets that default — no longer inert, and `config show` reports the scope it puts reads under. An UNTIERED block stays visible under every scope (A2 — `ds:button.icon`), because an entity the scope cannot place is not one it may hide. WHAT REMAINS: `config.channel` still scopes nothing and keeps only its npm dist-tag role for `upgrade`/`info`, so an experimental block is still visible to everyone in whatever tier it sits.",
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
96
|
id: "graph-query-deferred",
|
|
@@ -120,7 +120,7 @@ export const PARITY_GAPS: readonly ParityGapEntry[] = [
|
|
|
120
120
|
id: "config-tier-no-ontology-validation",
|
|
121
121
|
area: "config setters",
|
|
122
122
|
description:
|
|
123
|
-
"
|
|
123
|
+
"`config set tier <anything>` is STORELESS and writes the value verbatim: the old store-backed `validateTier` (which queried the ontology for valid tier paths) is dropped, and the covenant's `config_set` declares no `needsStore` to bring it back. What changed with the tier scope is the CONSEQUENCE of a bad value — it used to yield nothing at all (the key scoped nothing), and it is now refused at the first tiered READ, with an INVALID_INPUT naming the tiers the pack does carry and a `config unset tier` recovery. Validation at the read rather than at the write is deliberate: the write has no store, and the pack a value is valid against can change under a machine that set it months ago. `config/fields.ts`, `kernel/packs/tierScope.ts`.",
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
126
|
id: "doctor-exit-zero-with-failures",
|