@unbrained/pm-cli 2026.9.28 → 2026.9.30
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/.agents/skills/pm-developer/references/GRAPH_AND_RELATIONSHIPS.md +7 -6
- package/.agents/skills/pm-user/references/BACKLOG_SHAPING.md +1 -1
- package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +26 -3
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +26 -0
- package/dist/cli/error-guidance.js +1 -1
- package/dist/cli/help-json-payload.js +2 -2
- package/dist/cli/main.js +2 -2
- package/dist/cli/runtime/invocation-options.d.ts +3 -1
- package/dist/cli/runtime/invocation-options.js +1 -1
- package/dist/cli-bundle/bundle-manifest.json +152 -152
- package/dist/cli-bundle/chunks/{chunk-UT7GRGHY.js → chunk-6XTJLOKW.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-VMJLM7P2.js → chunk-6YEET7XI.js} +22 -22
- package/dist/cli-bundle/chunks/{chunk-NEUIVBFO.js → chunk-A6NDNRCK.js} +3 -3
- package/dist/cli-bundle/chunks/{chunk-GLH2LNRH.js → chunk-BNAT4EYR.js} +9 -9
- package/dist/cli-bundle/chunks/{chunk-CPOWTIIS.js → chunk-BTCE36JS.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-35DME35A.js → chunk-BX4LEJX4.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-A2G5OG6W.js → chunk-EGAIIWI3.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-FYTXVVZG.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-ZFHJP7YD.js → chunk-O23CQ5T4.js} +31 -29
- package/dist/cli-bundle/chunks/{chunk-NBDB3SRC.js → chunk-UIAH7UFK.js} +67 -67
- package/dist/cli-bundle/chunks/{chunk-OYCCWPBM.js → chunk-USTAFHIM.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-PR4UAMI5.js → chunk-XRN3JYNN.js} +85 -85
- package/dist/cli-bundle/chunks/{chunk-PLNNUAFZ.js → chunk-YM342ELM.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-N6RP6PSP.js → register-list-query-SYNBVMNG.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-CY7JGQE6.js → register-mutation-4GSUK23D.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-53HAD3QP.js → register-operations-7FCJUC4Z.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-NVX6374E.js → register-setup-YTWW2KL5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-BRLXKRI2.js → chunk-3CAZ7Z4R.js} +4 -4
- package/dist/cli-bundle/focused-chunks/{chunk-K7GSZ7YZ.js → chunk-4AORVUT4.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-AZA4AEOR.js → chunk-AKB3JN57.js} +33 -33
- package/dist/cli-bundle/focused-chunks/{chunk-OVVCXTIE.js → chunk-EFD4ATS7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IJXZQE3D.js → chunk-EVMH4SKN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IWLMQLBO.js → chunk-IDB6E3ZV.js} +28 -28
- package/dist/cli-bundle/focused-chunks/{chunk-YZB7IPR6.js → chunk-IQBFYQF2.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-BGWF6CMP.js → chunk-KZULQIAG.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-ZMQTIS3L.js → chunk-MOUCTR72.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-6IVQPNYN.js → chunk-OMBDUJWZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-DTLRXY77.js → chunk-RIJQQOEZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-A5O3VYWV.js → chunk-T4PZJQG4.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-V7OPZRL6.js → chunk-UPOMLSW7.js} +14 -14
- package/dist/cli-bundle/focused-chunks/{chunk-5KJXX2TZ.js → chunk-XB5Q2P7Z.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-ONDNR3VU.js → chunk-XUCXRZZZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-Y5Z3XNGY.js → chunk-ZJ3CLTV4.js} +2 -2
- package/dist/cli-bundle/main.js +22 -22
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +22 -21
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +38 -38
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +2 -2
- package/dist/core/extensions/extension-types.d.ts +13 -0
- package/dist/core/extensions/extension-types.js +1 -1
- package/dist/core/history/replay.js +1 -1
- package/dist/core/history/workspace-history.d.ts +17 -0
- package/dist/core/history/workspace-history.js +1 -1
- package/dist/core/output/mutation-projection.js +1 -1
- package/dist/core/telemetry/runtime.js +2 -2
- package/dist/sdk/cli-contracts/command-aliases.js +1 -1
- package/dist/sdk/extension-command-context.d.ts +1 -7
- package/dist/sdk/extension-command-context.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
- package/dist/sdk/index.d.ts +1 -0
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/lifecycle/plan-mutation-receipt.d.ts +53 -0
- package/dist/sdk/lifecycle/plan-mutation-receipt.js +2 -0
- package/dist/sdk/lifecycle/plan.d.ts +3 -0
- package/dist/sdk/lifecycle/plan.js +1 -1
- package/dist/sdk/lifecycle/update.d.ts +2 -0
- package/dist/sdk/lifecycle/update.js +1 -1
- package/dist/sdk/linked-artifacts.js +1 -1
- package/dist/sdk/merge/install.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/runtime/actions.js +1 -1
- package/dist/sdk/runtime-input.d.ts +2 -0
- package/dist/sdk/runtime-input.js +1 -1
- package/dist/sdk/runtime.js +1 -1
- package/dist/sdk/similarity-scoring.js +1 -1
- package/dist/sdk/structured-mutations.js +1 -1
- package/dist/sdk/telemetry.d.ts +2 -0
- package/dist/sdk/telemetry.js +3 -1
- package/dist/sdk/testing.js +1 -1
- package/docs/CLI_GRAMMAR.md +18 -1
- package/docs/ECOSYSTEM_PLANNING_REVIEW.md +270 -0
- package/docs/MERGE_SAFETY.md +11 -6
- package/docs/README.md +2 -0
- package/docs/RELEASE_RELIABILITY.md +26 -4
- package/docs/SDK_AUDITED_MUTATIONS.md +52 -0
- package/docs/SDK_EVIDENCE_TRACEABILITY.md +34 -0
- package/docs/agent-task-token-baseline.json +28 -2
- package/docs/agent-task-transcripts.json +137 -14
- package/docs/ecosystem-review-2026-09-28.json +3159 -0
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
- package/marketplace.json +2 -2
- package/package.json +1 -1
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/package.json +2 -2
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/package.json +2 -2
- package/plugins/pm-codex/plugin.json +1 -1
- package/dist/cli-bundle/chunks/chunk-YERBAGQD.js +0 -2
package/dist/sdk/testing.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fa9e95e5-7782-509d-96af-093a47daaed7")}catch(e2){}})();import{activateExtensions,deactivateExtensions,runAfterCommandHooks,runBeforeCommandHooks,runBeforeMutationHooks,runCommandHandler,runCommandOverride,runOnIndexHooks,runOnReadHooks,runOnWriteHooks,runParserOverride,runPreflightOverride,runRendererOverride,runServiceOverride}from"../core/extensions/loader.js";import{createDefaultExtensionGovernancePolicy}from"../core/extensions/extension-types.js";import{getMigrationRuntimeDefinition,resolveRegisteredSearchProvider,resolveRegisteredVectorStoreAdapter}from"../core/extensions/runtime-registrations.js";import{collectUsedExtensionCapabilities}from"../core/extensions/capability-usage.js";import{normalizeKnownExtensionCapability}from"../core/extensions/extension-capability-aliases.js";import{checkExtensionManifestCompatibility,describeExtensionBlueprint,lintExtensionBlueprint,preflightExtension}from"./compose.js";import{describeExtensionActivation}from"../core/extensions/activation-summary.js";import{lintProjectProfile}from"../core/profile/profile-lint.js";import{renderExtensionSurfaceMarkdown}from"../core/extensions/activation-summary-markdown.js";import{asPropertyRecord,resolveActivatablePropertyRecord}from"../core/shared/primitives.js";import{createExtensionCommandSdk}from"./extension-command-context.js";import{PmClient}from"./runtime.js";function registeredExpectationName(expectation,legacyValue){return expectation.name??legacyValue??""}const HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",before_mutation:"beforeMutation",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};const RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};function normalizeSdkIdentifier(value){return value.trim().toLowerCase()}function normalizeSdkProfileMatchKey(value){return normalizeSdkIdentifier(value).replaceAll("-","_")}function extensionNameSuffix(extensionName){return extensionName?` from extension "${extensionName}"`:""}function sortedUnique(values){return[...new Set(values)].sort((left,right)=>left.localeCompare(right))}function normalizeSdkCommandName(command){if(typeof command!=="string"){throw new Error('A command name string is required. Pass it via the options object, e.g. runRegisteredCommandForTest(activation, { command: "my command" }) \u2014 positional command arguments are not supported.')}return command.trim().toLowerCase().split(/\s+/).filter(part=>part.length>0).join(" ")}function formatAvailable(values){return values.length>0?values.join(", "):"(none)"}function assertStringSetIncludes(actual,expected,label){const actualValues=new Set(actual??[]);const missing=sortedUnique(expected.filter(value=>!actualValues.has(value)));if(missing.length>0){throw new Error(`Expected package manifest ${label} to include ${missing.join(", ")}; available: ${formatAvailable(sortedUnique(actual??[]))}`)}}function collectFlagLabels(flags){const labels=new Set;for(const flag of flags){if(typeof flag.long==="string"&&flag.long.trim().length>0){labels.add(flag.long.trim())}if(typeof flag.short==="string"&&flag.short.trim().length>0){labels.add(flag.short.trim())}}return labels}function readTestExtensionManifest(module){const moduleRecord=asPropertyRecord(module);if(!moduleRecord){return{}}const manifest=asPropertyRecord(moduleRecord.manifest);if(manifest){return manifest}const defaultExport=asPropertyRecord(moduleRecord.default);const defaultManifest=asPropertyRecord(defaultExport?.manifest);if(defaultManifest){return defaultManifest}if(defaultExport&&typeof moduleRecord.default!=="function"&&("name"in defaultExport||"capabilities"in defaultExport)){return defaultExport}if(typeof module!=="function"&&("name"in moduleRecord||"capabilities"in moduleRecord)){return moduleRecord}return{}}function resolveTestExtensionName(manifest,explicitName){return explicitName??(typeof manifest.name==="string"&&manifest.name.trim().length>0?manifest.name.trim():"test-extension")}function readTestExtensionCapabilities(manifest,options){if(Array.isArray(options.capabilities)){return[...options.capabilities]}if(Array.isArray(manifest.capabilities)){return manifest.capabilities.filter(capability=>typeof capability==="string")}return[]}function buildSingleExtensionLoadResult(module,manifest,identity){return{disabled_by_flag:false,roots:{global:"",project:""},configured_enabled:[],configured_disabled:[],discovered:[],effective:[],warnings:[],policy:identity.policy,failed:[],loaded:[{layer:identity.layer,directory:"",manifest_path:"",name:identity.name,version:typeof manifest.version==="string"?manifest.version:"0.0.0",entry:typeof manifest.entry==="string"?manifest.entry:"./index.js",priority:typeof manifest.priority==="number"?manifest.priority:0,entry_path:"",capabilities:identity.capabilities,manifest_version:typeof manifest.manifest_version==="number"?manifest.manifest_version:void 0,pm_min_version:typeof manifest.pm_min_version==="string"?manifest.pm_min_version:void 0,pm_max_version:typeof manifest.pm_max_version==="string"?manifest.pm_max_version:void 0,engines:manifest.engines,trusted:manifest.trusted,provenance:manifest.provenance,sandbox_profile:manifest.sandbox_profile,permissions:manifest.permissions,activation:manifest.activation,module}]}}async function activateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return activateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,options),policy:options.policy??createDefaultExtensionGovernancePolicy()}))}async function deactivateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return deactivateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,{}),policy:createDefaultExtensionGovernancePolicy()}),options.activation,options.deactivateTimeoutMs===void 0?{}:{deactivate_timeout_ms:options.deactivateTimeoutMs})}async function runRegisteredCommandForTest(commands,options){const command=normalizeSdkCommandName(options.command);if(command.length===0){throw new Error("Expected command name must be a non-empty string")}const hasHandler=commands.handlers.some(entry=>entry.command===command);if(!hasHandler){const available=sortedUnique(commands.handlers.map(entry=>entry.command));throw new Error(`Expected a registered command handler for "${command}" to invoke. Available command handlers: ${formatAvailable(available)}`)}if(options.sdk!==void 0&&options.sdkFactory!==void 0){throw new TypeError("runRegisteredCommandForTest accepts sdk or sdkFactory, not both")}const pmRoot=options.pmRoot??"";const global2={json:true,quiet:true,noPager:true,...options.global};const sdk=options.sdk??options.sdkFactory?.({pmRoot,global:global2})??(pmRoot.length>0?createExtensionCommandSdk(pmRoot,new PmClient({pmRoot})):void 0);return runCommandHandler(commands,{command,args:options.args?[...options.args]:[],options:options.options?{...options.options}:{},global:global2,pm_root:pmRoot,...sdk===void 0?{}:{sdk}})}async function runRegisteredMutationGuardForTest(hooks,options){if((hooks.beforeMutation?.length??0)===0){throw new Error("Expected a registered before_mutation hook to invoke")}await runBeforeMutationHooks(hooks,options.context)}async function runRegisteredHookForTest(hooks,options){const kind=options.kind;if(kind==="before_mutation"){throw new Error("Invoke before_mutation hooks with runRegisteredMutationGuardForTest so denials and failures retain fail-closed semantics.")}if((hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[kind]]?.length??0)===0){const populatedKinds=sortedUnique(Object.keys(RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD).filter(candidateKind=>(hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[candidateKind]]?.length??0)>0));throw new Error(`Expected a registered "${kind}" hook to invoke. Hook kinds with registrations: ${formatAvailable(populatedKinds)}`)}switch(options.kind){case"before_command":return runBeforeCommandHooks(hooks,options.context);case"after_command":return runAfterCommandHooks(hooks,options.context);case"on_read":return runOnReadHooks(hooks,options.context);case"on_write":return runOnWriteHooks(hooks,options.context);case"on_index":return runOnIndexHooks(hooks,options.context)}}async function runRegisteredParserOverrideForTest(parsers,context){const command=normalizeSdkCommandName(context.command);if(!parsers.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered parser override for command "${command}" to invoke. Available parser override commands: ${formatAvailable(sortedUnique(parsers.overrides.map(entry=>entry.command)))}`)}return runParserOverride(parsers,context)}async function runRegisteredPreflightOverrideForTest(preflight,context){if(preflight.overrides.length===0){throw new Error("Expected a registered preflight override to invoke, but none are registered.")}return runPreflightOverride(preflight,context)}async function runRegisteredCommandOverrideForTest(commands,context){const command=normalizeSdkCommandName(context.command);if(!commands.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered command override for command "${command}" to invoke. Available command override commands: ${formatAvailable(sortedUnique(commands.overrides.map(entry=>entry.command)))}`)}return runCommandOverride(commands,context)}async function runRegisteredRendererOverrideForTest(renderers,context){if(!renderers.overrides.some(entry=>entry.format===context.format)){throw new Error(`Expected a registered renderer override for format "${context.format}" to invoke. Available renderer override formats: ${formatAvailable(sortedUnique(renderers.overrides.map(entry=>entry.format)))}`)}return runRendererOverride(renderers,context)}async function runRegisteredServiceOverrideForTest(services,context){if(!services.overrides.some(entry=>entry.service===context.service)){throw new Error(`Expected a registered service override for service "${context.service}" to invoke. Available service override services: ${formatAvailable(sortedUnique(services.overrides.map(entry=>entry.service)))}`)}return runServiceOverride(services,context)}const SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS={query:["query"],embed:["embed"],embedBatch:["embedBatch","embed_batch"],queryExpansion:["queryExpansion","query_expansion"],rerank:["rerank"]};async function runRegisteredSearchProviderForTest(registrations,options){const registration=resolveRegisteredSearchProvider(registrations,options.provider);if(!registration){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected a registered search provider "${options.provider}" to invoke. Available search providers: ${formatAvailable(available)}`)}const runtimeDefinition=registration.runtime_definition;const operationFn=SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS[options.operation].map(key=>runtimeDefinition[key]).find(value=>typeof value==="function");if(typeof operationFn!=="function"){throw new Error(`Registered search provider "${options.provider}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredVectorStoreAdapterForTest(registrations,options){const registration=resolveRegisteredVectorStoreAdapter(registrations,options.adapter);if(!registration){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected a registered vector store adapter "${options.adapter}" to invoke. Available vector store adapters: ${formatAvailable(available)}`)}const operationFn=registration.runtime_definition[options.operation];if(typeof operationFn!=="function"){throw new Error(`Registered vector store adapter "${options.adapter}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredMigrationForTest(registrations,options){const migration=assertRegisteredMigration(registrations,{migration:options.migration,extensionName:options.extensionName});const run=getMigrationRuntimeDefinition(migration).run;if(typeof run!=="function"){throw new Error(`Registered migration "${options.migration}" does not implement a run function to invoke.`)}const declaredStatus=migration.definition.status;const context={id:migration.definition.id,command:"migration",layer:migration.layer,extension:migration.name,pm_root:options.pmRoot??"",status:(typeof declaredStatus==="string"?declaredStatus.trim().toLowerCase():"")||"pending"};return await run(context)}async function runRegisteredImporterForTest(activation,options){const importer=assertRegisteredImporter(activation.registrations,{importer:options.importer,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${importer.importer} import`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}async function runRegisteredExporterForTest(activation,options){const exporter=assertRegisteredExporter(activation.registrations,{exporter:options.exporter,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${exporter.exporter} export`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}function assertExtensionDeactivated(result,expectation={}){const expectedDeactivated=expectation.deactivated??1;if(result.deactivated!==expectedDeactivated){throw new Error(`Expected ${expectedDeactivated} extension${expectedDeactivated===1?"":"s"} to deactivate cleanly, but ${result.deactivated} did.`)}const expectedFailed=expectation.failed??0;if(result.failed.length!==expectedFailed){const detail=result.failed.map(failure=>`${failure.layer}:${failure.name} (${failure.error})`).join(", ");throw new Error(`Expected ${expectedFailed} teardown failure${expectedFailed===1?"":"s"}, but observed ${result.failed.length}${detail.length>0?`: ${detail}`:""}.`)}return result}function assertPackageManifest(manifest,expectation){if(expectation.packageName!==void 0&&manifest.package_name!==expectation.packageName){throw new Error(`Expected package manifest package_name to be "${expectation.packageName}"; received "${manifest.package_name??"(none)"}"`)}if(expectation.aliases!==void 0){assertStringSetIncludes(manifest.aliases,expectation.aliases,"aliases")}if(expectation.resources!==void 0){for(const[kind,expectedPaths]of Object.entries(expectation.resources)){assertStringSetIncludes(manifest.resources[kind],expectedPaths,`pm.${kind}`)}}return manifest}function assertRegisteredCommandContract(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const commandCandidates=registrations.commands.filter(entry=>entry.command===expectedCommand);const command=expectation.extensionName?commandCandidates.find(entry=>entry.name===expectation.extensionName):commandCandidates[0];if(!command){const available=registrations.commands.map(entry=>entry.command).sort((left,right)=>left.localeCompare(right));const extensionSuffix=expectation.extensionName?` from extension "${expectation.extensionName}"`:"";throw new Error(`Expected extension command "${expectedCommand}"${extensionSuffix} to be registered. Available commands: ${formatAvailable(available)}`)}if(expectation.action!==void 0&&command.action!==expectation.action){throw new Error(`Expected extension command "${expectedCommand}" action "${expectation.action}", received "${command.action}"`)}if(expectation.arguments!==void 0){const actualArguments=(command.arguments??[]).map(argument=>argument.name);const missingArguments=expectation.arguments.filter(argument=>!actualArguments.includes(argument));if(missingArguments.length>0){throw new Error(`Expected extension command "${expectedCommand}" arguments ${formatAvailable(expectation.arguments)}; missing ${formatAvailable(missingArguments)}; available ${formatAvailable(actualArguments)}`)}}const flags=registrations.flags.filter(entry=>entry.target_command===expectedCommand&&(expectation.extensionName===void 0||entry.name===expectation.extensionName)).flatMap(entry=>entry.flags);if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected extension command "${expectedCommand}" flags ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return{command,flags}}function assertRegisteredFlags(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.targetCommand));if(expectedCommand.length===0){throw new Error("Expected target command name must be a non-empty string")}const candidates=registrations.flags.filter(entry=>entry.target_command===expectedCommand);let registration;if(expectation.extensionName){registration=candidates.find(entry=>entry.name===expectation.extensionName)}else if(candidates.length===1){registration=candidates[0]}else if(candidates.length>1){const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}" matched multiple extensions: ${formatAvailable(availableExtensions)}. Specify extensionName to choose one registration.`)}if(!registration){const available=sortedUnique(registrations.flags.map(entry=>entry.target_command));const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available flag target commands: ${formatAvailable(available)}; matching extensions: ${formatAvailable(availableExtensions)}`)}if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(registration.flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected flags for target command "${expectedCommand}" to include ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return registration}function assertRegisteredHook(hooks,expectation){const field=HOOK_KIND_TO_REGISTRY_FIELD[expectation.kind];const candidates=hooks[field]??[];const hook=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!hook){const available=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected a "${expectation.kind}" hook${extensionNameSuffix(expectation.extensionName)} to be registered. Available "${expectation.kind}" hooks: ${formatAvailable(available)}`)}return hook}function assertRegisteredSearchProvider(registrations,expectation){const expectedProvider=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.provider));if(expectedProvider.length===0){throw new Error("Expected search provider name must be a non-empty string")}const candidates=registrations.search_providers.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedProvider);const provider=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!provider){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected search provider "${expectedProvider}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available search providers: ${formatAvailable(available)}`)}return provider}function assertRegisteredVectorStoreAdapter(registrations,expectation){const expectedAdapter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.adapter));if(expectedAdapter.length===0){throw new Error("Expected vector store adapter name must be a non-empty string")}const candidates=registrations.vector_store_adapters.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedAdapter);const adapter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!adapter){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected vector store adapter "${expectedAdapter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available vector store adapters: ${formatAvailable(available)}`)}return adapter}function assertRegisteredImporter(registrations,expectation){const expectedImporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.importer));if(expectedImporter.length===0){throw new Error("Expected importer name must be a non-empty string")}const candidates=registrations.importers.filter(entry=>normalizeSdkIdentifier(entry.importer)===expectedImporter);const importer=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!importer){const available=sortedUnique(registrations.importers.map(entry=>entry.importer));throw new Error(`Expected importer "${expectedImporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available importers: ${formatAvailable(available)}`)}return importer}function assertRegisteredExporter(registrations,expectation){const expectedExporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.exporter));if(expectedExporter.length===0){throw new Error("Expected exporter name must be a non-empty string")}const candidates=registrations.exporters.filter(entry=>normalizeSdkIdentifier(entry.exporter)===expectedExporter);const exporter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!exporter){const available=sortedUnique(registrations.exporters.map(entry=>entry.exporter));throw new Error(`Expected exporter "${expectedExporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available exporters: ${formatAvailable(available)}`)}return exporter}function assertRegisteredItemField(registrations,expectation){const expectedField=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.field));if(expectedField.length===0){throw new Error("Expected item field name must be a non-empty string")}const candidates=registrations.item_fields.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,field:registration.fields.find(field=>normalizeSdkIdentifier(field.name)===expectedField)})).filter(entry=>entry.field!==void 0);const match=candidates.find(entry=>expectation.type===void 0||entry.field.type===expectation.type);if(!match){const available=sortedUnique(registrations.item_fields.flatMap(entry=>entry.fields.map(field=>`${field.name}:${field.type}`)));throw new Error(`Expected item field "${expectedField}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item fields: ${formatAvailable(available)}`)}return match}function assertRegisteredItemType(registrations,expectation){const expectedType=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.itemType));if(expectedType.length===0){throw new Error("Expected item type name must be a non-empty string")}const candidates=registrations.item_types.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,itemType:registration.types.find(itemType=>normalizeSdkIdentifier(itemType.name)===expectedType)})).filter(entry=>entry.itemType!==void 0);const match=candidates.find(entry=>expectation.folder===void 0||entry.itemType.folder===expectation.folder);if(!match){const available=sortedUnique(registrations.item_types.flatMap(entry=>entry.types.map(itemType=>`${itemType.name}:${itemType.folder}`)));throw new Error(`Expected item type "${expectedType}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item types: ${formatAvailable(available)}`)}return match}function assertRegisteredProfile(registrations,expectation){const expectedProfile=normalizeSdkProfileMatchKey(registeredExpectationName(expectation,expectation.profile));if(expectedProfile.length===0){throw new Error("Expected profile name must be a non-empty string")}const match=registrations.profiles.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).find(entry=>normalizeSdkProfileMatchKey(entry.profile.name)===expectedProfile);if(!match){const available=sortedUnique(registrations.profiles.map(entry=>entry.profile.name));throw new Error(`Expected profile "${expectedProfile}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available profiles: ${formatAvailable(available)}`)}return{registration:match,profile:match.profile}}function assertRegisteredCommandOverride(commands,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=commands.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(commands.overrides.map(entry=>entry.command));throw new Error(`Expected command override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available command overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredParserOverride(parsers,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=parsers.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(parsers.overrides.map(entry=>entry.command));throw new Error(`Expected parser override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available parser overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredPreflightOverride(preflight,expectation={}){const override=expectation.extensionName?preflight.overrides.find(entry=>entry.name===expectation.extensionName):preflight.overrides[0];if(!override){const available=sortedUnique(preflight.overrides.map(entry=>entry.name));throw new Error(`Expected a preflight override${extensionNameSuffix(expectation.extensionName)} to be registered. Available preflight overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredRendererOverride(renderers,expectation){const expectedFormat=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.format));if(expectedFormat.length===0){throw new Error("Expected renderer format must be a non-empty string")}const candidates=renderers.overrides.filter(entry=>entry.format===expectedFormat);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(renderers.overrides.map(entry=>entry.format));throw new Error(`Expected renderer override "${expectedFormat}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available renderer overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredServiceOverride(services,expectation){const expectedService=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.service));if(expectedService.length===0){throw new Error("Expected service name must be a non-empty string")}const candidates=services.overrides.filter(entry=>normalizeSdkIdentifier(entry.service)===expectedService);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(services.overrides.map(entry=>entry.service));throw new Error(`Expected service override "${expectedService}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available service overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredMigration(registrations,expectation){const expectedMigration=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.migration));if(expectedMigration.length===0){throw new Error("Expected migration id must be a non-empty string")}const candidates=registrations.migrations.filter(entry=>(expectation.extensionName===void 0||entry.name===expectation.extensionName)&&typeof entry.definition.id==="string"&&normalizeSdkIdentifier(entry.definition.id)===expectedMigration);const match=candidates.find(entry=>expectation.mandatory===void 0||(entry.definition.mandatory??false)===expectation.mandatory);if(match){return match}const idMatch=candidates[0];if(idMatch!==void 0){throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to have mandatory=${expectation.mandatory}, but it is mandatory=${idMatch.definition.mandatory??false}.`)}const available=sortedUnique(registrations.migrations.map(entry=>{const id=typeof entry.definition.id==="string"&&entry.definition.id.trim().length>0?entry.definition.id.trim():"(unnamed)";return`${id}:${entry.definition.mandatory===true}`}));throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available migrations: ${formatAvailable(available)}`)}function assertExtensionCapabilityUsage(activation,expectation){const toKnownCapabilitySet=(capabilities,field)=>{const known=new Set;for(const capability of capabilities){const normalized=normalizeKnownExtensionCapability(capability);if(normalized===null){throw new Error(`Expected ${field} capability "${capability}" to be a known extension capability. Use canonical capability names (see manifest.capabilities).`)}known.add(normalized)}return known};const declared=[...toKnownCapabilitySet(expectation.declared,"declared")].sort((left,right)=>left.localeCompare(right));const allowUnused=toKnownCapabilitySet(expectation.allowUnused??[],"allowUnused");const used=collectUsedExtensionCapabilities(activation,{extensionName:expectation.extensionName});const usedSet=new Set(used);const unused=declared.filter(capability=>!usedSet.has(capability)&&!allowUnused.has(capability));if(unused.length>0){const scopeSuffix=extensionNameSuffix(expectation.extensionName);throw new Error(`Expected every declared capability${scopeSuffix} to be exercised, but [${unused.join(", ")}] ${unused.length===1?"is":"are"} declared yet never registered against. Remove ${unused.length===1?"it":"them"} from manifest.capabilities for least privilege, or pass allowUnused for capabilities registered only behind a runtime flag.`)}return{declared,used,unused}}function assertExtensionBlueprint(blueprint,options={}){const result=lintExtensionBlueprint(blueprint,options);if(!result.ok){const errors=result.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension blueprint failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
|
|
1
|
+
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="52ee6b9f-9465-52ec-9164-c5b21360d078")}catch(e2){}})();import{activateExtensions,deactivateExtensions,runAfterCommandHooks,runBeforeCommandHooks,runBeforeMutationHooks,runCommandHandler,runCommandOverride,runOnIndexHooks,runOnReadHooks,runOnWriteHooks,runParserOverride,runPreflightOverride,runRendererOverride,runServiceOverride}from"../core/extensions/loader.js";import{createDefaultExtensionGovernancePolicy}from"../core/extensions/extension-types.js";import{getMigrationRuntimeDefinition,resolveRegisteredSearchProvider,resolveRegisteredVectorStoreAdapter}from"../core/extensions/runtime-registrations.js";import{collectUsedExtensionCapabilities}from"../core/extensions/capability-usage.js";import{normalizeKnownExtensionCapability}from"../core/extensions/extension-capability-aliases.js";import{checkExtensionManifestCompatibility,describeExtensionBlueprint,lintExtensionBlueprint,preflightExtension}from"./compose.js";import{describeExtensionActivation}from"../core/extensions/activation-summary.js";import{lintProjectProfile}from"../core/profile/profile-lint.js";import{renderExtensionSurfaceMarkdown}from"../core/extensions/activation-summary-markdown.js";import{asPropertyRecord,resolveActivatablePropertyRecord}from"../core/shared/primitives.js";import{createExtensionCommandSdk}from"./extension-command-context.js";import{PmClient}from"./runtime.js";function registeredExpectationName(expectation,legacyValue){return expectation.name??legacyValue??""}const HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",before_mutation:"beforeMutation",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};const RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD={before_command:"beforeCommand",after_command:"afterCommand",on_read:"onRead",on_write:"onWrite",on_index:"onIndex"};function normalizeSdkIdentifier(value){return value.trim().toLowerCase()}function normalizeSdkProfileMatchKey(value){return normalizeSdkIdentifier(value).replaceAll("-","_")}function extensionNameSuffix(extensionName){return extensionName?` from extension "${extensionName}"`:""}function sortedUnique(values){return[...new Set(values)].sort((left,right)=>left.localeCompare(right))}function normalizeSdkCommandName(command){if(typeof command!=="string"){throw new Error('A command name string is required. Pass it via the options object, e.g. runRegisteredCommandForTest(activation, { command: "my command" }) \u2014 positional command arguments are not supported.')}return command.trim().toLowerCase().split(/\s+/).filter(part=>part.length>0).join(" ")}function formatAvailable(values){return values.length>0?values.join(", "):"(none)"}function assertStringSetIncludes(actual,expected,label){const actualValues=new Set(actual??[]);const missing=sortedUnique(expected.filter(value=>!actualValues.has(value)));if(missing.length>0){throw new Error(`Expected package manifest ${label} to include ${missing.join(", ")}; available: ${formatAvailable(sortedUnique(actual??[]))}`)}}function collectFlagLabels(flags){const labels=new Set;for(const flag of flags){if(typeof flag.long==="string"&&flag.long.trim().length>0){labels.add(flag.long.trim())}if(typeof flag.short==="string"&&flag.short.trim().length>0){labels.add(flag.short.trim())}}return labels}function readTestExtensionManifest(module){const moduleRecord=asPropertyRecord(module);if(!moduleRecord){return{}}const manifest=asPropertyRecord(moduleRecord.manifest);if(manifest){return manifest}const defaultExport=asPropertyRecord(moduleRecord.default);const defaultManifest=asPropertyRecord(defaultExport?.manifest);if(defaultManifest){return defaultManifest}if(defaultExport&&typeof moduleRecord.default!=="function"&&("name"in defaultExport||"capabilities"in defaultExport)){return defaultExport}if(typeof module!=="function"&&("name"in moduleRecord||"capabilities"in moduleRecord)){return moduleRecord}return{}}function resolveTestExtensionName(manifest,explicitName){return explicitName??(typeof manifest.name==="string"&&manifest.name.trim().length>0?manifest.name.trim():"test-extension")}function readTestExtensionCapabilities(manifest,options){if(Array.isArray(options.capabilities)){return[...options.capabilities]}if(Array.isArray(manifest.capabilities)){return manifest.capabilities.filter(capability=>typeof capability==="string")}return[]}function buildSingleExtensionLoadResult(module,manifest,identity){return{disabled_by_flag:false,roots:{global:"",project:""},configured_enabled:[],configured_disabled:[],discovered:[],effective:[],warnings:[],policy:identity.policy,failed:[],loaded:[{layer:identity.layer,directory:"",manifest_path:"",name:identity.name,version:typeof manifest.version==="string"?manifest.version:"0.0.0",entry:typeof manifest.entry==="string"?manifest.entry:"./index.js",priority:typeof manifest.priority==="number"?manifest.priority:0,entry_path:"",capabilities:identity.capabilities,manifest_version:typeof manifest.manifest_version==="number"?manifest.manifest_version:void 0,pm_min_version:typeof manifest.pm_min_version==="string"?manifest.pm_min_version:void 0,pm_max_version:typeof manifest.pm_max_version==="string"?manifest.pm_max_version:void 0,engines:manifest.engines,trusted:manifest.trusted,provenance:manifest.provenance,sandbox_profile:manifest.sandbox_profile,permissions:manifest.permissions,activation:manifest.activation,module}]}}async function activateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return activateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,options),policy:options.policy??createDefaultExtensionGovernancePolicy()}))}async function deactivateExtensionForTest(module,options={}){const manifest=readTestExtensionManifest(module);return deactivateExtensions(buildSingleExtensionLoadResult(module,manifest,{name:resolveTestExtensionName(manifest,options.name),layer:options.layer??"project",capabilities:readTestExtensionCapabilities(manifest,{}),policy:createDefaultExtensionGovernancePolicy()}),options.activation,options.deactivateTimeoutMs===void 0?{}:{deactivate_timeout_ms:options.deactivateTimeoutMs})}async function runRegisteredCommandForTest(commands,options){const command=normalizeSdkCommandName(options.command);if(command.length===0){throw new Error("Expected command name must be a non-empty string")}const hasHandler=commands.handlers.some(entry=>entry.command===command);if(!hasHandler){const available=sortedUnique(commands.handlers.map(entry=>entry.command));throw new Error(`Expected a registered command handler for "${command}" to invoke. Available command handlers: ${formatAvailable(available)}`)}if(options.sdk!==void 0&&options.sdkFactory!==void 0){throw new TypeError("runRegisteredCommandForTest accepts sdk or sdkFactory, not both")}const pmRoot=options.pmRoot??"";const global2={json:true,quiet:true,noPager:true,...options.global};const sdk=options.sdk??options.sdkFactory?.({pmRoot,global:global2})??(pmRoot.length>0?createExtensionCommandSdk(pmRoot,new PmClient({pmRoot}),void 0,command):void 0);return runCommandHandler(commands,{command,args:options.args?[...options.args]:[],options:options.options?{...options.options}:{},global:global2,pm_root:pmRoot,...sdk===void 0?{}:{sdk}})}async function runRegisteredMutationGuardForTest(hooks,options){if((hooks.beforeMutation?.length??0)===0){throw new Error("Expected a registered before_mutation hook to invoke")}await runBeforeMutationHooks(hooks,options.context)}async function runRegisteredHookForTest(hooks,options){const kind=options.kind;if(kind==="before_mutation"){throw new Error("Invoke before_mutation hooks with runRegisteredMutationGuardForTest so denials and failures retain fail-closed semantics.")}if((hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[kind]]?.length??0)===0){const populatedKinds=sortedUnique(Object.keys(RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD).filter(candidateKind=>(hooks[RUNNABLE_HOOK_KIND_TO_REGISTRY_FIELD[candidateKind]]?.length??0)>0));throw new Error(`Expected a registered "${kind}" hook to invoke. Hook kinds with registrations: ${formatAvailable(populatedKinds)}`)}switch(options.kind){case"before_command":return runBeforeCommandHooks(hooks,options.context);case"after_command":return runAfterCommandHooks(hooks,options.context);case"on_read":return runOnReadHooks(hooks,options.context);case"on_write":return runOnWriteHooks(hooks,options.context);case"on_index":return runOnIndexHooks(hooks,options.context)}}async function runRegisteredParserOverrideForTest(parsers,context){const command=normalizeSdkCommandName(context.command);if(!parsers.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered parser override for command "${command}" to invoke. Available parser override commands: ${formatAvailable(sortedUnique(parsers.overrides.map(entry=>entry.command)))}`)}return runParserOverride(parsers,context)}async function runRegisteredPreflightOverrideForTest(preflight,context){if(preflight.overrides.length===0){throw new Error("Expected a registered preflight override to invoke, but none are registered.")}return runPreflightOverride(preflight,context)}async function runRegisteredCommandOverrideForTest(commands,context){const command=normalizeSdkCommandName(context.command);if(!commands.overrides.some(entry=>entry.command===command)){throw new Error(`Expected a registered command override for command "${command}" to invoke. Available command override commands: ${formatAvailable(sortedUnique(commands.overrides.map(entry=>entry.command)))}`)}return runCommandOverride(commands,context)}async function runRegisteredRendererOverrideForTest(renderers,context){if(!renderers.overrides.some(entry=>entry.format===context.format)){throw new Error(`Expected a registered renderer override for format "${context.format}" to invoke. Available renderer override formats: ${formatAvailable(sortedUnique(renderers.overrides.map(entry=>entry.format)))}`)}return runRendererOverride(renderers,context)}async function runRegisteredServiceOverrideForTest(services,context){if(!services.overrides.some(entry=>entry.service===context.service)){throw new Error(`Expected a registered service override for service "${context.service}" to invoke. Available service override services: ${formatAvailable(sortedUnique(services.overrides.map(entry=>entry.service)))}`)}return runServiceOverride(services,context)}const SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS={query:["query"],embed:["embed"],embedBatch:["embedBatch","embed_batch"],queryExpansion:["queryExpansion","query_expansion"],rerank:["rerank"]};async function runRegisteredSearchProviderForTest(registrations,options){const registration=resolveRegisteredSearchProvider(registrations,options.provider);if(!registration){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected a registered search provider "${options.provider}" to invoke. Available search providers: ${formatAvailable(available)}`)}const runtimeDefinition=registration.runtime_definition;const operationFn=SEARCH_PROVIDER_OPERATION_DEFINITION_KEYS[options.operation].map(key=>runtimeDefinition[key]).find(value=>typeof value==="function");if(typeof operationFn!=="function"){throw new Error(`Registered search provider "${options.provider}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredVectorStoreAdapterForTest(registrations,options){const registration=resolveRegisteredVectorStoreAdapter(registrations,options.adapter);if(!registration){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected a registered vector store adapter "${options.adapter}" to invoke. Available vector store adapters: ${formatAvailable(available)}`)}const operationFn=registration.runtime_definition[options.operation];if(typeof operationFn!=="function"){throw new Error(`Registered vector store adapter "${options.adapter}" does not implement the "${options.operation}" operation.`)}return await operationFn(options.context)}async function runRegisteredMigrationForTest(registrations,options){const migration=assertRegisteredMigration(registrations,{migration:options.migration,extensionName:options.extensionName});const run=getMigrationRuntimeDefinition(migration).run;if(typeof run!=="function"){throw new Error(`Registered migration "${options.migration}" does not implement a run function to invoke.`)}const declaredStatus=migration.definition.status;const context={id:migration.definition.id,command:"migration",layer:migration.layer,extension:migration.name,pm_root:options.pmRoot??"",status:(typeof declaredStatus==="string"?declaredStatus.trim().toLowerCase():"")||"pending"};return await run(context)}async function runRegisteredImporterForTest(activation,options){const importer=assertRegisteredImporter(activation.registrations,{importer:options.importer,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${importer.importer} import`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}async function runRegisteredExporterForTest(activation,options){const exporter=assertRegisteredExporter(activation.registrations,{exporter:options.exporter,extensionName:options.extensionName});return runRegisteredCommandForTest(activation.commands,{command:`${exporter.exporter} export`,args:options.args,options:options.options,global:options.global,pmRoot:options.pmRoot})}function assertExtensionDeactivated(result,expectation={}){const expectedDeactivated=expectation.deactivated??1;if(result.deactivated!==expectedDeactivated){throw new Error(`Expected ${expectedDeactivated} extension${expectedDeactivated===1?"":"s"} to deactivate cleanly, but ${result.deactivated} did.`)}const expectedFailed=expectation.failed??0;if(result.failed.length!==expectedFailed){const detail=result.failed.map(failure=>`${failure.layer}:${failure.name} (${failure.error})`).join(", ");throw new Error(`Expected ${expectedFailed} teardown failure${expectedFailed===1?"":"s"}, but observed ${result.failed.length}${detail.length>0?`: ${detail}`:""}.`)}return result}function assertPackageManifest(manifest,expectation){if(expectation.packageName!==void 0&&manifest.package_name!==expectation.packageName){throw new Error(`Expected package manifest package_name to be "${expectation.packageName}"; received "${manifest.package_name??"(none)"}"`)}if(expectation.aliases!==void 0){assertStringSetIncludes(manifest.aliases,expectation.aliases,"aliases")}if(expectation.resources!==void 0){for(const[kind,expectedPaths]of Object.entries(expectation.resources)){assertStringSetIncludes(manifest.resources[kind],expectedPaths,`pm.${kind}`)}}return manifest}function assertRegisteredCommandContract(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const commandCandidates=registrations.commands.filter(entry=>entry.command===expectedCommand);const command=expectation.extensionName?commandCandidates.find(entry=>entry.name===expectation.extensionName):commandCandidates[0];if(!command){const available=registrations.commands.map(entry=>entry.command).sort((left,right)=>left.localeCompare(right));const extensionSuffix=expectation.extensionName?` from extension "${expectation.extensionName}"`:"";throw new Error(`Expected extension command "${expectedCommand}"${extensionSuffix} to be registered. Available commands: ${formatAvailable(available)}`)}if(expectation.action!==void 0&&command.action!==expectation.action){throw new Error(`Expected extension command "${expectedCommand}" action "${expectation.action}", received "${command.action}"`)}if(expectation.arguments!==void 0){const actualArguments=(command.arguments??[]).map(argument=>argument.name);const missingArguments=expectation.arguments.filter(argument=>!actualArguments.includes(argument));if(missingArguments.length>0){throw new Error(`Expected extension command "${expectedCommand}" arguments ${formatAvailable(expectation.arguments)}; missing ${formatAvailable(missingArguments)}; available ${formatAvailable(actualArguments)}`)}}const flags=registrations.flags.filter(entry=>entry.target_command===expectedCommand&&(expectation.extensionName===void 0||entry.name===expectation.extensionName)).flatMap(entry=>entry.flags);if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected extension command "${expectedCommand}" flags ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return{command,flags}}function assertRegisteredFlags(registrations,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.targetCommand));if(expectedCommand.length===0){throw new Error("Expected target command name must be a non-empty string")}const candidates=registrations.flags.filter(entry=>entry.target_command===expectedCommand);let registration;if(expectation.extensionName){registration=candidates.find(entry=>entry.name===expectation.extensionName)}else if(candidates.length===1){registration=candidates[0]}else if(candidates.length>1){const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}" matched multiple extensions: ${formatAvailable(availableExtensions)}. Specify extensionName to choose one registration.`)}if(!registration){const available=sortedUnique(registrations.flags.map(entry=>entry.target_command));const availableExtensions=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected flags for target command "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available flag target commands: ${formatAvailable(available)}; matching extensions: ${formatAvailable(availableExtensions)}`)}if(expectation.flags!==void 0){const actualFlagLabels=collectFlagLabels(registration.flags);const missingFlags=expectation.flags.filter(flag=>!actualFlagLabels.has(flag));if(missingFlags.length>0){throw new Error(`Expected flags for target command "${expectedCommand}" to include ${formatAvailable(expectation.flags)}; missing ${formatAvailable(missingFlags)}; available ${formatAvailable([...actualFlagLabels].sort((left,right)=>left.localeCompare(right)))}`)}}return registration}function assertRegisteredHook(hooks,expectation){const field=HOOK_KIND_TO_REGISTRY_FIELD[expectation.kind];const candidates=hooks[field]??[];const hook=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!hook){const available=sortedUnique(candidates.map(entry=>entry.name));throw new Error(`Expected a "${expectation.kind}" hook${extensionNameSuffix(expectation.extensionName)} to be registered. Available "${expectation.kind}" hooks: ${formatAvailable(available)}`)}return hook}function assertRegisteredSearchProvider(registrations,expectation){const expectedProvider=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.provider));if(expectedProvider.length===0){throw new Error("Expected search provider name must be a non-empty string")}const candidates=registrations.search_providers.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedProvider);const provider=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!provider){const available=sortedUnique(registrations.search_providers.map(entry=>entry.definition.name));throw new Error(`Expected search provider "${expectedProvider}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available search providers: ${formatAvailable(available)}`)}return provider}function assertRegisteredVectorStoreAdapter(registrations,expectation){const expectedAdapter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.adapter));if(expectedAdapter.length===0){throw new Error("Expected vector store adapter name must be a non-empty string")}const candidates=registrations.vector_store_adapters.filter(entry=>normalizeSdkIdentifier(entry.definition.name)===expectedAdapter);const adapter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!adapter){const available=sortedUnique(registrations.vector_store_adapters.map(entry=>entry.definition.name));throw new Error(`Expected vector store adapter "${expectedAdapter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available vector store adapters: ${formatAvailable(available)}`)}return adapter}function assertRegisteredImporter(registrations,expectation){const expectedImporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.importer));if(expectedImporter.length===0){throw new Error("Expected importer name must be a non-empty string")}const candidates=registrations.importers.filter(entry=>normalizeSdkIdentifier(entry.importer)===expectedImporter);const importer=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!importer){const available=sortedUnique(registrations.importers.map(entry=>entry.importer));throw new Error(`Expected importer "${expectedImporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available importers: ${formatAvailable(available)}`)}return importer}function assertRegisteredExporter(registrations,expectation){const expectedExporter=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.exporter));if(expectedExporter.length===0){throw new Error("Expected exporter name must be a non-empty string")}const candidates=registrations.exporters.filter(entry=>normalizeSdkIdentifier(entry.exporter)===expectedExporter);const exporter=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!exporter){const available=sortedUnique(registrations.exporters.map(entry=>entry.exporter));throw new Error(`Expected exporter "${expectedExporter}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available exporters: ${formatAvailable(available)}`)}return exporter}function assertRegisteredItemField(registrations,expectation){const expectedField=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.field));if(expectedField.length===0){throw new Error("Expected item field name must be a non-empty string")}const candidates=registrations.item_fields.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,field:registration.fields.find(field=>normalizeSdkIdentifier(field.name)===expectedField)})).filter(entry=>entry.field!==void 0);const match=candidates.find(entry=>expectation.type===void 0||entry.field.type===expectation.type);if(!match){const available=sortedUnique(registrations.item_fields.flatMap(entry=>entry.fields.map(field=>`${field.name}:${field.type}`)));throw new Error(`Expected item field "${expectedField}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item fields: ${formatAvailable(available)}`)}return match}function assertRegisteredItemType(registrations,expectation){const expectedType=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.itemType));if(expectedType.length===0){throw new Error("Expected item type name must be a non-empty string")}const candidates=registrations.item_types.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).map(registration=>({registration,itemType:registration.types.find(itemType=>normalizeSdkIdentifier(itemType.name)===expectedType)})).filter(entry=>entry.itemType!==void 0);const match=candidates.find(entry=>expectation.folder===void 0||entry.itemType.folder===expectation.folder);if(!match){const available=sortedUnique(registrations.item_types.flatMap(entry=>entry.types.map(itemType=>`${itemType.name}:${itemType.folder}`)));throw new Error(`Expected item type "${expectedType}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available item types: ${formatAvailable(available)}`)}return match}function assertRegisteredProfile(registrations,expectation){const expectedProfile=normalizeSdkProfileMatchKey(registeredExpectationName(expectation,expectation.profile));if(expectedProfile.length===0){throw new Error("Expected profile name must be a non-empty string")}const match=registrations.profiles.filter(entry=>expectation.extensionName===void 0||entry.name===expectation.extensionName).find(entry=>normalizeSdkProfileMatchKey(entry.profile.name)===expectedProfile);if(!match){const available=sortedUnique(registrations.profiles.map(entry=>entry.profile.name));throw new Error(`Expected profile "${expectedProfile}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available profiles: ${formatAvailable(available)}`)}return{registration:match,profile:match.profile}}function assertRegisteredCommandOverride(commands,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=commands.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(commands.overrides.map(entry=>entry.command));throw new Error(`Expected command override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available command overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredParserOverride(parsers,expectation){const expectedCommand=normalizeSdkCommandName(registeredExpectationName(expectation,expectation.command));if(expectedCommand.length===0){throw new Error("Expected command name must be a non-empty string")}const candidates=parsers.overrides.filter(entry=>entry.command===expectedCommand);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(parsers.overrides.map(entry=>entry.command));throw new Error(`Expected parser override "${expectedCommand}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available parser overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredPreflightOverride(preflight,expectation={}){const override=expectation.extensionName?preflight.overrides.find(entry=>entry.name===expectation.extensionName):preflight.overrides[0];if(!override){const available=sortedUnique(preflight.overrides.map(entry=>entry.name));throw new Error(`Expected a preflight override${extensionNameSuffix(expectation.extensionName)} to be registered. Available preflight overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredRendererOverride(renderers,expectation){const expectedFormat=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.format));if(expectedFormat.length===0){throw new Error("Expected renderer format must be a non-empty string")}const candidates=renderers.overrides.filter(entry=>entry.format===expectedFormat);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(renderers.overrides.map(entry=>entry.format));throw new Error(`Expected renderer override "${expectedFormat}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available renderer overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredServiceOverride(services,expectation){const expectedService=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.service));if(expectedService.length===0){throw new Error("Expected service name must be a non-empty string")}const candidates=services.overrides.filter(entry=>normalizeSdkIdentifier(entry.service)===expectedService);const override=expectation.extensionName?candidates.find(entry=>entry.name===expectation.extensionName):candidates[0];if(!override){const available=sortedUnique(services.overrides.map(entry=>entry.service));throw new Error(`Expected service override "${expectedService}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available service overrides: ${formatAvailable(available)}`)}return override}function assertRegisteredMigration(registrations,expectation){const expectedMigration=normalizeSdkIdentifier(registeredExpectationName(expectation,expectation.migration));if(expectedMigration.length===0){throw new Error("Expected migration id must be a non-empty string")}const candidates=registrations.migrations.filter(entry=>(expectation.extensionName===void 0||entry.name===expectation.extensionName)&&typeof entry.definition.id==="string"&&normalizeSdkIdentifier(entry.definition.id)===expectedMigration);const match=candidates.find(entry=>expectation.mandatory===void 0||(entry.definition.mandatory??false)===expectation.mandatory);if(match){return match}const idMatch=candidates[0];if(idMatch!==void 0){throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to have mandatory=${expectation.mandatory}, but it is mandatory=${idMatch.definition.mandatory??false}.`)}const available=sortedUnique(registrations.migrations.map(entry=>{const id=typeof entry.definition.id==="string"&&entry.definition.id.trim().length>0?entry.definition.id.trim():"(unnamed)";return`${id}:${entry.definition.mandatory===true}`}));throw new Error(`Expected migration "${expectedMigration}"${extensionNameSuffix(expectation.extensionName)} to be registered. Available migrations: ${formatAvailable(available)}`)}function assertExtensionCapabilityUsage(activation,expectation){const toKnownCapabilitySet=(capabilities,field)=>{const known=new Set;for(const capability of capabilities){const normalized=normalizeKnownExtensionCapability(capability);if(normalized===null){throw new Error(`Expected ${field} capability "${capability}" to be a known extension capability. Use canonical capability names (see manifest.capabilities).`)}known.add(normalized)}return known};const declared=[...toKnownCapabilitySet(expectation.declared,"declared")].sort((left,right)=>left.localeCompare(right));const allowUnused=toKnownCapabilitySet(expectation.allowUnused??[],"allowUnused");const used=collectUsedExtensionCapabilities(activation,{extensionName:expectation.extensionName});const usedSet=new Set(used);const unused=declared.filter(capability=>!usedSet.has(capability)&&!allowUnused.has(capability));if(unused.length>0){const scopeSuffix=extensionNameSuffix(expectation.extensionName);throw new Error(`Expected every declared capability${scopeSuffix} to be exercised, but [${unused.join(", ")}] ${unused.length===1?"is":"are"} declared yet never registered against. Remove ${unused.length===1?"it":"them"} from manifest.capabilities for least privilege, or pass allowUnused for capabilities registered only behind a runtime flag.`)}return{declared,used,unused}}function assertExtensionBlueprint(blueprint,options={}){const result=lintExtensionBlueprint(blueprint,options);if(!result.ok){const errors=result.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension blueprint failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
|
|
2
2
|
`+errors.map(finding=>` - [${finding.code}] ${finding.message}`).join("\n"))}return result}function assertProjectProfile(profile,options={}){const report=lintProjectProfile(profile);const blocking=report.findings.filter(finding=>finding.severity==="error"||options.strict===true&&finding.severity==="warning");if(blocking.length>0){throw new Error(`Project profile "${report.profile}" failed lint with ${blocking.length} ${blocking.length===1?"issue":"issues"}:
|
|
3
3
|
`+blocking.map(finding=>` - ${finding.severity} [${finding.code}] ${finding.message}`).join("\n"))}return report}function assertExtensionManifestMatchesBlueprint(manifest,blueprint){const declaredCapabilities=Array.isArray(manifest.capabilities)?manifest.capabilities:[];const result=lintExtensionBlueprint(blueprint,{declaredCapabilities});const declared=result.declared;const declaredSet=new Set(declared);const usedSet=new Set(result.used);const missing=result.used.filter(capability=>!declaredSet.has(capability));const unused=declared.filter(capability=>!usedSet.has(capability));if(missing.length>0||unused.length>0){const parts=[];if(missing.length>0){parts.push(`missing [${missing.join(", ")}] (the blueprint registers ${missing.length===1?"a surface":"surfaces"} requiring ${missing.length===1?"it":"them"}; activation throws extension_capability_missing)`)}if(unused.length>0){parts.push(`unused [${unused.join(", ")}] (declared but no surface exercises ${unused.length===1?"it":"them"}; drop for least privilege)`)}throw new Error(`Manifest capabilities do not match the blueprint: ${parts.join("; ")}. Set capabilities to synthesizeExtensionManifest(blueprint, identity).capabilities to stay in sync.`)}return{used:result.used,declared,missing,unused,findings:result.findings}}function assertExtensionManifestCompatible(manifest,target){const result=checkExtensionManifestCompatibility(manifest,target);const blocking=result.findings.filter(finding=>finding.severity==="error");if(blocking.length>0){throw new Error(`Extension manifest is not compatible with pm ${result.pmVersion}: ${blocking.map(finding=>finding.message).join("; ")}`)}return result}function assertExtensionPreflight(blueprint,options={}){const report=preflightExtension(blueprint,options);if(!report.ok){const errors=report.findings.filter(finding=>finding.severity==="error");throw new Error(`Extension failed preflight with ${errors.length} ${errors.length===1?"error":"errors"}:
|
|
4
4
|
`+errors.map(finding=>` - [${finding.source}:${finding.code}] ${finding.message}`).join("\n"))}return report}function assertTestModuleHasActivateExport(module){if(resolveActivatablePropertyRecord(module)){return}throw new Error("createExtensionTestHarness received a module with no activate export. Pass the extension module (with an activate(api) function on the module or its default export) as the first argument, and options second.")}async function createExtensionTestHarness(module,options={}){assertTestModuleHasActivateExport(module);const manifest=readTestExtensionManifest(module);const name=resolveTestExtensionName(manifest,options.name);const layer=options.layer??"project";const activation=await activateExtensionForTest(module,options);if(activation.failed.length>0){const detail=activation.failed.map(failure=>{const missingCapability=failure.trace?.missing_capability;const reason=missingCapability?`${failure.error}; missing capability "${missingCapability}"`:failure.error;return`${failure.layer}:${failure.name} (${reason})`}).join(", ");throw new Error(`createExtensionTestHarness could not activate the extension cleanly: ${detail}. Declare the required capability in the manifest (or pass it via options.capabilities), or use activateExtensionForTest directly to inspect a failed activation.`)}return{module,name,layer,activation,activationSummary(describeOptions){return describeExtensionActivation(activation,describeOptions)},renderMarkdown(markdownOptions={}){const{extensionName,...renderOptions}=markdownOptions??{};return renderExtensionSurfaceMarkdown(describeExtensionActivation(activation,{extensionName}),renderOptions)},assertCommandContract(expectation){return assertRegisteredCommandContract(activation.registrations,expectation)},assertFlags(expectation){return assertRegisteredFlags(activation.registrations,expectation)},assertItemField(expectation){return assertRegisteredItemField(activation.registrations,expectation)},assertItemType(expectation){return assertRegisteredItemType(activation.registrations,expectation)},assertProfile(expectation){return assertRegisteredProfile(activation.registrations,expectation)},assertHook(expectation){return assertRegisteredHook(activation.hooks,expectation)},assertCommandOverride(expectation){return assertRegisteredCommandOverride(activation.commands,expectation)},assertParserOverride(expectation){return assertRegisteredParserOverride(activation.parsers,expectation)},assertPreflightOverride(expectation){return assertRegisteredPreflightOverride(activation.preflight,expectation)},assertRendererOverride(expectation){return assertRegisteredRendererOverride(activation.renderers,expectation)},assertServiceOverride(expectation){return assertRegisteredServiceOverride(activation.services,expectation)},assertSearchProvider(expectation){return assertRegisteredSearchProvider(activation.registrations,expectation)},assertVectorStoreAdapter(expectation){return assertRegisteredVectorStoreAdapter(activation.registrations,expectation)},assertImporter(expectation){return assertRegisteredImporter(activation.registrations,expectation)},assertExporter(expectation){return assertRegisteredExporter(activation.registrations,expectation)},assertMigration(expectation){return assertRegisteredMigration(activation.registrations,expectation)},assertCapabilityUsage(expectation){return assertExtensionCapabilityUsage(activation,expectation)},runCommand(runOptions){return runRegisteredCommandForTest(activation.commands,runOptions)},runHook(runOptions){return runRegisteredHookForTest(activation.hooks,runOptions)},runMutationGuard(runOptions){return runRegisteredMutationGuardForTest(activation.hooks,runOptions)},runCommandOverride(context){return runRegisteredCommandOverrideForTest(activation.commands,context)},runParserOverride(context){return runRegisteredParserOverrideForTest(activation.parsers,context)},runPreflightOverride(context){return runRegisteredPreflightOverrideForTest(activation.preflight,context)},runRendererOverride(context){return runRegisteredRendererOverrideForTest(activation.renderers,context)},runServiceOverride(context){return runRegisteredServiceOverrideForTest(activation.services,context)},runSearchProvider(runOptions){return runRegisteredSearchProviderForTest(activation.registrations,runOptions)},runVectorStoreAdapter(runOptions){return runRegisteredVectorStoreAdapterForTest(activation.registrations,runOptions)},runMigration(runOptions){return runRegisteredMigrationForTest(activation.registrations,runOptions)},runImporter(runOptions){return runRegisteredImporterForTest(activation,runOptions)},runExporter(runOptions){return runRegisteredExporterForTest(activation,runOptions)},deactivate(deactivateOptions={}){return deactivateExtensionForTest(module,{name,layer,activation,deactivateTimeoutMs:deactivateOptions.deactivateTimeoutMs})}}}export{activateExtensionForTest,assertExtensionBlueprint,assertExtensionCapabilityUsage,assertExtensionDeactivated,assertExtensionManifestCompatible,assertExtensionManifestMatchesBlueprint,assertExtensionPreflight,assertPackageManifest,assertProjectProfile,assertRegisteredCommandContract,assertRegisteredCommandOverride,assertRegisteredExporter,assertRegisteredFlags,assertRegisteredHook,assertRegisteredImporter,assertRegisteredItemField,assertRegisteredItemType,assertRegisteredMigration,assertRegisteredParserOverride,assertRegisteredPreflightOverride,assertRegisteredProfile,assertRegisteredRendererOverride,assertRegisteredSearchProvider,assertRegisteredServiceOverride,assertRegisteredVectorStoreAdapter,createExtensionTestHarness,deactivateExtensionForTest,describeExtensionActivation,describeExtensionBlueprint,lintExtensionBlueprint,renderExtensionSurfaceMarkdown,runRegisteredCommandForTest,runRegisteredCommandOverrideForTest,runRegisteredExporterForTest,runRegisteredHookForTest,runRegisteredImporterForTest,runRegisteredMigrationForTest,runRegisteredMutationGuardForTest,runRegisteredParserOverrideForTest,runRegisteredPreflightOverrideForTest,runRegisteredRendererOverrideForTest,runRegisteredSearchProviderForTest,runRegisteredServiceOverrideForTest,runRegisteredVectorStoreAdapterForTest};
|
package/docs/CLI_GRAMMAR.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Noun–Verb CLI Grammar and Compatibility Policy
|
|
2
2
|
|
|
3
|
-
Tracked by [pm-pbyu](../.agents/pm/decisions/pm-pbyu.toon), implemented through [pm-0z7n](../.agents/pm/features/pm-0z7n.toon), [pm-pfqi](../.agents/pm/tasks/pm-pfqi.toon), [pm-yy8rmx](../.agents/pm/tasks/pm-yy8rmx.toon), [pm-wt43zj](../.agents/pm/tasks/pm-wt43zj.toon), [pm-e2bq](../.agents/pm/features/pm-e2bq.toon), and [pm-yql1](../.agents/pm/tasks/pm-yql1.toon).
|
|
3
|
+
Tracked by [pm-pbyu](../.agents/pm/decisions/pm-pbyu.toon), implemented through [pm-0z7n](../.agents/pm/features/pm-0z7n.toon), [pm-0mox](../.agents/pm/tasks/pm-0mox.toon), [pm-pfqi](../.agents/pm/tasks/pm-pfqi.toon), [pm-yy8rmx](../.agents/pm/tasks/pm-yy8rmx.toon), [pm-wt43zj](../.agents/pm/tasks/pm-wt43zj.toon), [pm-e2bq](../.agents/pm/features/pm-e2bq.toon), and [pm-yql1](../.agents/pm/tasks/pm-yql1.toon).
|
|
4
4
|
|
|
5
5
|
## Agent Quick Context
|
|
6
6
|
|
|
@@ -98,6 +98,23 @@ pm config project set ux_deprecation_hints false
|
|
|
98
98
|
|
|
99
99
|
This setting changes presentation only. It does not disable aliases or change command results. The uniform machine result-envelope receipt is intentionally deferred to its separately tracked cross-command contract so this implementation does not create a list-only shape.
|
|
100
100
|
|
|
101
|
+
### Removal evidence
|
|
102
|
+
|
|
103
|
+
`pm telemetry stats --json` reports `alias_usage` (declared deprecated spelling,
|
|
104
|
+
canonical target, retained hit count, and last seen time), `zero_use_aliases`,
|
|
105
|
+
and `alias_usage_retention_days`. These counts are held only in the local
|
|
106
|
+
telemetry runtime state when telemetry consent is enabled. The same process
|
|
107
|
+
opt-outs used by telemetry suppress recording. Diagnostics reads and new alias
|
|
108
|
+
hits prune expired observations from disk; `pm telemetry clear` removes them
|
|
109
|
+
immediately. Canonical spellings do
|
|
110
|
+
not write an alias counter.
|
|
111
|
+
|
|
112
|
+
An empty local counter is not evidence that an alias is unused across all
|
|
113
|
+
workspaces. The open GA stability decision [pm-agou](../.agents/pm/decisions/pm-agou.toon)
|
|
114
|
+
must set the number of releases with zero observed use across reporting
|
|
115
|
+
workspaces before any removal at a major version boundary. Until that decision
|
|
116
|
+
and cross-workspace evidence exist, published aliases remain executable.
|
|
117
|
+
|
|
101
118
|
## Enforcement
|
|
102
119
|
|
|
103
120
|
`pnpm quality:command-grammar` builds the SDK, reads the live runtime command contracts, and compares them with the checked-in destination and alias tables. Package-owned destination rows are conditional because installed packages vary by workspace; every activated package command still requires a declared row, while an inactive package does not make its row stale. The gate also proves parity between discoverable SDK actions, the MCP `pm_run` action enum, and narrow MCP tools. The same gate runs inside `quality:static`, alongside contract drift checks. Its negative controls prove that unknown commands, stale core rows, broken alias targets, MCP drift, and surface-ceiling growth fail upward.
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Ecosystem planning review — 2026-09-28
|
|
2
|
+
|
|
3
|
+
Tracked by [pm-p9a4](../.agents/pm/tasks/pm-p9a4.toon),
|
|
4
|
+
[pm-m08hza](../.agents/pm/decisions/pm-m08hza.toon),
|
|
5
|
+
[pm-1jzupz](../.agents/pm/features/pm-1jzupz.toon), and
|
|
6
|
+
[pm-wg07](../.agents/pm/chores/pm-wg07.toon).
|
|
7
|
+
|
|
8
|
+
Project management = context management. This review records a complete
|
|
9
|
+
structural census, targeted semantic review, measured operational checks, and
|
|
10
|
+
planning decisions on existing canonical items. It does not certify that every
|
|
11
|
+
historical sentence is correct or that every future capability is implemented.
|
|
12
|
+
Read the live item before acting on this dated evidence.
|
|
13
|
+
|
|
14
|
+
## Population and evidence
|
|
15
|
+
|
|
16
|
+
The strict CLI snapshot at `2026-09-28T05:40:12.139Z` contains 2,826 items:
|
|
17
|
+
2,532 closed, 221 open, 59 draft, and 14 canceled. No records or fields were
|
|
18
|
+
omitted, and no item or directory was unreadable. Blocking is also a derived
|
|
19
|
+
dependency condition: the initial orientation reported 60 blocked open items,
|
|
20
|
+
although no item used a literal `blocked` lifecycle status.
|
|
21
|
+
|
|
22
|
+
The cursor-resumed event read collected 68,876 complete events from 2,841
|
|
23
|
+
streams through `2026-09-28T05:41:59.143Z`, including deleted-item and workspace
|
|
24
|
+
history. All 23 Plan items also received deep plan reads for steps, decisions,
|
|
25
|
+
validation and resume metadata. This later cutoff includes the first review claim/comment events;
|
|
26
|
+
it is not silently equated with the earlier item snapshot. History-drift
|
|
27
|
+
validation passed across all 2,826 current items. The public SDK independently
|
|
28
|
+
verified hash chains for all 2,841 collected streams with zero failures.
|
|
29
|
+
|
|
30
|
+
[The per-item census](ecosystem-review-2026-09-28.json) records every snapshot
|
|
31
|
+
ID, status, content fingerprint, structural classification, missing planning
|
|
32
|
+
fields, historical evidence gaps, and remediation owner. These are mechanical
|
|
33
|
+
findings, not invented historical estimates, outcomes, or review approvals.
|
|
34
|
+
The full raw corpus stays outside public documentation.
|
|
35
|
+
|
|
36
|
+
| Measure | Initial observation | Interpretation |
|
|
37
|
+
| --- | ---: | --- |
|
|
38
|
+
| Directed graph edges | 14,097 | Deduplicated directed basis |
|
|
39
|
+
| Ordering layers / critical-path edges | 12 / 11 | Acyclic prerequisite graph |
|
|
40
|
+
| Missing graph endpoints / isolated items | 0 / 0 | Across all statuses |
|
|
41
|
+
| Active / terminal outcome-unreachable items | 0 / 0 | Typed reachability basis |
|
|
42
|
+
| Generic `related` edges | 5,589 | 39.65% of directed edges |
|
|
43
|
+
| Specialized semantic edges | 1,885 | Discovery, recurrence, incident, verification, supersession |
|
|
44
|
+
| Items without a specialized semantic edge | 1,463 | 86 active, 1,377 terminal; not necessarily invalid |
|
|
45
|
+
| Legacy hierarchy exceptions | 7 children | Existing informational cardinality/direction findings |
|
|
46
|
+
| Closed items missing structured resolution evidence | 170 | Preserved residual, not a new regression |
|
|
47
|
+
| Active items missing required planning fields | 1 | Sentry major-upgrade item, repaired in this review |
|
|
48
|
+
| Tracked non-tracker artifacts with exact item links | 1,921 / 1,921 | Path ownership, not proof for every code change |
|
|
49
|
+
| Duplicate candidate clusters | 28 | Reviewed candidates, not 28 duplicate obligations |
|
|
50
|
+
|
|
51
|
+
Scanner records with different provider identities, parent/child acceptance
|
|
52
|
+
slices, distinct incidents and dated release executions remain separate. The
|
|
53
|
+
three identically titled accidental probes are already canceled. Similar audit
|
|
54
|
+
titles require their own event/acceptance comparison before any consolidation.
|
|
55
|
+
No existing item was deleted or closed merely because it matched a title.
|
|
56
|
+
|
|
57
|
+
## Graph organization
|
|
58
|
+
|
|
59
|
+
Use one structural parent and outcome paths for scope, `implements` for delivery,
|
|
60
|
+
`blocked_by` for an actual prerequisite, `discovered_from` for provenance,
|
|
61
|
+
`verifies` for verification responsibility, and `recurs_from` only for an
|
|
62
|
+
established recurring mechanism. Maintain prior evidence when correcting a link.
|
|
63
|
+
A declaration that an item will verify something is not a passing test result.
|
|
64
|
+
|
|
65
|
+
The native reconstruction plan retains its original execution obligations;
|
|
66
|
+
overlapping newly added steps are explicitly superseded by their existing owners,
|
|
67
|
+
and a dated census checkpoint records the completed refresh.
|
|
68
|
+
|
|
69
|
+
The planning pass restores explicit missing prerequisites in prospective
|
|
70
|
+
reminders, cross-branch relevance and external-package verification. The
|
|
71
|
+
historical batch restores three `discovered_from` edges to the governance run
|
|
72
|
+
whose original comment explicitly named the discoveries. Their release and
|
|
73
|
+
closure evidence remain unchanged. Generic links and transitive ordering
|
|
74
|
+
shortcuts are not added to meet density or depth targets.
|
|
75
|
+
|
|
76
|
+
[pm-qnl2](../.agents/pm/tasks/pm-qnl2.toon) owns active enrichment;
|
|
77
|
+
[pm-lnrk](../.agents/pm/tasks/pm-lnrk.toon) owns historical batches;
|
|
78
|
+
[pm-pj2isb](../.agents/pm/chores/pm-pj2isb.toon) owns misleading placeholder
|
|
79
|
+
metadata. The completed [pm-e9zh](../.agents/pm/chores/pm-e9zh.toon) preserves
|
|
80
|
+
its explicitly unrecoverable 170-item residual. A missing historical observation
|
|
81
|
+
must remain unknown until independent evidence is found.
|
|
82
|
+
|
|
83
|
+
After this pass the live graph contains 2,829 items and 14,118 directed edges,
|
|
84
|
+
including 1,894 specialized semantic edges. Generic `related` is 5,590 after one
|
|
85
|
+
reviewed correction from causal provenance to non-causal coordination; no dangling endpoints, isolated items or ordering contradictions were
|
|
86
|
+
introduced. Outcome reachability remains 100%. The 1,458 records without a
|
|
87
|
+
specialized semantic edge remain visible for evidence review; absence alone
|
|
88
|
+
does not justify inventing a relationship.
|
|
89
|
+
|
|
90
|
+
## Horizon and outcome organization
|
|
91
|
+
|
|
92
|
+
These are planning windows, not delivery promises. Capacity, prerequisites and
|
|
93
|
+
measured evidence govern promotion. Priority measures impact; horizon measures
|
|
94
|
+
readiness and uncertainty. Review monthly and when a material observation changes.
|
|
95
|
+
|
|
96
|
+
| Horizon | Illustrative window | Canonical outcomes and promotion evidence |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| Current | 0–3 months | [Agent-legible grammar](../.agents/pm/milestones/pm-j82fd3.toon), active readiness, retrieval failures, release diagnosis and current protocol proof. Require completed-task token comparisons, preserved compatibility and actual installed-consumer evidence. |
|
|
99
|
+
| Emerging | 3–12 months | [Provable history](../.agents/pm/milestones/pm-w7ccmv.toon), reproducible observations and [measured self-improvement](../.agents/pm/milestones/pm-zzylb9.toon). Require independent replay, mutation coverage and three consecutive measured improvement cycles. |
|
|
100
|
+
| Expansion | 12–36 months | [Proven scale](../.agents/pm/milestones/pm-o87av6.toon), [universal domain platform](../.agents/pm/milestones/pm-t4d7nz.toon), [federated agents](../.agents/pm/milestones/pm-z1kr34.toon). Require published million-item/fleet distributions and SDK-only domain exemplars. |
|
|
101
|
+
| Exploratory | 36+ months | [Multi-decade continuity](../.agents/pm/milestones/pm-ljrh29.toon) and independently evaluated cognitive mechanisms. Require explicit architecture decisions, migration proofs, calibrated uncertainty and rejection criteria. |
|
|
102
|
+
|
|
103
|
+
The [SDK-complete outcome](../.agents/pm/milestones/pm-9rgaal.toon) and
|
|
104
|
+
[GA stability contract](../.agents/pm/milestones/pm-xp1xsw.toon) retain their own
|
|
105
|
+
acceptance boundaries. Earlier SDK promotion work remains completed history;
|
|
106
|
+
future integrations must use published SDK contracts and expose gaps as individual
|
|
107
|
+
items. New names or parallel roadmap trees do not improve traceability.
|
|
108
|
+
|
|
109
|
+
## Concrete planning refinements
|
|
110
|
+
|
|
111
|
+
- **Command simplicity:** root help already has thirteen entries including help.
|
|
112
|
+
Evaluate the remaining grammar through complete orient/retrieve/claim/mutate/
|
|
113
|
+
verify/close and recovery journeys. Preserve working aliases and capabilities.
|
|
114
|
+
Count tokens across errors, retries, cursor pages and contract discovery.
|
|
115
|
+
The new [Plan receipt issue](../.agents/pm/issues/pm-hqy7lr.toon) records
|
|
116
|
+
whole-plan output amplification observed during single-step mutations; its
|
|
117
|
+
acceptance requires bounded SDK-owned receipts and preserved full inspection.
|
|
118
|
+
- **Algorithms:** the [existing portfolio](CONTEXT_ALGORITHM_PORTFOLIO.md)
|
|
119
|
+
remains authoritative. Individual items now detail bounded candidate quotas,
|
|
120
|
+
evidence coverage per token, typed spreading activation, activation decay,
|
|
121
|
+
approximate affinity sketches and content-addressed summary trees. Each names
|
|
122
|
+
inputs, costs, fallback, negative controls and a promotion/rejection rule.
|
|
123
|
+
Mathematical guarantees require their assumptions; a heuristic is not a proof.
|
|
124
|
+
- **Scale and branches:** separately measure cold build and warm reads at 3,
|
|
125
|
+
100,000 and 1,000,000 items, and 1/10/100/1,000 agents. Exercise partitions,
|
|
126
|
+
retries, expired claims, clock skew and arbitrary merge order. Record p50/p95/
|
|
127
|
+
p99, memory, index amplification and no-lost-acknowledged-write evidence.
|
|
128
|
+
- **Recursive improvement:** bind observe, propose, evaluate, land and remeasure
|
|
129
|
+
stages to immutable evidence. Keep the evaluator and held-out data independent
|
|
130
|
+
of the change under evaluation. Rejected proposals stay searchable.
|
|
131
|
+
- **Universal environments:** episodes use SDK reset/observe/step/score contracts,
|
|
132
|
+
seeded snapshots, idempotent actions, independent verdicts and temporal cutoffs.
|
|
133
|
+
Distinguish termination from truncation and prevent future-evidence leakage.
|
|
134
|
+
- **Durability:** append-only API behavior, hash-chain tamper evidence,
|
|
135
|
+
authenticated authorship and externally anchored proof are different guarantees.
|
|
136
|
+
Migration, redaction and cryptographic rotation need explicit restore tests.
|
|
137
|
+
|
|
138
|
+
## Verified behavior and remaining limits
|
|
139
|
+
|
|
140
|
+
### Local qwen3 retrieval
|
|
141
|
+
|
|
142
|
+
The project already selects local Ollama `qwen3-embedding:0.6b`. Refresh embedded
|
|
143
|
+
1,085 stale inputs in 34 requests, with zero retries or splits, at approximately
|
|
144
|
+
11.91 inputs/second. This proves multi-input batching at the current corpus size;
|
|
145
|
+
it does not prove the outstanding 100k/1M performance acceptance.
|
|
146
|
+
|
|
147
|
+
Five project golden queries were evaluated at k=10:
|
|
148
|
+
|
|
149
|
+
| Mode | Mean nDCG | Mean MRR | Mean recall | Broad history/episode query recall |
|
|
150
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
151
|
+
| Keyword | 0.7451 | 0.80 | 0.8727 | 4/11 |
|
|
152
|
+
| Semantic | 0.7944 | 0.85 | 0.8182 | 1/11 |
|
|
153
|
+
| Hybrid | 0.8405 | 0.90 | 0.8364 | 2/11 |
|
|
154
|
+
|
|
155
|
+
Aggregate passes conceal weak queries. The existing retrieval-quality owner
|
|
156
|
+
[pm-p1g43q](../.agents/pm/features/pm-p1g43q.toon) now carries this observation,
|
|
157
|
+
feasible per-query floors, held-out paraphrase evaluation and mode comparisons.
|
|
158
|
+
At k=10, a query with eleven relevant items cannot attain recall 1. The final
|
|
159
|
+
semantic index check covered all 2,829 current items with zero stale vectors.
|
|
160
|
+
|
|
161
|
+
### MCP and SDK
|
|
162
|
+
|
|
163
|
+
The official [versioning page](https://modelcontextprotocol.io/docs/2026-07-28/learn/versioning)
|
|
164
|
+
identifies `2026-07-28` as current. The repository
|
|
165
|
+
[conformance matrix](MCP_2026_07_28_CONFORMANCE.md) was compared with the official
|
|
166
|
+
[key changes](https://modelcontextprotocol.io/specification/2026-07-28/changelog).
|
|
167
|
+
The focused SDK, stateless stdio, Streamable HTTP and legacy-handshake run passed
|
|
168
|
+
145 tests in twelve files. Exact-version public verification also passed direct
|
|
169
|
+
and package-selected npx/bunx CLI dispatch, MCP discovery, Skills, Apps and HTTP,
|
|
170
|
+
including missing-bin negative controls. This is focused conformance evidence, not an assertion
|
|
171
|
+
that every optional extension or every deployment is certified. Deprecated
|
|
172
|
+
features must not be newly advertised merely to increase a feature count.
|
|
173
|
+
|
|
174
|
+
Ordinary review mutations recorded detected Codex identity, model and effort
|
|
175
|
+
without `PM_AUTHOR` or author flags. Role/topic inference carries its source and
|
|
176
|
+
confidence. Missing signals remain unknown; configuration is not native proof.
|
|
177
|
+
|
|
178
|
+
### Daily release
|
|
179
|
+
|
|
180
|
+
Version `2026.9.28` exists in npm and GitHub. The
|
|
181
|
+
[original automatic attempt](https://github.com/unbraind/pm-cli/actions/runs/36375818142)
|
|
182
|
+
failed after Windows global npm installation failed in the child release run.
|
|
183
|
+
The [same-version recovery](https://github.com/unbraind/pm-cli/actions/runs/36380136948)
|
|
184
|
+
passed npm, global npm and Bun on Linux, macOS and Windows and advertised the
|
|
185
|
+
existing version. It did not create a second automatic version that day.
|
|
186
|
+
|
|
187
|
+
The new [installer-diagnostics issue](../.agents/pm/issues/pm-q7c36n.toon)
|
|
188
|
+
records why the original cause remains unknown: the subprocess wrapper discards
|
|
189
|
+
error/signal details and the installer labels every retry registry availability.
|
|
190
|
+
Recovery proves the artifact can pass; it does not erase the failed attempt or
|
|
191
|
+
establish its cause. Keep automatic reliability, publication and consumer
|
|
192
|
+
acceptance as separate measurements.
|
|
193
|
+
|
|
194
|
+
## Prevention and repeatability
|
|
195
|
+
|
|
196
|
+
Six active-only zero-debt assertions now join `tracker-context-quality` for
|
|
197
|
+
missing description, acceptance criteria, risk, confidence, estimate and expected
|
|
198
|
+
result. The existing local and hosted quality pipeline invokes this same native
|
|
199
|
+
SDK gate. Terminal evidence gaps are deliberately outside the active-only policy.
|
|
200
|
+
An isolated temporary workspace failed all six assertions with missing fields
|
|
201
|
+
and passed all six after repair. This checks presence; semantic quality still
|
|
202
|
+
requires evidence review.
|
|
203
|
+
|
|
204
|
+
For repeat reviews, follow the
|
|
205
|
+
[ecosystem planning skill](../.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md).
|
|
206
|
+
Use complete CLI snapshots outside the prompt, cursor-resumed event streams,
|
|
207
|
+
exact source fingerprints, all-status duplicate checks and bounded mutation
|
|
208
|
+
batches. At million-item scale, use streaming/indexed consumers rather than
|
|
209
|
+
loading a whole JSON collection into the agent's context. Every changed item
|
|
210
|
+
retains its canonical owner, history and original acceptance boundary.
|
|
211
|
+
|
|
212
|
+
## Verification record
|
|
213
|
+
|
|
214
|
+
Quiescent validation passed for all 2,829 final items. The 170 historical
|
|
215
|
+
structured-evidence warnings are unchanged. All 38 changed history streams pass
|
|
216
|
+
the public SDK chain verifier; the 35 pre-existing streams retain byte-identical
|
|
217
|
+
historical prefixes. The three enriched closed features retain identical status,
|
|
218
|
+
closure, resolution and release fields. Final graph-composition and all 47
|
|
219
|
+
tracker-context-quality assertions pass, including the six new readiness rules.
|
|
220
|
+
|
|
221
|
+
An earlier long-running context gate saw one history-drift failure while this
|
|
222
|
+
review was still mutating the tracker. Independent validation and the final gate
|
|
223
|
+
pass with writes stopped; the earlier result is retained as a concurrent-read
|
|
224
|
+
observation, not rewritten as success. Skill/document validation, local links
|
|
225
|
+
and the generated changelog check also pass. All review claims are released. Three distinct issues were added after duplicate
|
|
226
|
+
checks: installer diagnostics, Plan mutation receipts and startup admission
|
|
227
|
+
diagnosis. Existing programmes retain their original acceptance boundaries.
|
|
228
|
+
|
|
229
|
+
The broad static run passed its build, lint, generated contracts, SDK parity,
|
|
230
|
+
command grammar, task-token, MCP deprecation, gate-registry and context-quality
|
|
231
|
+
checks, then failed the import-cost timing gate (bare Node median 305 ms against
|
|
232
|
+
106 ms). An isolated rerun passed all ten SDK entrypoints with unchanged budgets.
|
|
233
|
+
The initial timing failure remains part of the evidence; it is not a continuous
|
|
234
|
+
all-green static invocation.
|
|
235
|
+
|
|
236
|
+
The remaining graph and record-integrity gates passed. The mutation gate passed
|
|
237
|
+
with 323 of 330 mutants killed and seven classified as equivalent. The CLI
|
|
238
|
+
transport benchmark initially exceeded its list threshold (442 ms versus 419 ms);
|
|
239
|
+
the isolated rerun also failed: get 385/362 ms, context 466/433 ms, next
|
|
240
|
+
457/440 ms and create 424/398 ms. No performance limit was raised. The new
|
|
241
|
+
[startup investigation](../.agents/pm/issues/pm-bj7rq0.toon) distinguishes runner
|
|
242
|
+
qualification from a demonstrated product regression and preserves the completed
|
|
243
|
+
transport-floor implementation. This is an unresolved validation limitation.
|
|
244
|
+
|
|
245
|
+
## PR review corrections
|
|
246
|
+
|
|
247
|
+
PR #1333 identified two overly broad relationship claims. The standing release
|
|
248
|
+
reminder predates the new installer-diagnostics issue, so their link is `related`
|
|
249
|
+
coordination rather than `discovered_from` provenance. Four `verifies` rows from
|
|
250
|
+
the historical batch were removed: checking citations, history continuity and
|
|
251
|
+
release attribution does not verify the source run or feature behavior. The three
|
|
252
|
+
source-backed discovery links on closed features remain valid and unchanged.
|
|
253
|
+
|
|
254
|
+
The original writes and their corrections remain in immutable history. Five
|
|
255
|
+
holder/target/text-scoped citation exemptions distinguish the batch inventory
|
|
256
|
+
from verification claims without raising the prose-edge ceiling. The corrected
|
|
257
|
+
graph-composition gate passes.
|
|
258
|
+
|
|
259
|
+
The follow-up review narrowed those five exemptions to the complete original
|
|
260
|
+
paragraphs, including their historical source-event coordinates. In an isolated
|
|
261
|
+
temporary tracker the original citations pass, while a later batch comment
|
|
262
|
+
asserting an unrecorded prerequisite is blocked with one detected gap. This
|
|
263
|
+
prevents the common phrase “Historical batch” from exempting future claims.
|
|
264
|
+
|
|
265
|
+
Annotation review corrected 26 notes and five comments that contained literal
|
|
266
|
+
JSON wrappers around prose. Supported note/comment edits retain the exact inner
|
|
267
|
+
text and append correction events. Per-item rereads confirm lifecycle, closure,
|
|
268
|
+
release, expected and actual fields are unchanged; original annotations remain
|
|
269
|
+
in the history streams. Planning guidance now distinguishes plain-text input
|
|
270
|
+
from intentional JSON events.
|
package/docs/MERGE_SAFETY.md
CHANGED
|
@@ -13,6 +13,8 @@ compatibility is tracked by
|
|
|
13
13
|
[pm-wn3ee5](../.agents/pm/issues/pm-wn3ee5.toon).
|
|
14
14
|
Lossless concurrent acceptance-criteria composition is tracked by
|
|
15
15
|
[pm-inn5y5](../.agents/pm/issues/pm-inn5y5.toon).
|
|
16
|
+
The Bun launcher and strict-health repair are tracked by
|
|
17
|
+
[pm-vks66s](../.agents/pm/issues/pm-vks66s.toon).
|
|
16
18
|
Recency-aware scalar convergence, bounded fresh-clone decision evidence, and
|
|
17
19
|
audited pre-durable receipt dispositions are tracked by
|
|
18
20
|
[pm-7wzb6d](../.agents/pm/issues/pm-7wzb6d.toon),
|
|
@@ -39,11 +41,14 @@ git commit -m "chore(pm): install tracker merge drivers"
|
|
|
39
41
|
`pm merge install` writes an idempotent, fenced `.gitattributes` block and repository-local `git config` entries. The attributes are committed; the driver commands are clone-local, so every collaborator and fresh CI clone that performs merges must run the install command.
|
|
40
42
|
|
|
41
43
|
The clone-local driver values record the absolute runtime launcher and bundled
|
|
42
|
-
`dist/cli.js` path resolved by the installing SDK.
|
|
43
|
-
absolute PATH symlink when it resolves to the executing runtime, so
|
|
44
|
-
manager can retarget that launcher during upgrades without breaking
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
`dist/cli.js` path resolved by the installing SDK. With Node, the installer
|
|
45
|
+
preserves an absolute PATH symlink when it resolves to the executing runtime, so
|
|
46
|
+
a runtime manager can retarget that launcher during upgrades without breaking
|
|
47
|
+
every clone. With Bun, it resolves an executable `bun` on an absolute PATH entry,
|
|
48
|
+
checks that its version matches the running Bun version, and records its stable
|
|
49
|
+
absolute target. Relative PATH entries, inaccessible launchers, and different
|
|
50
|
+
runtimes are ignored; without a matching launcher, the installer retains the
|
|
51
|
+
executing binary path.
|
|
47
52
|
Git therefore does not depend
|
|
48
53
|
on a bare `pm` command or the caller's later `PATH` when it merges tracker data.
|
|
49
54
|
The item-path placeholder is stored as bare `%P`: Git performs the required
|
|
@@ -53,7 +58,7 @@ remain reconcilable.
|
|
|
53
58
|
`pm validate --check-storage-integrity` and `pm health` also compare every
|
|
54
59
|
effective driver definition with the installed SDK. A command installed by
|
|
55
60
|
another valid `@unbrained/pm-cli` package is
|
|
56
|
-
also accepted when its Node and `dist/cli.js` paths exist, its manifest owns the
|
|
61
|
+
also accepted when its Node or Bun launcher and `dist/cli.js` paths exist, its manifest owns the
|
|
57
62
|
`pm` bin, the driver arguments are semantically identical, and its version
|
|
58
63
|
matches any exact project runtime pins. A driver inside a sibling worktree of
|
|
59
64
|
the same repository is rejected, even when that package is otherwise valid.
|
package/docs/README.md
CHANGED
|
@@ -59,6 +59,7 @@ pm guide release --json
|
|
|
59
59
|
- [Context Relevance and Packing](CONTEXT_RELEVANCE.md) - shared CLI/SDK signals, derived-store provenance, ranking explanations, and token budgets.
|
|
60
60
|
- [Next-work Selection Budgets](NEXT_SELECTION_BUDGETS.md) - bounded executable recommendations, omission receipts, and graded 10k/100k context verification.
|
|
61
61
|
- [Context Algorithm Portfolio](CONTEXT_ALGORITHM_PORTFOLIO.md) - stable proposal slugs, historical aliases, individual owners, cognitive compositions, and experiment gates.
|
|
62
|
+
- [Ecosystem Planning Review](ECOSYSTEM_PLANNING_REVIEW.md) - dated all-status census, canonical horizons, operational evidence, and active-readiness gates.
|
|
62
63
|
- [Output Projection and Omission Contracts](OUTPUT_PROJECTION_CONTRACTS.md) - explicit withheld-field receipts, mode-paired row keys, and completion resolver outcomes.
|
|
63
64
|
- [Output Token Accounting](OUTPUT_TOKEN_ACCOUNTING.md) - opt-in CLI/MCP byte attribution, bounded receipt overhead, and release-level tokens-per-task baselines.
|
|
64
65
|
- [SDK Context Platform](SDK_CONTEXT.md) - task-oriented entry point for authoritative reads, ranking, package workflows, diagnostics, recovery, and verification.
|
|
@@ -94,6 +95,7 @@ pm guide release --json
|
|
|
94
95
|
- [Extension Lifecycle Contracts](EXTENSION_LIFECYCLE.md) - source identity, durable migrations, and scoped preflight ownership.
|
|
95
96
|
- [Extension Author Contracts](EXTENSION_AUTHOR_CONTRACTS.md) - the stability guarantees and contract surface package authors build against.
|
|
96
97
|
- [SDK](SDK.md) - public import surfaces and typed authoring examples.
|
|
98
|
+
- [Audited SDK Settings and Item Updates](SDK_AUDITED_MUTATIONS.md) - extension-owned settings changes, replay-safe previews, and full-item JSON annotation integrity.
|
|
97
99
|
- [SDK execution and recovery contracts](SDK_EXECUTION_CONTRACTS.md): schema-driven scheduling, handoff, and command recovery.
|
|
98
100
|
- [Multi-Branch Merge Safety](MERGE_SAFETY.md) - semantic tracker merge drivers, post-merge integrity gates, delete/modify policy, and recovery-receipt retention.
|
|
99
101
|
- [Codex Plugin](CODEX_PLUGIN.md) - native MCP plugin install, tools, skills, and safety notes.
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Scheduled release reliability
|
|
2
2
|
|
|
3
3
|
Tracked by [pm-e70zh5](../.agents/pm/issues/pm-e70zh5.toon) and
|
|
4
|
-
[pm-44u3wt](../.agents/pm/issues/pm-44u3wt.toon).
|
|
4
|
+
[pm-44u3wt](../.agents/pm/issues/pm-44u3wt.toon). Trigger provenance and
|
|
5
|
+
installed-package diagnostics are tracked by
|
|
6
|
+
[pm-prrlce](../.agents/pm/issues/pm-prrlce.toon) and
|
|
7
|
+
[pm-q7c36n](../.agents/pm/issues/pm-q7c36n.toon).
|
|
5
8
|
|
|
6
9
|
The **Release Reliability** workflow evaluates the preceding 30 days after a
|
|
7
10
|
scheduled Auto Release completes, daily at nominal 04:35 UTC, and on manual
|
|
@@ -33,16 +36,27 @@ The unchanged [release recovery rules](RELEASING.md#failure-handling) apply.
|
|
|
33
36
|
|
|
34
37
|
## Evidence and denominator
|
|
35
38
|
|
|
36
|
-
The collector paginates
|
|
37
|
-
|
|
39
|
+
The collector paginates Auto Release runs over an explicit half-open UTC window
|
|
40
|
+
and checks returned counts and unique IDs. It retrieves attempt 1
|
|
38
41
|
when a run was rerun. A later successful attempt, manual dispatch, issue recovery,
|
|
39
42
|
or closed blocker cannot change the original scheduled failure. Pending runs
|
|
40
43
|
are listed separately; all completed non-success conclusions count as failures.
|
|
44
|
+
The native scheduled denominator and policy remain separate from the morning
|
|
45
|
+
dispatcher series. Each new dispatch declares `trigger_origin`; the workflow
|
|
46
|
+
run name and attempt receipt preserve it. If setup fails before the receipt can
|
|
47
|
+
be uploaded, the collector reads the declared run name. Historical dispatches
|
|
48
|
+
without either declaration appear in `pre_attribution_gap`; the failed
|
|
49
|
+
2026-09-28 morning dispatch cannot be attributed from its old run record.
|
|
50
|
+
Dispatcher rows retain creation time, queue delay, outcome, and failure stage.
|
|
51
|
+
They omit nominal occurrence and dispatch delay because the native 02:35 UTC
|
|
52
|
+
cron is not the dispatcher's clock; the scheduled timing policy does not apply
|
|
53
|
+
to those rows.
|
|
41
54
|
API errors, truncated pages, ambiguous artifacts, and mismatched run identities
|
|
42
55
|
fail collection rather than silently reducing the denominator.
|
|
43
56
|
|
|
44
57
|
Auto Release saves `release-observation-<attempt>` artifacts for 90 days. Each
|
|
45
|
-
contains only run/attempt identity, event, outcome and
|
|
58
|
+
contains only run/attempt identity, event, trigger origin, outcome and
|
|
59
|
+
failure-stage name. Gate
|
|
46
60
|
stdout, stderr, credentials and environment values are not archived. A confirmed
|
|
47
61
|
publication, verified existing same-day release, unchanged source, tracker-only
|
|
48
62
|
changes, and an empty changelog are distinct outcomes. Failed gates retain their
|
|
@@ -61,6 +75,14 @@ downloads only data, never executable source. The reporting workflow checks out
|
|
|
61
75
|
the default branch rather than the triggering run's code and has read-only token
|
|
62
76
|
permissions.
|
|
63
77
|
|
|
78
|
+
Registry acceptance records every exact-package install attempt. A timeout,
|
|
79
|
+
signal, missing executable, other spawn error, and nonzero exit each keep their
|
|
80
|
+
own classification, exit status, signal, error code, and a bounded redacted
|
|
81
|
+
stderr excerpt. Only an explicit registry 404 is retried as possible metadata
|
|
82
|
+
propagation; successful recovery retains the earlier failure receipts. The
|
|
83
|
+
control and candidate sessions share a 13-minute deadline under the 15-minute
|
|
84
|
+
hosted acceptance job limit.
|
|
85
|
+
|
|
64
86
|
The report is a rolling observation, not a permanent archive of all releases.
|
|
65
87
|
Retain exported receipts externally if a longer historical window is needed.
|
|
66
88
|
It does not implement the separate nightly per-leg reliability programme or
|