@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
|
@@ -19,83 +19,167 @@
|
|
|
19
19
|
* p95, and it lands TIGHTER than 2× its median); its arithmetic is written out
|
|
20
20
|
* in full in BUDGETS.md, as are the measurements and environment for all of
|
|
21
21
|
* them.
|
|
22
|
+
*
|
|
23
|
+
* A CEILING IS ALSO RELATIVE TO WHAT THE SURFACE HAS GROWN INTO. Re-derived
|
|
24
|
+
* again 2026-09-10, because three of the five had drifted red for two causes,
|
|
25
|
+
* both of them real work rather than noise: the eagerly-imported capability
|
|
26
|
+
* barrel grew 37.6 → 68.0 ms as the surface reached 22 capability modules and
|
|
27
|
+
* 42 MCP tools, and the embedded pack grew 8 479 → 49 630 triples. Every
|
|
28
|
+
* constant below names its cause, because a raised ceiling with no cause is
|
|
29
|
+
* just a lower standard. The two in-process ceilings were re-measured and
|
|
30
|
+
* DELIBERATELY LEFT ALONE — they have three orders of magnitude of headroom.
|
|
31
|
+
*
|
|
32
|
+
* THIS PASS IS NOT RUN BY CI — owner ruling 2026-08-30, restated 2026-09-10.
|
|
33
|
+
* Nothing reaches it indirectly either: `test` does not chain it, no Nx target
|
|
34
|
+
* or workflow names it, and the repo has no git hooks. See
|
|
35
|
+
* `vitest.perf.config.ts`, `docs/CI.md` and BUDGETS.md.
|
|
36
|
+
*
|
|
37
|
+
* EVERY CEILING HERE IS LATENCY. The one SIZE budget the package enforces on a
|
|
38
|
+
* verb — a list-shaped answer's payload — deliberately does not live here: it
|
|
39
|
+
* is a property of the payload rather than of wall-clock time, so it needs
|
|
40
|
+
* neither the serial pass nor the spawned binary, — and a budget nothing
|
|
41
|
+
* runs is not a gate. It sits where the payload is built, in
|
|
42
|
+
* `capabilities/listBudget.shipped.exec.test.ts`, as the MCP resource
|
|
43
|
+
* listing's own ceiling does in `capabilities/resources/resources.test.ts`.
|
|
22
44
|
*/
|
|
23
45
|
|
|
24
46
|
/**
|
|
25
|
-
* `pragma --help` ceiling (ms). Designed 50
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
47
|
+
* `pragma --help` ceiling (ms). Designed 50 — **not met, and recorded as such**
|
|
48
|
+
* for the same reason as {@link BUDGET_COMPLETE_MS}: node's own start is most
|
|
49
|
+
* of that number before pragma runs a line. The rule is 2× the measured median.
|
|
50
|
+
*
|
|
51
|
+
* Up from 130, re-derived 2026-09-10. 130 *was* the rule's own number, back
|
|
52
|
+
* when this path's work was 35.3 ms net of process start; it is not any more.
|
|
53
|
+
* The eagerly-imported capability barrel (`capabilities/index`) now costs
|
|
54
|
+
* **68.0 ms** to import where the recovery A/B measured 37.6 — the surface
|
|
55
|
+
* grew to 22 capability modules and 42 MCP tools, and each one puts its spec +
|
|
56
|
+
* formatter modules on this path. `lazy.test.ts` is green, so this is SURFACE
|
|
57
|
+
* GROWTH and not a lazy-boundary leak: summon-core's projection, its Commander
|
|
58
|
+
* adapter, `commander`, zod and oxigraph are all still absent from the
|
|
59
|
+
* fast-path graph. The barrel import is now essentially the whole of the cost
|
|
60
|
+
* (68.0 of 69.1 ms), which is where any future cut has to come from.
|
|
61
|
+
*
|
|
62
|
+
* MEASURED (2026-09-10; five load-gated repetitions × 40 kept samples,
|
|
63
|
+
* interleaved with an in-run `--version` control of 34.8 ms): median
|
|
64
|
+
* **103.3 ms**, p95 134.0 ms, work **69.1 ms** net of the control.
|
|
65
|
+
* `2 × 103.3 = 206.6`, so **210**. The reference-box projection cross-checks
|
|
66
|
+
* LOOSER, not tighter — `45.5 + 69.1 = 114.6` median → 2× = 229 — so 210 is
|
|
67
|
+
* the tight side of the rule, exactly as 130 was.
|
|
68
|
+
*
|
|
69
|
+
* What this can and cannot separate: at 210 against a 103 ms median it catches
|
|
70
|
+
* a doubling of the path and no longer catches the ~30 ms the barrel just
|
|
71
|
+
* gained. It is asserted on median AND raw p95 over 12 kept samples, where a
|
|
72
|
+
* nearest-rank p95 is effectively the maximum — the worst of 12 protocol
|
|
73
|
+
* replays here was 200.2 ms, so it holds but without much room. BUDGETS.md
|
|
74
|
+
* carries the table, the environment, and that flake characterisation.
|
|
42
75
|
*/
|
|
43
|
-
export const BUDGET_HELP_MS =
|
|
76
|
+
export const BUDGET_HELP_MS = 210;
|
|
44
77
|
|
|
45
78
|
/**
|
|
46
79
|
* `pragma __complete …` ceiling (ms). Designed 50 — **not met, and recorded as
|
|
47
80
|
* such**: the shipped entry cannot reach it, because node's own start is most
|
|
48
81
|
* of that number before pragma runs a line.
|
|
49
82
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
83
|
+
* Up from 150, re-derived 2026-09-10, and for the same single cause as
|
|
84
|
+
* {@link BUDGET_HELP_MS} — completion pays for the same grown capability
|
|
85
|
+
* barrel. It still sheds Commander (nothing on the `__complete` closure
|
|
86
|
+
* imports it), and it still costs ~12 ms more than `--help` because it walks
|
|
87
|
+
* the grammar and, in the name-source case, the skills directory.
|
|
88
|
+
*
|
|
89
|
+
* MEASURED in the same runs, on the SLOWER of the two cases
|
|
90
|
+
* (`__complete skill lookup do`): median **115.7 ms**, 10%-trimmed mean
|
|
91
|
+
* 118.6 ms, p95 145.0 ms, work 81.4 ms net of the control. The noun case
|
|
92
|
+
* (`__complete config`) is median 116.4 / trimmed mean 118.5 ms — the two are
|
|
93
|
+
* now within a millisecond of each other. `2 × 115.7 = 231.4`, so **240**;
|
|
94
|
+
* 2× the trimmed mean gives 237.2, the same 240. The reference-box projection
|
|
95
|
+
* again cross-checks looser (`45.5 + 81.4 = 126.9` → 2× = 254), so 240 is the
|
|
96
|
+
* tight side of the rule.
|
|
97
|
+
*
|
|
98
|
+
* The ceiling is enforced on the trimmed mean, with p95 kept as a SOFT signal
|
|
99
|
+
* at 1.5× — the statistic change made in the p95-stabilization work, which is
|
|
100
|
+
* unaffected by this re-derivation. Worst trimmed mean across 8 protocol
|
|
101
|
+
* replays here: 136.7 ms, so 240 is not a marginal ceiling.
|
|
102
|
+
*
|
|
103
|
+
* Completion is typed interactively, so this stays the budget most worth
|
|
104
|
+
* defending, and it has now lost 90 ms of standard across two re-derivations
|
|
105
|
+
* with nothing bought back. See BUDGETS.md.
|
|
67
106
|
*/
|
|
68
|
-
export const BUDGET_COMPLETE_MS =
|
|
107
|
+
export const BUDGET_COMPLETE_MS = 240;
|
|
69
108
|
|
|
70
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Warm project-config (`pragma.config.ts`) load ceiling (ms). Cache hit is
|
|
111
|
+
* sub-ms.
|
|
112
|
+
*
|
|
113
|
+
* RE-MEASURED 2026-09-10 and deliberately UNCHANGED: 40 warm
|
|
114
|
+
* `evaluateProjectConfig` calls after priming the content-hash cache give a
|
|
115
|
+
* median of **0.011 ms** and a p95 of 0.034 ms — roughly 900× of headroom. The
|
|
116
|
+
* 2×-median rule would put this at 0.02 ms, which would assert nothing but
|
|
117
|
+
* scheduler jitter. 10 ms is a gross-regression guard (a cache that stopped
|
|
118
|
+
* hitting, an import that stopped being cached) and that is all it is for.
|
|
119
|
+
*/
|
|
71
120
|
export const BUDGET_PROJECT_CONFIG_MS = 10;
|
|
72
121
|
|
|
73
122
|
/**
|
|
74
123
|
* Warm store-backed verb ceiling (ms) — a store boot from the cached n-quads
|
|
75
124
|
* dump plus a query, through the shipped entry.
|
|
76
125
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* the
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
126
|
+
* Up from 500, re-derived 2026-09-10. The cause is the pack, not the code: the
|
|
127
|
+
* embed went from the 8 479-triple graph the 500 was derived against to
|
|
128
|
+
* **49 630 triples / 4 167 entities** (5.85× the triples). Boot still loads
|
|
129
|
+
* the n-quads dump rather than parsing TTL and still rebuilds the schema from
|
|
130
|
+
* the extraction artifact rather than running a live 7-pass compile, which is
|
|
131
|
+
* why the cost grew 1.75× and not 5.85×.
|
|
132
|
+
*
|
|
133
|
+
* The arithmetic, on the same route that produced 500, with one new input —
|
|
134
|
+
* the within-box growth multiplier. This box's netted real store work was
|
|
135
|
+
* +285.4 ms against the 8 479-triple pack (five repetitions, recorded in
|
|
136
|
+
* BUDGETS.md) and is +499.9 ms against this one (four load-gated repetitions
|
|
137
|
+
* × 40 kept samples, 2026-09-10), so:
|
|
138
|
+
*
|
|
139
|
+
* ```
|
|
140
|
+
* within-box growth multiplier = 499.9 / 285.4 = 1.752
|
|
141
|
+
* reference store work (old pack) = 101.5 × 2.83 = 287.2 ms
|
|
142
|
+
* reference store work (new pack) = 287.2 × 1.752 = 503.2 ms
|
|
143
|
+
* projected reference median = 45.5 + 503.2 = 548.7 ms
|
|
144
|
+
* reference p95/median, this command = 176 / 147 = 1.197
|
|
145
|
+
* projected p95 = 548.7 × 1.197 = 656.9 ms
|
|
146
|
+
* ceiling = ceil(656.9 × 1.25 / 50) × 50 = 850 ms
|
|
147
|
+
* ```
|
|
148
|
+
*
|
|
149
|
+
* The local-only route agrees to within 6%: this box's own measured p95 is
|
|
150
|
+
* 608.9 ms and `ceil(608.9 × 1.25 / 50) × 50` = 800. The reference-box number
|
|
151
|
+
* is taken, because that is the route the standing ceiling came from.
|
|
152
|
+
*
|
|
153
|
+
* 850 is still **1.55×** the projected median, i.e. tighter than the
|
|
154
|
+
* 2×-of-median rule the fast paths use. But it is now **2.83×** the designed
|
|
155
|
+
* `<300ms` target, where 500 was 1.67× — and that gap is the honest headline:
|
|
156
|
+
* the designed target was set against a 23-triple sample and then held against
|
|
157
|
+
* 8 479, and a 49 630-triple pack whose store work alone is ~500 ms cannot
|
|
158
|
+
* reach 300 ms at all. `warmStoreVerb: "<300ms"` stays in the surface covenant
|
|
159
|
+
* as the aspiration, and it is now an aspiration that needs a different boot
|
|
160
|
+
* strategy rather than a tuning pass. Every input and every step is in
|
|
161
|
+
* BUDGETS.md.
|
|
162
|
+
*
|
|
163
|
+
* What this can and cannot separate: asserted on median AND p95 over 9 kept
|
|
164
|
+
* samples, where a nearest-rank p95 IS the maximum — kept deliberately,
|
|
165
|
+
* because this excess is real work rather than contention noise, and hiding
|
|
166
|
+
* real cost behind a robust statistic would make the budget lie.
|
|
90
167
|
*/
|
|
91
|
-
export const BUDGET_WARM_STORE_MS =
|
|
168
|
+
export const BUDGET_WARM_STORE_MS = 850;
|
|
92
169
|
|
|
93
170
|
/**
|
|
94
171
|
* Warm in-process MCP tool-call ceiling (ms) — PR7 graduates this from seeded to
|
|
95
172
|
* ENFORCED. Measured over a warm, storeless tool (`capabilities`): pure envelope
|
|
96
173
|
* + dispatch, no store boot, no network, so it isolates the per-call overhead of
|
|
97
|
-
* the grown
|
|
98
|
-
*
|
|
99
|
-
*
|
|
174
|
+
* the grown tool catalog. `info` is deliberately NOT used — its network
|
|
175
|
+
* update-check makes it ~55 ms (see BUDGETS.md).
|
|
176
|
+
*
|
|
177
|
+
* RE-MEASURED 2026-09-10 and deliberately UNCHANGED. The catalog has grown
|
|
178
|
+
* 38 → 42 tools and the warm call grew with it — p95 **0.732 ms**, trimmed
|
|
179
|
+
* mean 0.530 ms over 40 calls, against ~0.4 ms p95 at 38 tools — but that is
|
|
180
|
+
* still ~137× of headroom under 100 ms. This is a gross-regression guard (a
|
|
181
|
+
* per-call store boot, a network read, a catalog rebuilt per call), and 100 ms
|
|
182
|
+
* is what lets it be one without flaking. Enforced on the trimmed mean with
|
|
183
|
+
* p95 as a second check (both ≤ 100).
|
|
100
184
|
*/
|
|
101
185
|
export const BUDGET_MCP_P95_WARM_MS = 100;
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Apply declared pack filters to resolved list rows.
|
|
3
|
-
*
|
|
4
|
-
* Filters are row predicates on projected SELECT variables — the author query is
|
|
5
|
-
* never modified, so user input cannot inject SPARQL and the query's ordering is
|
|
6
|
-
* preserved. With declared `values` the input is canonicalized against the set
|
|
7
|
-
* (NFC, case-insensitive) and rows are matched case-insensitively; without them
|
|
8
|
-
* the input is a free term matched the same way. A row lacking the variable never
|
|
9
|
-
* matches. Several filters combine conjunctively; several values for one
|
|
10
|
-
* filter (a repeated flag) combine as a union.
|
|
11
|
-
*
|
|
12
|
-
* A filter declaring `match: "set"` reads its cell as a space-separated SET and
|
|
13
|
-
* matches any member — the shape a query produces for a dimension a row belongs
|
|
14
|
-
* to several values of at once (a category and its ancestors, say). The
|
|
15
|
-
* comparison is otherwise identical, so exactly one code path decides what a
|
|
16
|
-
* match is.
|
|
17
|
-
*
|
|
18
|
-
* A value-free filter still REJECTS a value the data does not carry, with the
|
|
19
|
-
* admissible values as `validOptions` — the same courtesy a declared-`values`
|
|
20
|
-
* filter has always extended, without copying the vocabulary into the consumer.
|
|
21
|
-
* The vocabulary is read from the graph, because the graph IS the vocabulary; a
|
|
22
|
-
* story that hard-coded the slugs would go stale the first time the data grew a
|
|
23
|
-
* category. Before this, a typo'd `--category` returned
|
|
24
|
-
* `{"ok":true,"data":[],"meta":{}}` — indistinguishable, over MCP, from a
|
|
25
|
-
* genuinely empty category.
|
|
26
|
-
*
|
|
27
|
-
* WHICH part of the graph is the vocabulary matters, and it is NOT the rows.
|
|
28
|
-
* Rows are a population: a value can be perfectly real while no row carries it —
|
|
29
|
-
* `standard categories` lists a category with count 0, `ds:ConceptType` declares
|
|
30
|
-
* "Decision guide" before any concept uses it. Validating against rows called
|
|
31
|
-
* those `INVALID_INPUT`, when the documented answer is a calm empty list. So a
|
|
32
|
-
* filter DECLARES where its vocabulary lives ({@link PackFilter.vocabulary},
|
|
33
|
-
* resolved by the caller and handed in here), and the observed rows are the
|
|
34
|
-
* fallback for a filter that declares none — the only evidence there is, and
|
|
35
|
-
* knowingly narrower than the truth.
|
|
36
|
-
*/
|
|
37
|
-
import { PragmaError } from "../../error/index.js";
|
|
38
|
-
/**
|
|
39
|
-
* @param rows - Rows produced by the pack's list query.
|
|
40
|
-
* @param filters - Declared filters (absent means no filtering).
|
|
41
|
-
* @param params - Story parameters as provided by the surface.
|
|
42
|
-
* @param vocabularies - Authoritative values per filter param, resolved from
|
|
43
|
-
* each filter's declared `vocabulary` (see {@link FilterVocabularies}).
|
|
44
|
-
* @returns Rows matching every provided filter.
|
|
45
|
-
* @throws PragmaError INVALID_INPUT when a value is not in a filter's declared
|
|
46
|
-
* set, or when a value-free filter receives a non-string value.
|
|
47
|
-
*/
|
|
48
|
-
export function applyPackFilters(rows, filters, params, vocabularies) {
|
|
49
|
-
let result = rows;
|
|
50
|
-
for (const filter of filters ?? []) {
|
|
51
|
-
const provided = params[filter.param];
|
|
52
|
-
if (provided === undefined)
|
|
53
|
-
continue;
|
|
54
|
-
// A repeated CLI flag accumulates into an array; MCP args stay scalar.
|
|
55
|
-
// Several values for ONE filter are a union (a row matches any of them);
|
|
56
|
-
// several filters still combine conjunctively.
|
|
57
|
-
const occurrences = Array.isArray(provided) ? provided : [provided];
|
|
58
|
-
if (occurrences.length === 0)
|
|
59
|
-
continue;
|
|
60
|
-
const values = filter.values;
|
|
61
|
-
const terms = occurrences.map((occurrence) => values === undefined
|
|
62
|
-
? requireStringValue(occurrence, filter).toLowerCase()
|
|
63
|
-
: canonicalizeFilterValue(occurrence, filter, values).toLowerCase());
|
|
64
|
-
// The fallback vocabulary is read from the UNFILTERED rows: a value that
|
|
65
|
-
// exists but is excluded by a conjunction is a legitimately empty answer,
|
|
66
|
-
// not a bad argument.
|
|
67
|
-
if (values === undefined)
|
|
68
|
-
rejectUnknownValue(vocabularies?.get(filter.param) ?? observedValues(rows, filter), filter, occurrences, terms);
|
|
69
|
-
result = result.filter((row) => cellValues(row, filter).some((value) => terms.includes(value.toLowerCase())));
|
|
70
|
-
}
|
|
71
|
-
return result;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* The values the rows themselves carry — the fallback vocabulary for a filter
|
|
75
|
-
* that declares no authoritative one, in the data's own DISPLAY spelling.
|
|
76
|
-
*/
|
|
77
|
-
function observedValues(rows, filter) {
|
|
78
|
-
const observed = new Map();
|
|
79
|
-
for (const row of rows) {
|
|
80
|
-
for (const value of cellValues(row, filter)) {
|
|
81
|
-
const key = value.toLowerCase();
|
|
82
|
-
if (!observed.has(key))
|
|
83
|
-
observed.set(key, value);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return [...observed.values()];
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Reject a value-free filter value the vocabulary does not admit, naming the
|
|
90
|
-
* ones it does.
|
|
91
|
-
*
|
|
92
|
-
* Silent when the vocabulary is EMPTY: an unbuilt or genuinely-empty store is
|
|
93
|
-
* not the caller's mistake, and the list's own `emptyRecovery` is the right
|
|
94
|
-
* voice for it.
|
|
95
|
-
*
|
|
96
|
-
* A value the vocabulary admits is never rejected here even when no row carries
|
|
97
|
-
* it — a real slug with zero standards is a calm empty list, which is what the
|
|
98
|
-
* tool descriptions document and what `standard categories` reporting count 0
|
|
99
|
-
* promises.
|
|
100
|
-
*
|
|
101
|
-
* @throws PragmaError INVALID_INPUT when a term matches none of the admissible
|
|
102
|
-
* values, carrying them as `validOptions`.
|
|
103
|
-
*/
|
|
104
|
-
function rejectUnknownValue(vocabulary, filter, occurrences, terms) {
|
|
105
|
-
const admissible = new Map();
|
|
106
|
-
for (const value of vocabulary) {
|
|
107
|
-
const key = value.normalize("NFC").toLowerCase();
|
|
108
|
-
if (!admissible.has(key))
|
|
109
|
-
admissible.set(key, value.normalize("NFC"));
|
|
110
|
-
}
|
|
111
|
-
if (admissible.size === 0)
|
|
112
|
-
return;
|
|
113
|
-
const index = terms.findIndex((term) => !admissible.has(term));
|
|
114
|
-
if (index === -1)
|
|
115
|
-
return;
|
|
116
|
-
const validOptions = [...admissible.values()].sort();
|
|
117
|
-
throw PragmaError.invalidInput(filter.param, String(occurrences[index]), {
|
|
118
|
-
validOptions,
|
|
119
|
-
recovery: {
|
|
120
|
-
message: `Values allowed for --${filter.param}: ${validOptions.join(", ")}.`,
|
|
121
|
-
},
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* The values a row offers for one filter, in their DISPLAY spelling (NFC).
|
|
126
|
-
*
|
|
127
|
-
* One value for an ordinary filter; every space-separated member for a `"set"`
|
|
128
|
-
* one. A row lacking the variable offers none, so it never matches. Kept in the
|
|
129
|
-
* data's own casing because these are also what an INVALID_INPUT lists back as
|
|
130
|
-
* `validOptions` — lowercasing here would hand the caller a value the graph does
|
|
131
|
-
* not spell that way. Comparison lowercases at the point of comparison instead.
|
|
132
|
-
*/
|
|
133
|
-
function cellValues(row, filter) {
|
|
134
|
-
const cell = row[filter.variable];
|
|
135
|
-
if (cell === undefined)
|
|
136
|
-
return [];
|
|
137
|
-
const normalized = cell.normalize("NFC");
|
|
138
|
-
if (filter.match !== "set") {
|
|
139
|
-
return normalized === "" ? [] : [normalized];
|
|
140
|
-
}
|
|
141
|
-
return normalized.split(/\s+/).filter((value) => value !== "");
|
|
142
|
-
}
|
|
143
|
-
/** @throws PragmaError INVALID_INPUT when a value-free filter value is not a string. */
|
|
144
|
-
function requireStringValue(provided, filter) {
|
|
145
|
-
if (typeof provided !== "string") {
|
|
146
|
-
throw PragmaError.invalidInput(filter.param, String(provided), {
|
|
147
|
-
recovery: { message: `Provide a string value for --${filter.param}.` },
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return provided.trim().normalize("NFC");
|
|
151
|
-
}
|
|
152
|
-
/** @throws PragmaError INVALID_INPUT when the value is not in the declared set. */
|
|
153
|
-
function canonicalizeFilterValue(provided, filter, values) {
|
|
154
|
-
if (typeof provided === "string") {
|
|
155
|
-
const normalized = provided.trim().normalize("NFC");
|
|
156
|
-
const match = values.find((value) => value.normalize("NFC").toLowerCase() === normalized.toLowerCase());
|
|
157
|
-
if (match !== undefined)
|
|
158
|
-
return match.normalize("NFC");
|
|
159
|
-
}
|
|
160
|
-
throw PragmaError.invalidInput(filter.param, String(provided), {
|
|
161
|
-
validOptions: [...values],
|
|
162
|
-
recovery: {
|
|
163
|
-
message: `Allowed values for --${filter.param}: ${values.join(", ")}.`,
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
//# sourceMappingURL=applyFilters.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"applyFilters.js","sourceRoot":"","sources":["../../../../../src/kernel/packs/sparql/applyFilters.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Apply a pack list's free-text search to resolved rows.
|
|
3
|
-
*
|
|
4
|
-
* Like filters, search runs AFTER the author query, so user input never touches
|
|
5
|
-
* the query text (injection-safe) and the author's ORDER BY is preserved. The
|
|
6
|
-
* term is NFC-normalized and matched case-insensitively; a row passes when ANY
|
|
7
|
-
* declared variable contains it as a substring. A non-string or empty term is a
|
|
8
|
-
* no-op.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* @param rows - Rows produced by the pack's list query (post-filter).
|
|
12
|
-
* @param search - The declared search (absent means no searching).
|
|
13
|
-
* @param params - Story parameters as provided by the surface.
|
|
14
|
-
* @returns Rows containing the term in at least one searched variable.
|
|
15
|
-
*/
|
|
16
|
-
export function applyPackSearch(rows, search, params) {
|
|
17
|
-
const provided = params.search;
|
|
18
|
-
if (search === undefined || typeof provided !== "string")
|
|
19
|
-
return rows;
|
|
20
|
-
const term = provided.trim().normalize("NFC").toLowerCase();
|
|
21
|
-
if (term === "")
|
|
22
|
-
return rows;
|
|
23
|
-
return rows.filter((row) => search.variables.some((variable) => row[variable]?.normalize("NFC").toLowerCase().includes(term)));
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=applySearch.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"applySearch.js","sourceRoot":"","sources":["../../../../../src/kernel/packs/sparql/applySearch.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAe,EACf,MAA8B,EAC9B,MAA+B;IAE/B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;IAC/B,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACtE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CACzB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CACjC,GAAG,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAC7D,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Apply declared pack filters to resolved list rows.
|
|
3
|
-
*
|
|
4
|
-
* Filters are row predicates on projected SELECT variables — the author query is
|
|
5
|
-
* never modified, so user input cannot inject SPARQL and the query's ordering is
|
|
6
|
-
* preserved. With declared `values` the input is canonicalized against the set
|
|
7
|
-
* (NFC, case-insensitive) and rows are matched case-insensitively; without them
|
|
8
|
-
* the input is a free term matched the same way. A row lacking the variable never
|
|
9
|
-
* matches. Several filters combine conjunctively; several values for one
|
|
10
|
-
* filter (a repeated flag) combine as a union.
|
|
11
|
-
*
|
|
12
|
-
* A filter declaring `match: "set"` reads its cell as a space-separated SET and
|
|
13
|
-
* matches any member — the shape a query produces for a dimension a row belongs
|
|
14
|
-
* to several values of at once (a category and its ancestors, say). The
|
|
15
|
-
* comparison is otherwise identical, so exactly one code path decides what a
|
|
16
|
-
* match is.
|
|
17
|
-
*
|
|
18
|
-
* A value-free filter still REJECTS a value the data does not carry, with the
|
|
19
|
-
* admissible values as `validOptions` — the same courtesy a declared-`values`
|
|
20
|
-
* filter has always extended, without copying the vocabulary into the consumer.
|
|
21
|
-
* The vocabulary is read from the graph, because the graph IS the vocabulary; a
|
|
22
|
-
* story that hard-coded the slugs would go stale the first time the data grew a
|
|
23
|
-
* category. Before this, a typo'd `--category` returned
|
|
24
|
-
* `{"ok":true,"data":[],"meta":{}}` — indistinguishable, over MCP, from a
|
|
25
|
-
* genuinely empty category.
|
|
26
|
-
*
|
|
27
|
-
* WHICH part of the graph is the vocabulary matters, and it is NOT the rows.
|
|
28
|
-
* Rows are a population: a value can be perfectly real while no row carries it —
|
|
29
|
-
* `standard categories` lists a category with count 0, `ds:ConceptType` declares
|
|
30
|
-
* "Decision guide" before any concept uses it. Validating against rows called
|
|
31
|
-
* those `INVALID_INPUT`, when the documented answer is a calm empty list. So a
|
|
32
|
-
* filter DECLARES where its vocabulary lives ({@link PackFilter.vocabulary},
|
|
33
|
-
* resolved by the caller and handed in here), and the observed rows are the
|
|
34
|
-
* fallback for a filter that declares none — the only evidence there is, and
|
|
35
|
-
* knowingly narrower than the truth.
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
import { PragmaError } from "../../error/index.js";
|
|
39
|
-
import type { PackFilter, PackRow } from "../types.js";
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Authoritative values per filter `param`, read from each filter's declared
|
|
43
|
-
* `vocabulary`. A param absent from the map falls back to the observed rows.
|
|
44
|
-
*/
|
|
45
|
-
export type FilterVocabularies = ReadonlyMap<string, readonly string[]>;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @param rows - Rows produced by the pack's list query.
|
|
49
|
-
* @param filters - Declared filters (absent means no filtering).
|
|
50
|
-
* @param params - Story parameters as provided by the surface.
|
|
51
|
-
* @param vocabularies - Authoritative values per filter param, resolved from
|
|
52
|
-
* each filter's declared `vocabulary` (see {@link FilterVocabularies}).
|
|
53
|
-
* @returns Rows matching every provided filter.
|
|
54
|
-
* @throws PragmaError INVALID_INPUT when a value is not in a filter's declared
|
|
55
|
-
* set, or when a value-free filter receives a non-string value.
|
|
56
|
-
*/
|
|
57
|
-
export function applyPackFilters(
|
|
58
|
-
rows: PackRow[],
|
|
59
|
-
filters: readonly PackFilter[] | undefined,
|
|
60
|
-
params: Record<string, unknown>,
|
|
61
|
-
vocabularies?: FilterVocabularies,
|
|
62
|
-
): PackRow[] {
|
|
63
|
-
let result = rows;
|
|
64
|
-
for (const filter of filters ?? []) {
|
|
65
|
-
const provided = params[filter.param];
|
|
66
|
-
if (provided === undefined) continue;
|
|
67
|
-
// A repeated CLI flag accumulates into an array; MCP args stay scalar.
|
|
68
|
-
// Several values for ONE filter are a union (a row matches any of them);
|
|
69
|
-
// several filters still combine conjunctively.
|
|
70
|
-
const occurrences = Array.isArray(provided) ? provided : [provided];
|
|
71
|
-
if (occurrences.length === 0) continue;
|
|
72
|
-
const values = filter.values;
|
|
73
|
-
const terms = occurrences.map((occurrence) =>
|
|
74
|
-
values === undefined
|
|
75
|
-
? requireStringValue(occurrence, filter).toLowerCase()
|
|
76
|
-
: canonicalizeFilterValue(occurrence, filter, values).toLowerCase(),
|
|
77
|
-
);
|
|
78
|
-
// The fallback vocabulary is read from the UNFILTERED rows: a value that
|
|
79
|
-
// exists but is excluded by a conjunction is a legitimately empty answer,
|
|
80
|
-
// not a bad argument.
|
|
81
|
-
if (values === undefined)
|
|
82
|
-
rejectUnknownValue(
|
|
83
|
-
vocabularies?.get(filter.param) ?? observedValues(rows, filter),
|
|
84
|
-
filter,
|
|
85
|
-
occurrences,
|
|
86
|
-
terms,
|
|
87
|
-
);
|
|
88
|
-
result = result.filter((row) =>
|
|
89
|
-
cellValues(row, filter).some((value) =>
|
|
90
|
-
terms.includes(value.toLowerCase()),
|
|
91
|
-
),
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
return result;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* The values the rows themselves carry — the fallback vocabulary for a filter
|
|
99
|
-
* that declares no authoritative one, in the data's own DISPLAY spelling.
|
|
100
|
-
*/
|
|
101
|
-
function observedValues(
|
|
102
|
-
rows: readonly PackRow[],
|
|
103
|
-
filter: PackFilter,
|
|
104
|
-
): string[] {
|
|
105
|
-
const observed = new Map<string, string>();
|
|
106
|
-
for (const row of rows) {
|
|
107
|
-
for (const value of cellValues(row, filter)) {
|
|
108
|
-
const key = value.toLowerCase();
|
|
109
|
-
if (!observed.has(key)) observed.set(key, value);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return [...observed.values()];
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Reject a value-free filter value the vocabulary does not admit, naming the
|
|
117
|
-
* ones it does.
|
|
118
|
-
*
|
|
119
|
-
* Silent when the vocabulary is EMPTY: an unbuilt or genuinely-empty store is
|
|
120
|
-
* not the caller's mistake, and the list's own `emptyRecovery` is the right
|
|
121
|
-
* voice for it.
|
|
122
|
-
*
|
|
123
|
-
* A value the vocabulary admits is never rejected here even when no row carries
|
|
124
|
-
* it — a real slug with zero standards is a calm empty list, which is what the
|
|
125
|
-
* tool descriptions document and what `standard categories` reporting count 0
|
|
126
|
-
* promises.
|
|
127
|
-
*
|
|
128
|
-
* @throws PragmaError INVALID_INPUT when a term matches none of the admissible
|
|
129
|
-
* values, carrying them as `validOptions`.
|
|
130
|
-
*/
|
|
131
|
-
function rejectUnknownValue(
|
|
132
|
-
vocabulary: readonly string[],
|
|
133
|
-
filter: PackFilter,
|
|
134
|
-
occurrences: readonly unknown[],
|
|
135
|
-
terms: readonly string[],
|
|
136
|
-
): void {
|
|
137
|
-
const admissible = new Map<string, string>();
|
|
138
|
-
for (const value of vocabulary) {
|
|
139
|
-
const key = value.normalize("NFC").toLowerCase();
|
|
140
|
-
if (!admissible.has(key)) admissible.set(key, value.normalize("NFC"));
|
|
141
|
-
}
|
|
142
|
-
if (admissible.size === 0) return;
|
|
143
|
-
const index = terms.findIndex((term) => !admissible.has(term));
|
|
144
|
-
if (index === -1) return;
|
|
145
|
-
const validOptions = [...admissible.values()].sort();
|
|
146
|
-
throw PragmaError.invalidInput(filter.param, String(occurrences[index]), {
|
|
147
|
-
validOptions,
|
|
148
|
-
recovery: {
|
|
149
|
-
message: `Values allowed for --${filter.param}: ${validOptions.join(", ")}.`,
|
|
150
|
-
},
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* The values a row offers for one filter, in their DISPLAY spelling (NFC).
|
|
156
|
-
*
|
|
157
|
-
* One value for an ordinary filter; every space-separated member for a `"set"`
|
|
158
|
-
* one. A row lacking the variable offers none, so it never matches. Kept in the
|
|
159
|
-
* data's own casing because these are also what an INVALID_INPUT lists back as
|
|
160
|
-
* `validOptions` — lowercasing here would hand the caller a value the graph does
|
|
161
|
-
* not spell that way. Comparison lowercases at the point of comparison instead.
|
|
162
|
-
*/
|
|
163
|
-
function cellValues(row: PackRow, filter: PackFilter): string[] {
|
|
164
|
-
const cell = row[filter.variable];
|
|
165
|
-
if (cell === undefined) return [];
|
|
166
|
-
const normalized = cell.normalize("NFC");
|
|
167
|
-
if (filter.match !== "set") {
|
|
168
|
-
return normalized === "" ? [] : [normalized];
|
|
169
|
-
}
|
|
170
|
-
return normalized.split(/\s+/).filter((value) => value !== "");
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/** @throws PragmaError INVALID_INPUT when a value-free filter value is not a string. */
|
|
174
|
-
function requireStringValue(provided: unknown, filter: PackFilter): string {
|
|
175
|
-
if (typeof provided !== "string") {
|
|
176
|
-
throw PragmaError.invalidInput(filter.param, String(provided), {
|
|
177
|
-
recovery: { message: `Provide a string value for --${filter.param}.` },
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return provided.trim().normalize("NFC");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/** @throws PragmaError INVALID_INPUT when the value is not in the declared set. */
|
|
184
|
-
function canonicalizeFilterValue(
|
|
185
|
-
provided: unknown,
|
|
186
|
-
filter: PackFilter,
|
|
187
|
-
values: readonly string[],
|
|
188
|
-
): string {
|
|
189
|
-
if (typeof provided === "string") {
|
|
190
|
-
const normalized = provided.trim().normalize("NFC");
|
|
191
|
-
const match = values.find(
|
|
192
|
-
(value) =>
|
|
193
|
-
value.normalize("NFC").toLowerCase() === normalized.toLowerCase(),
|
|
194
|
-
);
|
|
195
|
-
if (match !== undefined) return match.normalize("NFC");
|
|
196
|
-
}
|
|
197
|
-
throw PragmaError.invalidInput(filter.param, String(provided), {
|
|
198
|
-
validOptions: [...values],
|
|
199
|
-
recovery: {
|
|
200
|
-
message: `Allowed values for --${filter.param}: ${values.join(", ")}.`,
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
}
|