@canonical/pragma-cli 0.38.0 → 0.42.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 -3
- package/dist/package.json +16 -16
- package/dist/pragma.conf.js +273 -176
- package/dist/pragma.conf.js.map +1 -1
- package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
- package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
- package/dist/src/capabilities/capabilities/catalog.js +47 -51
- package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
- package/dist/src/capabilities/capabilities/index.js +2 -0
- package/dist/src/capabilities/capabilities/index.js.map +1 -1
- package/dist/src/capabilities/capabilities/types.js +5 -5
- package/dist/src/capabilities/colophon/colophon.render.js +7 -4
- package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
- package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
- package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
- package/dist/src/capabilities/colophon/markdownTerminal.js.map +1 -1
- package/dist/src/capabilities/config/get.verb.js +2 -0
- package/dist/src/capabilities/config/get.verb.js.map +1 -1
- package/dist/src/capabilities/config/runField.js +2 -2
- package/dist/src/capabilities/config/runField.js.map +1 -1
- package/dist/src/capabilities/config/set.verb.js +2 -0
- package/dist/src/capabilities/config/set.verb.js.map +1 -1
- package/dist/src/capabilities/config/show.render.js.map +1 -1
- package/dist/src/capabilities/config/show.verb.js +1 -0
- package/dist/src/capabilities/config/show.verb.js.map +1 -1
- package/dist/src/capabilities/config/unset.verb.js +2 -0
- package/dist/src/capabilities/config/unset.verb.js.map +1 -1
- package/dist/src/capabilities/create/constants.js +8 -5
- package/dist/src/capabilities/create/constants.js.map +1 -1
- package/dist/src/capabilities/create/create.render.js.map +1 -1
- package/dist/src/capabilities/create/create.verb.js +36 -7
- package/dist/src/capabilities/create/create.verb.js.map +1 -1
- package/dist/src/capabilities/create/createSurface.generated.js +22 -0
- package/dist/src/capabilities/create/createSurface.generated.js.map +1 -1
- package/dist/src/capabilities/create/index.js +1 -1
- package/dist/src/capabilities/create/pickGenerator.js +1 -0
- package/dist/src/capabilities/create/pickGenerator.js.map +1 -1
- package/dist/src/capabilities/distribution.js +3 -1
- package/dist/src/capabilities/distribution.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
- package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
- package/dist/src/capabilities/doctor/checks/targetHealth.js.map +1 -1
- package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
- package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
- package/dist/src/capabilities/graph/connect.verb.js +2 -0
- package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.render.js +7 -1
- package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
- package/dist/src/capabilities/graph/inspect.verb.js +2 -0
- package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
- package/dist/src/capabilities/graph/query.render.js +37 -5
- package/dist/src/capabilities/graph/query.render.js.map +1 -1
- package/dist/src/capabilities/graph/query.verb.js +2 -0
- package/dist/src/capabilities/graph/query.verb.js.map +1 -1
- package/dist/src/capabilities/graph/queryText.js +28 -0
- package/dist/src/capabilities/graph/queryText.js.map +1 -1
- package/dist/src/capabilities/graph/runConnect.js +3 -3
- package/dist/src/capabilities/graph/runConnect.js.map +1 -1
- package/dist/src/capabilities/graph/runQuery.js +8 -6
- package/dist/src/capabilities/graph/runQuery.js.map +1 -1
- package/dist/src/capabilities/info/info.verb.js +2 -0
- package/dist/src/capabilities/info/info.verb.js.map +1 -1
- package/dist/src/capabilities/meta/mcp.verb.js +1 -0
- package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
- package/dist/src/capabilities/ontology/verbs.js +7 -10
- package/dist/src/capabilities/ontology/verbs.js.map +1 -1
- package/dist/src/capabilities/prompt/verbs.js +5 -4
- package/dist/src/capabilities/prompt/verbs.js.map +1 -1
- package/dist/src/capabilities/resources/provider.js +6 -3
- package/dist/src/capabilities/resources/provider.js.map +1 -1
- package/dist/src/capabilities/setup/operations/writability.js.map +1 -1
- package/dist/src/capabilities/setup/plan.render.js.map +1 -1
- package/dist/src/capabilities/setup/setup.verb.js +13 -5
- package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
- package/dist/src/capabilities/shared/calls.js +16 -0
- package/dist/src/capabilities/shared/calls.js.map +1 -0
- package/dist/src/capabilities/shared/packageManager.js.map +1 -1
- package/dist/src/capabilities/skill/discover.js.map +1 -1
- package/dist/src/capabilities/skill/render.js +7 -5
- package/dist/src/capabilities/skill/render.js.map +1 -1
- package/dist/src/capabilities/skill/verbs.js +5 -4
- package/dist/src/capabilities/skill/verbs.js.map +1 -1
- package/dist/src/capabilities/sources/collectStatus.js +6 -1
- package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
- package/dist/src/capabilities/sources/index.js +4 -3
- package/dist/src/capabilities/sources/index.js.map +1 -1
- package/dist/src/capabilities/sources/reset.render.js +37 -0
- package/dist/src/capabilities/sources/reset.render.js.map +1 -0
- package/dist/src/capabilities/sources/reset.verb.js +51 -0
- package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
- package/dist/src/capabilities/sources/runReset.js +59 -0
- package/dist/src/capabilities/sources/runReset.js.map +1 -0
- package/dist/src/capabilities/sources/runUpdate.js +7 -6
- package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
- package/dist/src/capabilities/sources/status.render.js +24 -8
- package/dist/src/capabilities/sources/status.render.js.map +1 -1
- package/dist/src/capabilities/sources/status.verb.js +1 -0
- package/dist/src/capabilities/sources/status.verb.js.map +1 -1
- package/dist/src/capabilities/sources/update.verb.js +2 -0
- package/dist/src/capabilities/sources/update.verb.js.map +1 -1
- package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
- package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
- package/dist/src/capabilities/version/version.verb.js +1 -0
- package/dist/src/capabilities/version/version.verb.js.map +1 -1
- package/dist/src/constants.js +4 -3
- package/dist/src/constants.js.map +1 -1
- package/dist/src/kernel/completion/complete.js.map +1 -1
- package/dist/src/kernel/completion/emitScripts.js.map +1 -1
- package/dist/src/kernel/completion/parse.js.map +1 -1
- package/dist/src/kernel/completion/rank.js.map +1 -1
- package/dist/src/kernel/completion/resolve.js.map +1 -1
- package/dist/src/kernel/config/readConfig.js.map +1 -1
- package/dist/src/kernel/error/PragmaError.js.map +1 -1
- package/dist/src/kernel/error/index.js +1 -1
- package/dist/src/kernel/error/index.js.map +1 -1
- package/dist/src/kernel/error/recovery.js +24 -42
- package/dist/src/kernel/error/recovery.js.map +1 -1
- package/dist/src/kernel/packs/collect.js +123 -9
- package/dist/src/kernel/packs/collect.js.map +1 -1
- package/dist/src/kernel/packs/compile.js +74 -15
- package/dist/src/kernel/packs/compile.js.map +1 -1
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
- package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
- package/dist/src/kernel/packs/iri.js +2 -4
- package/dist/src/kernel/packs/iri.js.map +1 -1
- package/dist/src/kernel/packs/renderPack.js +90 -50
- package/dist/src/kernel/packs/renderPack.js.map +1 -1
- package/dist/src/kernel/packs/resolveEntity.js +202 -90
- package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
- package/dist/src/kernel/packs/runBodies.js +89 -30
- package/dist/src/kernel/packs/runBodies.js.map +1 -1
- package/dist/src/kernel/packs/schema.js +66 -12
- package/dist/src/kernel/packs/schema.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
- package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
- package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
- package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
- package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
- package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
- package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
- package/dist/src/kernel/packs/storyRules.js +31 -4
- package/dist/src/kernel/packs/storyRules.js.map +1 -1
- package/dist/src/kernel/packs/tierScope.js +3 -3
- package/dist/src/kernel/packs/tierScope.js.map +1 -1
- package/dist/src/kernel/packs/types.js +4 -0
- package/dist/src/kernel/packs/types.js.map +1 -1
- package/dist/src/kernel/project/cli/buildProgram.js +4 -0
- package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
- package/dist/src/kernel/project/cli/dispatch.js +32 -8
- package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
- package/dist/src/kernel/project/cli/globalFlags.js.map +1 -1
- package/dist/src/kernel/project/cli/suggestNames.js +44 -1
- package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
- package/dist/src/kernel/project/cli/verbHelp.js +17 -18
- package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
- package/dist/src/kernel/project/mcp/buildServer.js +11 -4
- package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
- package/dist/src/kernel/project/mcp/envelope.js +3 -2
- package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
- package/dist/src/kernel/project/mcp/instructions.js +101 -31
- package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
- package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
- package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
- package/dist/src/kernel/render/envelope.js.map +1 -1
- package/dist/src/kernel/render/renderers.js +3 -3
- package/dist/src/kernel/render/renderers.js.map +1 -1
- package/dist/src/kernel/render/turtle.js +21 -4
- package/dist/src/kernel/render/turtle.js.map +1 -1
- package/dist/src/kernel/runtime/boot.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/build.js +54 -1
- package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
- package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/read.js +3 -5
- package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
- package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
- package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
- package/dist/src/kernel/runtime/loadSession.js +8 -1
- package/dist/src/kernel/runtime/loadSession.js.map +1 -1
- package/dist/src/kernel/runtime/packVersion.js +75 -0
- package/dist/src/kernel/runtime/packVersion.js.map +1 -0
- package/dist/src/kernel/runtime/readEntity.js +43 -4
- package/dist/src/kernel/runtime/readEntity.js.map +1 -1
- package/dist/src/kernel/runtime/refs/resolve.js.map +1 -1
- package/dist/src/kernel/runtime/resolveSources.js +61 -9
- package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
- package/dist/src/kernel/runtime/storeReadiness.js +3 -2
- package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
- package/dist/src/kernel/spec/call.js +167 -0
- package/dist/src/kernel/spec/call.js.map +1 -0
- package/dist/src/kernel/spec/emitReference.js +9 -13
- package/dist/src/kernel/spec/emitReference.js.map +1 -1
- package/dist/src/kernel/spec/emitSurface.js +8 -1
- package/dist/src/kernel/spec/emitSurface.js.map +1 -1
- package/dist/src/kernel/spec/guidance.js +63 -0
- package/dist/src/kernel/spec/guidance.js.map +1 -0
- package/dist/src/kernel/spec/wireType.js +36 -0
- package/dist/src/kernel/spec/wireType.js.map +1 -0
- package/package.json +16 -16
- package/pragma.conf.ts +294 -177
- package/src/capabilities/block.parity.test.ts +1 -1
- package/src/capabilities/callRule.test.ts +147 -0
- package/src/capabilities/capabilities/capabilities.test.ts +47 -19
- package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
- package/src/capabilities/capabilities/catalog.ts +60 -55
- package/src/capabilities/capabilities/index.ts +2 -0
- package/src/capabilities/capabilities/types.ts +16 -19
- package/src/capabilities/colophon/colophon.render.ts +9 -5
- package/src/capabilities/colophon/colophon.verb.ts +1 -0
- package/src/capabilities/config/get.verb.ts +2 -0
- package/src/capabilities/config/runField.ts +3 -3
- package/src/capabilities/config/set.verb.ts +3 -0
- package/src/capabilities/config/show.verb.ts +2 -0
- package/src/capabilities/config/unset.verb.ts +2 -0
- package/src/capabilities/create/constants.ts +8 -5
- package/src/capabilities/create/create.mcp.test.ts +173 -1
- package/src/capabilities/create/create.test.ts +4 -2
- package/src/capabilities/create/create.verb.ts +45 -3
- package/src/capabilities/create/createGrammar.test.ts +41 -5
- package/src/capabilities/create/createSurface.generated.ts +22 -0
- package/src/capabilities/create/crossCli.subprocess.test.ts +53 -14
- package/src/capabilities/create/index.ts +1 -1
- package/src/capabilities/create/pickGenerator.ts +1 -0
- package/src/capabilities/create/shippedCreate.subprocess.test.ts +28 -7
- package/src/capabilities/distribution.test.ts +6 -3
- package/src/capabilities/distribution.ts +6 -0
- package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
- package/src/capabilities/doctor/doctor.test.ts +38 -1
- package/src/capabilities/doctor/doctor.verb.ts +2 -0
- package/src/capabilities/emptyRecovery.test.ts +5 -0
- package/src/capabilities/graph/connect.verb.ts +2 -0
- package/src/capabilities/graph/inspect.render.ts +9 -1
- package/src/capabilities/graph/inspect.test.ts +73 -0
- package/src/capabilities/graph/inspect.verb.ts +2 -0
- package/src/capabilities/graph/query.render.test.ts +35 -0
- package/src/capabilities/graph/query.render.ts +45 -7
- package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
- package/src/capabilities/graph/query.verb.ts +2 -0
- package/src/capabilities/graph/queryText.test.ts +55 -1
- package/src/capabilities/graph/queryText.ts +47 -0
- package/src/capabilities/graph/runConnect.ts +4 -5
- package/src/capabilities/graph/runQuery.ts +23 -9
- package/src/capabilities/info/info.verb.ts +2 -0
- package/src/capabilities/links.shipped.exec.test.ts +398 -0
- package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
- package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
- package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
- package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
- package/src/capabilities/meta/mcp.verb.ts +2 -0
- package/src/capabilities/ontology/ontology.test.ts +24 -0
- package/src/capabilities/ontology/verbs.ts +16 -12
- package/src/capabilities/prompt/verbs.ts +10 -4
- package/src/capabilities/resources/provider.ts +7 -3
- package/src/capabilities/resources/resources.test.ts +2 -1
- package/src/capabilities/setup/setup.verb.ts +16 -0
- package/src/capabilities/shared/calls.ts +20 -0
- package/src/capabilities/skill/render.ts +9 -6
- package/src/capabilities/skill/verbs.ts +8 -4
- package/src/capabilities/sources/collectStatus.ts +6 -1
- package/src/capabilities/sources/index.ts +4 -3
- package/src/capabilities/sources/reset.render.test.ts +33 -0
- package/src/capabilities/sources/reset.render.ts +43 -0
- package/src/capabilities/sources/reset.verb.ts +59 -0
- package/src/capabilities/sources/runReset.ts +65 -0
- package/src/capabilities/sources/runUpdate.ts +12 -11
- package/src/capabilities/sources/sources.test.ts +158 -3
- package/src/capabilities/sources/status.render.test.ts +63 -1
- package/src/capabilities/sources/status.render.ts +25 -8
- package/src/capabilities/sources/status.verb.ts +2 -0
- package/src/capabilities/sources/types.ts +26 -0
- package/src/capabilities/sources/update.verb.ts +3 -0
- package/src/capabilities/surface.test.ts +29 -4
- package/src/capabilities/token.parity.test.ts +3 -2
- package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
- package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
- package/src/capabilities/variable.parity.test.ts +2 -1
- package/src/capabilities/version/version.verb.ts +1 -0
- package/src/capabilities/wireContract.test.ts +110 -0
- package/src/constants.ts +4 -3
- package/src/kernel/completion/shellDrive.test.ts +2 -2
- package/src/kernel/copy.test.ts +1 -1
- package/src/kernel/error/PragmaError.test.ts +7 -3
- package/src/kernel/error/index.ts +1 -1
- package/src/kernel/error/recovery.test.ts +52 -18
- package/src/kernel/error/recovery.ts +25 -46
- package/src/kernel/error/types.ts +1 -1
- package/src/kernel/packs/collect.test.ts +79 -0
- package/src/kernel/packs/collect.ts +151 -11
- package/src/kernel/packs/compile.test.ts +107 -10
- package/src/kernel/packs/compile.ts +100 -11
- package/src/kernel/packs/filteredEmpty.test.ts +22 -5
- package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
- package/src/kernel/packs/graphqlSource.test.ts +42 -0
- package/src/kernel/packs/iri.ts +5 -4
- package/src/kernel/packs/listNotice.test.ts +1 -1
- package/src/kernel/packs/nounLink.test.ts +385 -0
- package/src/kernel/packs/renderPack.ts +137 -57
- package/src/kernel/packs/resolveEntity.test.ts +170 -1
- package/src/kernel/packs/resolveEntity.ts +281 -101
- package/src/kernel/packs/runBodies.ts +133 -31
- package/src/kernel/packs/schema.ts +77 -12
- package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
- package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
- package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
- package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
- package/src/kernel/packs/sparql/filterValues.ts +50 -31
- package/src/kernel/packs/sparql/runSelect.ts +4 -6
- package/src/kernel/packs/storyRules.ts +37 -4
- package/src/kernel/packs/tierScope.test.ts +120 -6
- package/src/kernel/packs/tierScope.ts +5 -7
- package/src/kernel/packs/toolDescription.test.ts +59 -74
- package/src/kernel/packs/types.ts +93 -11
- package/src/kernel/project/cli/buildProgram.ts +4 -0
- package/src/kernel/project/cli/dispatch.ts +34 -8
- package/src/kernel/project/cli/suggest.test.ts +77 -0
- package/src/kernel/project/cli/suggestNames.ts +47 -1
- package/src/kernel/project/cli/verbHelp.ts +16 -19
- package/src/kernel/project/mcp/buildServer.ts +15 -4
- package/src/kernel/project/mcp/envelope.ts +3 -2
- package/src/kernel/project/mcp/instructions.ts +123 -35
- package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
- package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
- package/src/kernel/project/mcp/registerVerb.ts +67 -29
- package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
- package/src/kernel/render/renderers.test.ts +2 -2
- package/src/kernel/render/renderers.ts +3 -3
- package/src/kernel/render/turtle.ts +24 -4
- package/src/kernel/runtime/graphpack/build.ts +58 -1
- package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
- package/src/kernel/runtime/graphpack/manifest.ts +15 -0
- package/src/kernel/runtime/graphpack/read.ts +4 -6
- package/src/kernel/runtime/graphpack/schemas.ts +1 -0
- package/src/kernel/runtime/graphpack/types.ts +13 -0
- package/src/kernel/runtime/loadSession.ts +8 -1
- package/src/kernel/runtime/packVersion.test.ts +61 -0
- package/src/kernel/runtime/packVersion.ts +84 -0
- package/src/kernel/runtime/readEntity.ts +61 -6
- package/src/kernel/runtime/resolveSources.test.ts +90 -7
- package/src/kernel/runtime/resolveSources.ts +91 -10
- package/src/kernel/runtime/storeReadiness.ts +4 -4
- package/src/kernel/runtime/types.ts +12 -0
- package/src/kernel/spec/call.test.ts +95 -0
- package/src/kernel/spec/call.ts +186 -0
- package/src/kernel/spec/emitReference.ts +9 -13
- package/src/kernel/spec/emitSurface.ts +9 -1
- package/src/kernel/spec/guidance.test.ts +49 -0
- package/src/kernel/spec/guidance.ts +68 -0
- package/src/kernel/spec/index.ts +4 -0
- package/src/kernel/spec/surfaceConformance.test.ts +2 -2
- package/src/kernel/spec/types.ts +64 -7
- package/src/kernel/spec/wireType.ts +53 -0
- package/src/testing/behavioral/README.md +1 -1
- package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
- package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
- package/src/testing/behavioral/parity.test.ts +40 -0
- package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
- package/src/testing/eval/cases/assembly.ts +4 -3
- package/src/testing/eval/cases/stable.ts +2 -3
- package/src/testing/helpers/callSchema.ts +40 -0
- package/src/testing/helpers/parity.ts +35 -2
- package/src/testing/setupCallChecking.ts +30 -0
- package/dist/src/capabilities/capabilities/hints.js +0 -223
- package/dist/src/capabilities/capabilities/hints.js.map +0 -1
- package/src/capabilities/capabilities/hints.ts +0 -266
|
@@ -51,6 +51,7 @@ export const packIndexSchema: z.ZodType<PackIndex> = z.object({
|
|
|
51
51
|
/** zod schema validating a persisted `manifest.json`. */
|
|
52
52
|
export const manifestSchema: z.ZodType<Manifest> = z.object({
|
|
53
53
|
name: z.string(),
|
|
54
|
+
/** The CLI version that built the pack — the boot decision compares it. */
|
|
54
55
|
version: z.string(),
|
|
55
56
|
/** The config `packs` ref this pack was built from (verbatim), or a label. */
|
|
56
57
|
sourceRef: z.string(),
|
|
@@ -115,6 +115,19 @@ export interface PackIndex {
|
|
|
115
115
|
*/
|
|
116
116
|
export interface Manifest {
|
|
117
117
|
readonly name: string;
|
|
118
|
+
/**
|
|
119
|
+
* The version of the CLI that built this pack — `VERSION`, written by
|
|
120
|
+
* `sources update` and by the bundler for the embedded snapshot.
|
|
121
|
+
*
|
|
122
|
+
* Load-bearing, not just provenance: the boot decision compares it with the
|
|
123
|
+
* running CLI's version, so a pack an older CLI built in a project that
|
|
124
|
+
* declares no packs of its own yields to the newer CLI's embedded snapshot
|
|
125
|
+
* (see `resolveSources`). Every pack any released CLI has ever built already
|
|
126
|
+
* carries it, which is why that rule needed no new field and heals packs built
|
|
127
|
+
* before it existed. A REUSED pack has it refreshed (see
|
|
128
|
+
* `build.stampBuilderVersion`) — otherwise `sources update` could not get a
|
|
129
|
+
* project out of that state.
|
|
130
|
+
*/
|
|
118
131
|
readonly version: string;
|
|
119
132
|
/** The config `packs` ref this pack was built from (verbatim), or a label. */
|
|
120
133
|
readonly sourceRef: string;
|
|
@@ -46,5 +46,12 @@ export async function loadStoreSession(
|
|
|
46
46
|
decision.kind === "embedded"
|
|
47
47
|
? await materializeEmbeddedPack()
|
|
48
48
|
: decision.dir;
|
|
49
|
-
|
|
49
|
+
const session = await readPack(dir);
|
|
50
|
+
// Carry the passed-over pack (the upgrade row of the decision table) onto the
|
|
51
|
+
// session, so a read can report which pack it did NOT answer from without
|
|
52
|
+
// re-deriving the decision — the same reason the session already carries the
|
|
53
|
+
// loaded pack's own manifest.
|
|
54
|
+
return decision.kind === "embedded" && decision.ignoredPack !== undefined
|
|
55
|
+
? { ...session, ignoredPack: decision.ignoredPack }
|
|
56
|
+
: session;
|
|
50
57
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The builder-version comparison behind the boot decision's upgrade row.
|
|
3
|
+
*
|
|
4
|
+
* It decides whether a pack a user built on purpose is passed over, so the
|
|
5
|
+
* cases that matter most here are the ones where it must answer NO.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, expect, it } from "vitest";
|
|
9
|
+
import { packIsOlderThanCli, parseSemver } from "./packVersion.js";
|
|
10
|
+
|
|
11
|
+
describe("parseSemver", () => {
|
|
12
|
+
it("parses a release triple", () => {
|
|
13
|
+
expect(parseSemver("0.38.0")).toEqual([0, 38, 0]);
|
|
14
|
+
expect(parseSemver("10.2.137")).toEqual([10, 2, 137]);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("parses a pre-release/build version to its release triple", () => {
|
|
18
|
+
// The suffix orders two builds of the same release; this comparison only
|
|
19
|
+
// ever asks which RELEASE is older.
|
|
20
|
+
expect(parseSemver("1.2.3-rc.1")).toEqual([1, 2, 3]);
|
|
21
|
+
expect(parseSemver("1.2.3+sha.abc")).toEqual([1, 2, 3]);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("refuses anything that is not a version", () => {
|
|
25
|
+
for (const value of ["0", "1.2", "v1.2.3", "", "next", "1.2.3.4"]) {
|
|
26
|
+
expect(parseSemver(value)).toBeUndefined();
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("packIsOlderThanCli", () => {
|
|
32
|
+
it("is true only for a pack built by a strictly older CLI", () => {
|
|
33
|
+
expect(packIsOlderThanCli("0.37.0", "0.38.0")).toBe(true);
|
|
34
|
+
expect(packIsOlderThanCli("0.38.0", "1.0.0")).toBe(true);
|
|
35
|
+
expect(packIsOlderThanCli("1.2.3", "1.2.4")).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("keeps the pack when the versions are equal", () => {
|
|
39
|
+
expect(packIsOlderThanCli("0.38.0", "0.38.0")).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("keeps the pack when a NEWER CLI built it", () => {
|
|
43
|
+
// A downgrade, or two CLIs sharing one cache. The user's own build wins.
|
|
44
|
+
expect(packIsOlderThanCli("0.39.0", "0.38.0")).toBe(false);
|
|
45
|
+
expect(packIsOlderThanCli("1.0.0", "0.38.0")).toBe(false);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("keeps the pack under a dev build (0.0.0) or an unreadable CLI version", () => {
|
|
49
|
+
// A local checkout must not start reporting every pack on the machine as
|
|
50
|
+
// stale — its version says nothing about how its snapshot compares.
|
|
51
|
+
expect(packIsOlderThanCli("0.37.0", "0.0.0")).toBe(false);
|
|
52
|
+
expect(packIsOlderThanCli("0.37.0", "workspace")).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("keeps a pack whose recorded version cannot be read", () => {
|
|
56
|
+
// The notice names the version that built the pack, so a pack we cannot
|
|
57
|
+
// name is one we cannot honestly call old.
|
|
58
|
+
expect(packIsOlderThanCli("0", "0.38.0")).toBe(false);
|
|
59
|
+
expect(packIsOlderThanCli("", "0.38.0")).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is a built pack older than the CLI that is now reading it?
|
|
3
|
+
*
|
|
4
|
+
* A pack's `manifest.json` records the version of the CLI that built it (the
|
|
5
|
+
* `version` field — see `graphpack/types.Manifest`). When a user upgrades, the
|
|
6
|
+
* new CLI ships a new embedded snapshot, and a pack built by the OLD CLI weeks
|
|
7
|
+
* ago is no longer the better of the two: it answered every read from a graph
|
|
8
|
+
* the upgrade was meant to replace, silently, with nothing in `sources status`
|
|
9
|
+
* to say so. This module is the comparison that ends that — `resolveSources`
|
|
10
|
+
* uses it to let the embedded snapshot answer instead (see its decision table).
|
|
11
|
+
*
|
|
12
|
+
* A LEAF on the storeless fast path: string arithmetic, no imports at all. It
|
|
13
|
+
* deliberately does NOT depend on a semver library — the comparison is
|
|
14
|
+
* major/minor/patch on two strings that both come from a `package.json`
|
|
15
|
+
* `version`, and the fast path pays for every value import it makes.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** A parsed `major.minor.patch` triple. */
|
|
19
|
+
type Semver = readonly [major: number, minor: number, patch: number];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse the `major.minor.patch` prefix of a version string.
|
|
23
|
+
*
|
|
24
|
+
* A pre-release or build suffix (`1.2.3-rc.1`, `1.2.3+sha`) parses to its
|
|
25
|
+
* release triple: the suffix orders two builds of the SAME release, and this
|
|
26
|
+
* comparison only ever asks which release is older.
|
|
27
|
+
*
|
|
28
|
+
* @param value - The recorded version string.
|
|
29
|
+
* @returns The triple, or `undefined` when the string is not a version.
|
|
30
|
+
*/
|
|
31
|
+
export function parseSemver(value: string): Semver | undefined {
|
|
32
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(value.trim());
|
|
33
|
+
if (match === null) return undefined;
|
|
34
|
+
const [, major, minor, patch] = match as unknown as [
|
|
35
|
+
string,
|
|
36
|
+
string,
|
|
37
|
+
string,
|
|
38
|
+
string,
|
|
39
|
+
];
|
|
40
|
+
return [Number(major), Number(minor), Number(patch)];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Whether a pack built by `packVersion` predates the CLI at `cliVersion`.
|
|
45
|
+
*
|
|
46
|
+
* Deliberately conservative — it answers `true` only when the pack is PROVABLY
|
|
47
|
+
* older, because answering `true` means overriding a store the user built on
|
|
48
|
+
* purpose:
|
|
49
|
+
*
|
|
50
|
+
* - Equal, or a pack built by a NEWER CLI (a downgrade, or two CLIs sharing one
|
|
51
|
+
* cache): keep the built pack. The user's own build wins, exactly as today.
|
|
52
|
+
* - `cliVersion` unparseable, or `0.0.0` (a dev build, whose version says
|
|
53
|
+
* nothing about how its snapshot compares): keep the built pack, and say
|
|
54
|
+
* nothing about it — a local checkout must not start reporting every pack on
|
|
55
|
+
* the machine as stale.
|
|
56
|
+
* - `packVersion` unparseable: keep the built pack. The notice a user reads
|
|
57
|
+
* names the version that built the pack, so a pack we cannot name is one we
|
|
58
|
+
* cannot honestly call old. (No released CLI writes such a manifest —
|
|
59
|
+
* `version` comes from `package.json` — and a manifest MISSING the field is
|
|
60
|
+
* already not a readable pack at all: `validateManifest` requires it, so the
|
|
61
|
+
* directory fails the completeness gate and boot reports the ordinary
|
|
62
|
+
* "not built" recovery.)
|
|
63
|
+
*
|
|
64
|
+
* @param packVersion - The manifest's `version` (the CLI that built the pack).
|
|
65
|
+
* @param cliVersion - The running CLI's version.
|
|
66
|
+
* @returns Whether the pack should yield to the running CLI's snapshot.
|
|
67
|
+
*/
|
|
68
|
+
export function packIsOlderThanCli(
|
|
69
|
+
packVersion: string,
|
|
70
|
+
cliVersion: string,
|
|
71
|
+
): boolean {
|
|
72
|
+
const cli = parseSemver(cliVersion);
|
|
73
|
+
if (cli === undefined || (cli[0] === 0 && cli[1] === 0 && cli[2] === 0)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const pack = parseSemver(packVersion);
|
|
77
|
+
if (pack === undefined) return false;
|
|
78
|
+
for (let index = 0; index < 3; index++) {
|
|
79
|
+
const packPart = pack[index] as number;
|
|
80
|
+
const cliPart = cli[index] as number;
|
|
81
|
+
if (packPart !== cliPart) return packPart < cliPart;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
*/
|
|
55
55
|
|
|
56
56
|
import type { DetailLevel } from "../../constants.js";
|
|
57
|
-
import {
|
|
57
|
+
import { callRecovery, PragmaError } from "../error/index.js";
|
|
58
58
|
import { resolveUri } from "../packs/iri.js";
|
|
59
59
|
import { compactUri, resolveDetail } from "../render/index.js";
|
|
60
60
|
import type { PackIndex } from "./graphpack/types.js";
|
|
@@ -110,6 +110,18 @@ const SAMPLE_CAP: Readonly<Record<DetailLevel, number>> = {
|
|
|
110
110
|
detailed: 5,
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
+
/**
|
|
114
|
+
* How many OBJECTS of one outbound predicate each level lists; the true total
|
|
115
|
+
* rides {@link PredicateGroup.count}. `summary` keeps a few rather than none —
|
|
116
|
+
* a predicate's first objects are the entity's type, name and tier. Derivation
|
|
117
|
+
* in BUDGETS.md.
|
|
118
|
+
*/
|
|
119
|
+
const OUTBOUND_CAP: Readonly<Record<DetailLevel, number>> = {
|
|
120
|
+
summary: 3,
|
|
121
|
+
standard: 10,
|
|
122
|
+
detailed: Number.POSITIVE_INFINITY,
|
|
123
|
+
};
|
|
124
|
+
|
|
113
125
|
/**
|
|
114
126
|
* How many characters of a LITERAL each level carries.
|
|
115
127
|
*
|
|
@@ -182,7 +194,12 @@ export type NestedRecord = Record<string, ReadTerm>;
|
|
|
182
194
|
/** All objects asserted for one predicate on the subject. */
|
|
183
195
|
export interface PredicateGroup {
|
|
184
196
|
readonly predicate: ReadTerm;
|
|
197
|
+
/** The objects — all of them, or the head when {@link truncated}. */
|
|
185
198
|
readonly objects: ReadTerm[];
|
|
199
|
+
/** Set when `objects` is shorter than the predicate's true fan-out. */
|
|
200
|
+
readonly truncated?: true;
|
|
201
|
+
/** The TRUE number of objects, present only when truncated. */
|
|
202
|
+
readonly count?: number;
|
|
186
203
|
}
|
|
187
204
|
|
|
188
205
|
/** Everything asserting one predicate ABOUT the subject. */
|
|
@@ -348,10 +365,12 @@ export async function readEntity(
|
|
|
348
365
|
|
|
349
366
|
if (outbound.type !== "select" || outbound.termBindings.length === 0) {
|
|
350
367
|
throw PragmaError.notFound("entity", uri, {
|
|
351
|
-
recovery:
|
|
352
|
-
|
|
368
|
+
recovery: callRecovery(
|
|
369
|
+
{
|
|
370
|
+
verb: "graph query",
|
|
371
|
+
params: { sparql: "SELECT ?s WHERE { ?s ?p ?o } LIMIT 10" },
|
|
372
|
+
},
|
|
353
373
|
"Check the URI, or list known entities.",
|
|
354
|
-
{ tool: "graph_query" },
|
|
355
374
|
),
|
|
356
375
|
});
|
|
357
376
|
}
|
|
@@ -381,11 +400,17 @@ export async function readEntity(
|
|
|
381
400
|
objects.push(term(object));
|
|
382
401
|
groupMap.set(predicate.value, objects);
|
|
383
402
|
}
|
|
403
|
+
// Cut from the rows already in hand: the count is the group's own length, so
|
|
404
|
+
// bounding the answer costs no second query.
|
|
405
|
+
const cap = OUTBOUND_CAP[detail];
|
|
384
406
|
const groups: PredicateGroup[] = [...groupMap.entries()].map(
|
|
385
407
|
([predicate, objects]) => ({
|
|
386
408
|
// Every key was written from a binding above, so the lookup cannot miss.
|
|
387
409
|
predicate: predicates.get(predicate) as ReadTerm,
|
|
388
|
-
objects,
|
|
410
|
+
objects: objects.slice(0, cap),
|
|
411
|
+
...(objects.length > cap
|
|
412
|
+
? { truncated: true as const, count: objects.length }
|
|
413
|
+
: {}),
|
|
389
414
|
}),
|
|
390
415
|
);
|
|
391
416
|
|
|
@@ -393,7 +418,10 @@ export async function readEntity(
|
|
|
393
418
|
const nested =
|
|
394
419
|
detail === "summary" || !hasBlankObject
|
|
395
420
|
? {}
|
|
396
|
-
:
|
|
421
|
+
: capNested(
|
|
422
|
+
await readNested(rt, resolved, session.prefixes, term),
|
|
423
|
+
groups,
|
|
424
|
+
);
|
|
397
425
|
|
|
398
426
|
const prefixed = compactUri(resolved, session.prefixes);
|
|
399
427
|
const label = session.index.entities.find((e) => e.uri === resolved)?.label;
|
|
@@ -409,6 +437,33 @@ export async function readEntity(
|
|
|
409
437
|
};
|
|
410
438
|
}
|
|
411
439
|
|
|
440
|
+
/**
|
|
441
|
+
* Hold the inlined records to the objects their predicate kept.
|
|
442
|
+
*
|
|
443
|
+
* A record IS one of its predicate's objects, written out. Listing ten of a
|
|
444
|
+
* predicate's 24 blank objects beside all 24 of their records would make the
|
|
445
|
+
* cap a claim about the group and not about the answer.
|
|
446
|
+
*/
|
|
447
|
+
function capNested(
|
|
448
|
+
nested: Record<string, NestedRecord[]>,
|
|
449
|
+
groups: readonly PredicateGroup[],
|
|
450
|
+
): Record<string, NestedRecord[]> {
|
|
451
|
+
const kept = new Map(
|
|
452
|
+
groups
|
|
453
|
+
.filter((group) => group.truncated)
|
|
454
|
+
.map((group) => [
|
|
455
|
+
group.predicate.prefixed ?? group.predicate.value,
|
|
456
|
+
group.objects.filter((object) => object.termType === "BlankNode")
|
|
457
|
+
.length,
|
|
458
|
+
]),
|
|
459
|
+
);
|
|
460
|
+
return Object.fromEntries(
|
|
461
|
+
Object.entries(nested)
|
|
462
|
+
.map(([via, rows]) => [via, rows.slice(0, kept.get(via))] as const)
|
|
463
|
+
.filter(([, rows]) => rows.length > 0),
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
412
467
|
/**
|
|
413
468
|
* Everything asserting a predicate about the subject, grouped and capped.
|
|
414
469
|
*
|
|
@@ -2,10 +2,12 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { VERSION } from "../../constants.js";
|
|
5
6
|
import type { ConfigLayers } from "../config/types.js";
|
|
6
7
|
import { loadStoreSession } from "./loadSession.js";
|
|
8
|
+
import { parseSemver } from "./packVersion.js";
|
|
7
9
|
import { activePackPath, packDir } from "./paths.js";
|
|
8
|
-
import { resolveSources } from "./resolveSources.js";
|
|
10
|
+
import { describeIgnoredPack, resolveSources } from "./resolveSources.js";
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* Pin the store-boot decision table (`resolveSources`) — especially the two
|
|
@@ -41,33 +43,52 @@ function layersWith(packsOrigin: "default" | "project"): ConfigLayers {
|
|
|
41
43
|
};
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Write a pack's manifest.json at the given hash (the completeness marker).
|
|
48
|
+
*
|
|
49
|
+
* `version` is the CLI version that built the pack — the field the upgrade row
|
|
50
|
+
* of the decision table compares. The default is deliberately NOT a version:
|
|
51
|
+
* every case that predates the upgrade row keeps the built pack whatever CLI is
|
|
52
|
+
* running, so those cases stay about what they were about.
|
|
53
|
+
*/
|
|
54
|
+
function writeManifest(dir: string, hash: string, version = "0"): void {
|
|
46
55
|
writeFileSync(
|
|
47
56
|
join(dir, "manifest.json"),
|
|
48
57
|
JSON.stringify({
|
|
49
58
|
name: "t",
|
|
50
|
-
version
|
|
59
|
+
version,
|
|
51
60
|
sourceRef: "t",
|
|
52
61
|
contentHash: hash,
|
|
53
62
|
prefixes: {},
|
|
54
|
-
createdAt:
|
|
63
|
+
createdAt: BUILT_AT,
|
|
55
64
|
}),
|
|
56
65
|
);
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
/** The `createdAt` every fixture pack records — a fixed day, for the sentence. */
|
|
69
|
+
const BUILT_AT = "2026-09-10T21:49:00.411Z";
|
|
70
|
+
|
|
59
71
|
/** Materialize a COMPLETE pack — manifest + non-empty dump, schema, and index. */
|
|
60
|
-
function writeCompletePack(hash: string): string {
|
|
72
|
+
function writeCompletePack(hash: string, version?: string): string {
|
|
61
73
|
const dir = packDir(hash);
|
|
62
74
|
mkdirSync(dir, { recursive: true });
|
|
63
75
|
writeFileSync(join(dir, "data.nq"), "<urn:s> <urn:p> <urn:o> .\n");
|
|
64
76
|
writeFileSync(join(dir, "schema.json"), "{}");
|
|
65
77
|
writeFileSync(join(dir, "index.json"), "{}");
|
|
66
78
|
writeFileSync(join(dir, "stories.json"), "[]");
|
|
67
|
-
writeManifest(dir, hash);
|
|
79
|
+
writeManifest(dir, hash, version);
|
|
68
80
|
return dir;
|
|
69
81
|
}
|
|
70
82
|
|
|
83
|
+
/** A version one minor release below the running CLI, and one above it. */
|
|
84
|
+
function neighbouringVersions(): { older: string; newer: string } {
|
|
85
|
+
const [major, minor] = parseSemver(VERSION) as [number, number, number];
|
|
86
|
+
return {
|
|
87
|
+
older: minor > 0 ? `${major}.${minor - 1}.0` : `${major - 1}.0.0`,
|
|
88
|
+
newer: `${major}.${minor + 1}.0`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
71
92
|
/** Plant the active-pack pointer a `sources update` would have written. */
|
|
72
93
|
function writePointer(cwd: string, content: string): void {
|
|
73
94
|
const path = activePackPath(cwd);
|
|
@@ -175,6 +196,68 @@ describe("resolveSources decision table", () => {
|
|
|
175
196
|
});
|
|
176
197
|
});
|
|
177
198
|
|
|
199
|
+
describe("resolveSources — a pack an older CLI built (the upgrade row)", () => {
|
|
200
|
+
it("yields to the embedded snapshot, and names the pack it passed over", () => {
|
|
201
|
+
// The reported bug: a pack built weeks ago by an older CLI answered every
|
|
202
|
+
// read from the graph the upgrade replaced, and nothing said so.
|
|
203
|
+
const cwd = tmp();
|
|
204
|
+
const hash = "e".repeat(64);
|
|
205
|
+
const { older } = neighbouringVersions();
|
|
206
|
+
writeCompletePack(hash, older);
|
|
207
|
+
writePointer(cwd, hash);
|
|
208
|
+
|
|
209
|
+
expect(resolveSources(layersWith("default"), cwd)).toEqual({
|
|
210
|
+
kind: "embedded",
|
|
211
|
+
ignoredPack: { contentHash: hash, builtBy: older, builtAt: BUILT_AT },
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("keeps the built pack when the project declares its OWN packs", () => {
|
|
216
|
+
// A project with its own packs has its own graph, and a snapshot of the
|
|
217
|
+
// distribution's is not a substitute for it, stale or not. Byte-identical
|
|
218
|
+
// to the pre-existing pack row.
|
|
219
|
+
const cwd = tmp();
|
|
220
|
+
const hash = "f".repeat(64);
|
|
221
|
+
const { older } = neighbouringVersions();
|
|
222
|
+
const dir = writeCompletePack(hash, older);
|
|
223
|
+
writePointer(cwd, hash);
|
|
224
|
+
|
|
225
|
+
expect(resolveSources(layersWith("project"), cwd)).toEqual({
|
|
226
|
+
kind: "pack",
|
|
227
|
+
dir,
|
|
228
|
+
contentHash: hash,
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it("keeps a pack this CLI, or a newer one, built", () => {
|
|
233
|
+
const { newer } = neighbouringVersions();
|
|
234
|
+
for (const version of [VERSION, newer]) {
|
|
235
|
+
const cwd = tmp();
|
|
236
|
+
const hash = version === VERSION ? "1".repeat(64) : "2".repeat(64);
|
|
237
|
+
const dir = writeCompletePack(hash, version);
|
|
238
|
+
writePointer(cwd, hash);
|
|
239
|
+
|
|
240
|
+
expect(resolveSources(layersWith("default"), cwd)).toEqual({
|
|
241
|
+
kind: "pack",
|
|
242
|
+
dir,
|
|
243
|
+
contentHash: hash,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("names the version, the day, and both ways out", () => {
|
|
249
|
+
expect(
|
|
250
|
+
describeIgnoredPack({
|
|
251
|
+
contentHash: "0e82d35c6668",
|
|
252
|
+
builtBy: "0.37.0",
|
|
253
|
+
builtAt: BUILT_AT,
|
|
254
|
+
}),
|
|
255
|
+
).toBe(
|
|
256
|
+
"a pack built by pragma 0.37.0 on 2026-09-10 is ignored — run `pragma sources update` to rebuild it or `pragma sources reset` to remove it",
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
178
261
|
describe("loadStoreSession recovery", () => {
|
|
179
262
|
it("surfaces the `pragma sources update` recovery (CLI + MCP tool) when cold", async () => {
|
|
180
263
|
const cwd = tmp();
|
|
@@ -8,27 +8,59 @@
|
|
|
8
8
|
* says so, and it answers with completion candidates, never content.
|
|
9
9
|
*
|
|
10
10
|
* Boot never touches the network. From the project's active-pack pointer (the
|
|
11
|
-
* content hash `sources update` last built here)
|
|
11
|
+
* content hash `sources update` last built here), the pack's recorded builder
|
|
12
|
+
* version, and the resolved config:
|
|
12
13
|
*
|
|
13
|
-
* | pointer | pack complete | packs origin | → decision
|
|
14
|
-
*
|
|
15
|
-
* | yes | yes | — | load the built pack
|
|
16
|
-
* | yes |
|
|
17
|
-
* |
|
|
18
|
-
* | no
|
|
14
|
+
* | pointer | pack complete | built by | packs origin | → decision |
|
|
15
|
+
* |---------|---------------|----------------|--------------|---------------------|
|
|
16
|
+
* | yes | yes | this CLI/newer | — | load the built pack |
|
|
17
|
+
* | yes | yes | an older CLI | configured | load the built pack |
|
|
18
|
+
* | yes | yes | an older CLI | default | embedded, pack named|
|
|
19
|
+
* | yes | no | — | — | STORE_UNAVAILABLE |
|
|
20
|
+
* | no | — | — | default | embedded fallback |
|
|
21
|
+
* | no | — | — | configured | STORE_UNAVAILABLE |
|
|
19
22
|
*
|
|
20
23
|
* A configured-but-unbuilt store (or a pointer whose pack the cache lost)
|
|
21
24
|
* surfaces STORE_UNAVAILABLE with a single recovery: `pragma sources update`.
|
|
22
25
|
* "packs origin default" means the user has not pinned their own packs —
|
|
23
26
|
* a fresh install — so the embedded pack answers reads offline. A project that
|
|
24
27
|
* DID configure its own packs is never quietly served the distribution's graph.
|
|
28
|
+
*
|
|
29
|
+
* The third row is the UPGRADE row, and it is why the pack's builder version is
|
|
30
|
+
* part of the decision. A CLI upgrade ships a NEW embedded snapshot; a pack some
|
|
31
|
+
* older CLI built in this directory weeks ago then answered every read from the
|
|
32
|
+
* graph the upgrade replaced — silently, with `sources status` reporting a
|
|
33
|
+
* healthy `built` store. So a pack built by an older CLI, in a project that
|
|
34
|
+
* declares no packs of its own, YIELDS: the snapshot answers and the pack is
|
|
35
|
+
* named as ignored ({@link IgnoredPack}) on every surface that reports on the
|
|
36
|
+
* store, until `sources update` rebuilds it or `sources reset` removes it. The
|
|
37
|
+
* second row is the other half of the same rule: a project with its own packs
|
|
38
|
+
* has its own graph, and a snapshot of the distribution's is not a substitute
|
|
39
|
+
* for it, stale or not — that project keeps today's behaviour exactly.
|
|
25
40
|
*/
|
|
26
41
|
|
|
27
42
|
import { existsSync } from "node:fs";
|
|
43
|
+
import { BIN_NAME, VERSION } from "../../constants.js";
|
|
28
44
|
import type { ConfigLayers } from "../config/types.js";
|
|
29
|
-
import {
|
|
45
|
+
import { packArtifactsPresent, readManifest } from "./graphpack/manifest.js";
|
|
46
|
+
import { packIsOlderThanCli } from "./packVersion.js";
|
|
30
47
|
import { packDir, readActivePack } from "./paths.js";
|
|
31
48
|
|
|
49
|
+
/**
|
|
50
|
+
* A built pack the boot decided to pass over: it was built by an older CLI, and
|
|
51
|
+
* the project declares no packs of its own, so the CLI's own snapshot answers
|
|
52
|
+
* instead. Carried on the decision (and onward on the booted session) so every
|
|
53
|
+
* surface that reports on the store can name the pack it is NOT reading.
|
|
54
|
+
*/
|
|
55
|
+
export interface IgnoredPack {
|
|
56
|
+
/** The passed-over pack's content hash (its cache directory name). */
|
|
57
|
+
readonly contentHash: string;
|
|
58
|
+
/** The CLI version that built it (its manifest `version`). */
|
|
59
|
+
readonly builtBy: string;
|
|
60
|
+
/** When it was built (its manifest `createdAt`). */
|
|
61
|
+
readonly builtAt: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
32
64
|
/** The resolved boot strategy. */
|
|
33
65
|
export type SourcesDecision =
|
|
34
66
|
| {
|
|
@@ -36,9 +68,37 @@ export type SourcesDecision =
|
|
|
36
68
|
readonly dir: string;
|
|
37
69
|
readonly contentHash: string;
|
|
38
70
|
}
|
|
39
|
-
| {
|
|
71
|
+
| {
|
|
72
|
+
readonly kind: "embedded";
|
|
73
|
+
/**
|
|
74
|
+
* Present only on the upgrade row: the built pack this project has, which
|
|
75
|
+
* an older CLI built and which the snapshot is answering in place of.
|
|
76
|
+
* Absent for an ordinary fresh install, which has no pack at all.
|
|
77
|
+
*/
|
|
78
|
+
readonly ignoredPack?: IgnoredPack;
|
|
79
|
+
}
|
|
40
80
|
| { readonly kind: "unavailable"; readonly reason: string };
|
|
41
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The one sentence every surface uses for a passed-over pack.
|
|
84
|
+
*
|
|
85
|
+
* ONE wording, in one place, because the three surfaces that say it
|
|
86
|
+
* (`sources status`, `doctor`'s pack refs row, and the recovery text a reader
|
|
87
|
+
* lands on) must not each invent their own account of the same state. It names
|
|
88
|
+
* the version and the day — the two facts that make the situation legible — and
|
|
89
|
+
* both ways out, because they are genuinely different choices: rebuild if you
|
|
90
|
+
* want your own packs back, reset if you want the shipped snapshot.
|
|
91
|
+
*
|
|
92
|
+
* @param ignored - The passed-over pack.
|
|
93
|
+
* @returns The sentence, without a trailing full stop.
|
|
94
|
+
*/
|
|
95
|
+
export function describeIgnoredPack(ignored: IgnoredPack): string {
|
|
96
|
+
return (
|
|
97
|
+
`a pack built by ${BIN_NAME} ${ignored.builtBy} on ${ignored.builtAt.slice(0, 10)} is ignored — ` +
|
|
98
|
+
`run \`${BIN_NAME} sources update\` to rebuild it or \`${BIN_NAME} sources reset\` to remove it`
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
42
102
|
/**
|
|
43
103
|
* Decide how (or whether) to boot the store — without any network or store I/O.
|
|
44
104
|
*
|
|
@@ -54,7 +114,28 @@ export function resolveSources(
|
|
|
54
114
|
const contentHash = readActivePack(cwd);
|
|
55
115
|
if (contentHash !== undefined) {
|
|
56
116
|
const dir = packDir(contentHash);
|
|
57
|
-
|
|
117
|
+
// The manifest is read ONCE and used twice — for completeness (the half of
|
|
118
|
+
// `packIsComplete` that is not the manifest itself) and for the builder
|
|
119
|
+
// version. The boot decision is evaluated on the storeless fast path, so
|
|
120
|
+
// asking the same ~1 KB file for the version separately would be a second
|
|
121
|
+
// read for a field the first read already had in hand.
|
|
122
|
+
const manifest = readManifest(dir);
|
|
123
|
+
if (manifest !== undefined && packArtifactsPresent(dir)) {
|
|
124
|
+
if (
|
|
125
|
+
layers.origins.packs === "default" &&
|
|
126
|
+
packIsOlderThanCli(manifest.version, VERSION)
|
|
127
|
+
) {
|
|
128
|
+
return {
|
|
129
|
+
kind: "embedded",
|
|
130
|
+
ignoredPack: {
|
|
131
|
+
contentHash,
|
|
132
|
+
builtBy: manifest.version,
|
|
133
|
+
builtAt: manifest.createdAt,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return { kind: "pack", dir, contentHash };
|
|
138
|
+
}
|
|
58
139
|
// Two different things, and the recovery text is the only thing a user has
|
|
59
140
|
// to go on: the cache evicted the pack, or the directory is there but does
|
|
60
141
|
// not hold a whole pack — the state EVERY install upgraded past the
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type { ConfigLayers } from "../config/types.js";
|
|
21
|
-
import {
|
|
21
|
+
import { callRecovery, PragmaError } from "../error/index.js";
|
|
22
|
+
import { BUILD_STORE_CALL } from "../spec/call.js";
|
|
22
23
|
import { resolveSources } from "./resolveSources.js";
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -32,10 +33,9 @@ import { resolveSources } from "./resolveSources.js";
|
|
|
32
33
|
*/
|
|
33
34
|
export function storeUnavailable(reason: string): PragmaError {
|
|
34
35
|
return PragmaError.storeUnavailable(`${reason}.`, {
|
|
35
|
-
recovery:
|
|
36
|
-
|
|
36
|
+
recovery: callRecovery(
|
|
37
|
+
BUILD_STORE_CALL,
|
|
37
38
|
"Build the local store from the configured packs.",
|
|
38
|
-
{ tool: "sources_update" },
|
|
39
39
|
),
|
|
40
40
|
});
|
|
41
41
|
}
|
|
@@ -67,6 +67,18 @@ export interface StoreSession {
|
|
|
67
67
|
* have changed since.
|
|
68
68
|
*/
|
|
69
69
|
readonly manifest: import("./graphpack/types.js").Manifest;
|
|
70
|
+
/**
|
|
71
|
+
* The project's OWN built pack, when the boot passed over it because an older
|
|
72
|
+
* CLI built it (see `resolveSources`' upgrade row). Present only on a session
|
|
73
|
+
* booted from the embedded snapshot in a project that has a pack; absent on
|
|
74
|
+
* every ordinary boot.
|
|
75
|
+
*
|
|
76
|
+
* Carried for the same reason `manifest` is: a read that was answered from
|
|
77
|
+
* the snapshot instead of the pack this directory built has to be able to say
|
|
78
|
+
* so, from the session it was answered from, without re-resolving a decision
|
|
79
|
+
* the caller could have changed since.
|
|
80
|
+
*/
|
|
81
|
+
readonly ignoredPack?: import("./resolveSources.js").IgnoredPack;
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
/**
|