@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.
Files changed (118) hide show
  1. package/.agents/skills/pm-developer/references/GRAPH_AND_RELATIONSHIPS.md +7 -6
  2. package/.agents/skills/pm-user/references/BACKLOG_SHAPING.md +1 -1
  3. package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +26 -3
  4. package/.claude-plugin/marketplace.json +2 -2
  5. package/CHANGELOG.md +26 -0
  6. package/dist/cli/error-guidance.js +1 -1
  7. package/dist/cli/help-json-payload.js +2 -2
  8. package/dist/cli/main.js +2 -2
  9. package/dist/cli/runtime/invocation-options.d.ts +3 -1
  10. package/dist/cli/runtime/invocation-options.js +1 -1
  11. package/dist/cli-bundle/bundle-manifest.json +152 -152
  12. package/dist/cli-bundle/chunks/{chunk-UT7GRGHY.js → chunk-6XTJLOKW.js} +2 -2
  13. package/dist/cli-bundle/chunks/{chunk-VMJLM7P2.js → chunk-6YEET7XI.js} +22 -22
  14. package/dist/cli-bundle/chunks/{chunk-NEUIVBFO.js → chunk-A6NDNRCK.js} +3 -3
  15. package/dist/cli-bundle/chunks/{chunk-GLH2LNRH.js → chunk-BNAT4EYR.js} +9 -9
  16. package/dist/cli-bundle/chunks/{chunk-CPOWTIIS.js → chunk-BTCE36JS.js} +2 -2
  17. package/dist/cli-bundle/chunks/{chunk-35DME35A.js → chunk-BX4LEJX4.js} +2 -2
  18. package/dist/cli-bundle/chunks/{chunk-A2G5OG6W.js → chunk-EGAIIWI3.js} +2 -2
  19. package/dist/cli-bundle/chunks/chunk-FYTXVVZG.js +2 -0
  20. package/dist/cli-bundle/chunks/{chunk-ZFHJP7YD.js → chunk-O23CQ5T4.js} +31 -29
  21. package/dist/cli-bundle/chunks/{chunk-NBDB3SRC.js → chunk-UIAH7UFK.js} +67 -67
  22. package/dist/cli-bundle/chunks/{chunk-OYCCWPBM.js → chunk-USTAFHIM.js} +2 -2
  23. package/dist/cli-bundle/chunks/{chunk-PR4UAMI5.js → chunk-XRN3JYNN.js} +85 -85
  24. package/dist/cli-bundle/chunks/{chunk-PLNNUAFZ.js → chunk-YM342ELM.js} +2 -2
  25. package/dist/cli-bundle/chunks/{register-list-query-N6RP6PSP.js → register-list-query-SYNBVMNG.js} +2 -2
  26. package/dist/cli-bundle/chunks/{register-mutation-CY7JGQE6.js → register-mutation-4GSUK23D.js} +2 -2
  27. package/dist/cli-bundle/chunks/{register-operations-53HAD3QP.js → register-operations-7FCJUC4Z.js} +2 -2
  28. package/dist/cli-bundle/chunks/{register-setup-NVX6374E.js → register-setup-YTWW2KL5.js} +2 -2
  29. package/dist/cli-bundle/focused-chunks/{chunk-BRLXKRI2.js → chunk-3CAZ7Z4R.js} +4 -4
  30. package/dist/cli-bundle/focused-chunks/{chunk-K7GSZ7YZ.js → chunk-4AORVUT4.js} +2 -2
  31. package/dist/cli-bundle/focused-chunks/{chunk-AZA4AEOR.js → chunk-AKB3JN57.js} +33 -33
  32. package/dist/cli-bundle/focused-chunks/{chunk-OVVCXTIE.js → chunk-EFD4ATS7.js} +2 -2
  33. package/dist/cli-bundle/focused-chunks/{chunk-IJXZQE3D.js → chunk-EVMH4SKN.js} +2 -2
  34. package/dist/cli-bundle/focused-chunks/{chunk-IWLMQLBO.js → chunk-IDB6E3ZV.js} +28 -28
  35. package/dist/cli-bundle/focused-chunks/{chunk-YZB7IPR6.js → chunk-IQBFYQF2.js} +2 -2
  36. package/dist/cli-bundle/focused-chunks/{chunk-BGWF6CMP.js → chunk-KZULQIAG.js} +2 -2
  37. package/dist/cli-bundle/focused-chunks/{chunk-ZMQTIS3L.js → chunk-MOUCTR72.js} +2 -2
  38. package/dist/cli-bundle/focused-chunks/{chunk-6IVQPNYN.js → chunk-OMBDUJWZ.js} +2 -2
  39. package/dist/cli-bundle/focused-chunks/{chunk-DTLRXY77.js → chunk-RIJQQOEZ.js} +2 -2
  40. package/dist/cli-bundle/focused-chunks/{chunk-A5O3VYWV.js → chunk-T4PZJQG4.js} +2 -2
  41. package/dist/cli-bundle/focused-chunks/{chunk-V7OPZRL6.js → chunk-UPOMLSW7.js} +14 -14
  42. package/dist/cli-bundle/focused-chunks/{chunk-5KJXX2TZ.js → chunk-XB5Q2P7Z.js} +2 -2
  43. package/dist/cli-bundle/focused-chunks/{chunk-ONDNR3VU.js → chunk-XUCXRZZZ.js} +2 -2
  44. package/dist/cli-bundle/focused-chunks/{chunk-Y5Z3XNGY.js → chunk-ZJ3CLTV4.js} +2 -2
  45. package/dist/cli-bundle/main.js +22 -22
  46. package/dist/cli-bundle/sdk-authoring.js +1 -1
  47. package/dist/cli-bundle/sdk-contracts.js +1 -1
  48. package/dist/cli-bundle/sdk-core.js +22 -21
  49. package/dist/cli-bundle/sdk-governance.js +1 -1
  50. package/dist/cli-bundle/sdk-graph.js +1 -1
  51. package/dist/cli-bundle/sdk-merge.js +38 -38
  52. package/dist/cli-bundle/sdk-query.js +1 -1
  53. package/dist/cli-bundle/sdk-runtime.js +1 -1
  54. package/dist/cli-bundle/sdk-testing.js +1 -1
  55. package/dist/cli-bundle/sdk.js +2 -2
  56. package/dist/core/extensions/extension-types.d.ts +13 -0
  57. package/dist/core/extensions/extension-types.js +1 -1
  58. package/dist/core/history/replay.js +1 -1
  59. package/dist/core/history/workspace-history.d.ts +17 -0
  60. package/dist/core/history/workspace-history.js +1 -1
  61. package/dist/core/output/mutation-projection.js +1 -1
  62. package/dist/core/telemetry/runtime.js +2 -2
  63. package/dist/sdk/cli-contracts/command-aliases.js +1 -1
  64. package/dist/sdk/extension-command-context.d.ts +1 -7
  65. package/dist/sdk/extension-command-context.js +1 -1
  66. package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -1
  67. package/dist/sdk/index.d.ts +1 -0
  68. package/dist/sdk/index.js +1 -1
  69. package/dist/sdk/lifecycle/plan-mutation-receipt.d.ts +53 -0
  70. package/dist/sdk/lifecycle/plan-mutation-receipt.js +2 -0
  71. package/dist/sdk/lifecycle/plan.d.ts +3 -0
  72. package/dist/sdk/lifecycle/plan.js +1 -1
  73. package/dist/sdk/lifecycle/update.d.ts +2 -0
  74. package/dist/sdk/lifecycle/update.js +1 -1
  75. package/dist/sdk/linked-artifacts.js +1 -1
  76. package/dist/sdk/merge/install.js +1 -1
  77. package/dist/sdk/public-surface.json +1 -1
  78. package/dist/sdk/runtime/actions.js +1 -1
  79. package/dist/sdk/runtime-input.d.ts +2 -0
  80. package/dist/sdk/runtime-input.js +1 -1
  81. package/dist/sdk/runtime.js +1 -1
  82. package/dist/sdk/similarity-scoring.js +1 -1
  83. package/dist/sdk/structured-mutations.js +1 -1
  84. package/dist/sdk/telemetry.d.ts +2 -0
  85. package/dist/sdk/telemetry.js +3 -1
  86. package/dist/sdk/testing.js +1 -1
  87. package/docs/CLI_GRAMMAR.md +18 -1
  88. package/docs/ECOSYSTEM_PLANNING_REVIEW.md +270 -0
  89. package/docs/MERGE_SAFETY.md +11 -6
  90. package/docs/README.md +2 -0
  91. package/docs/RELEASE_RELIABILITY.md +26 -4
  92. package/docs/SDK_AUDITED_MUTATIONS.md +52 -0
  93. package/docs/SDK_EVIDENCE_TRACEABILITY.md +34 -0
  94. package/docs/agent-task-token-baseline.json +28 -2
  95. package/docs/agent-task-transcripts.json +137 -14
  96. package/docs/ecosystem-review-2026-09-28.json +3159 -0
  97. package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
  98. package/marketplace.json +2 -2
  99. package/package.json +1 -1
  100. package/packages/pm-beads/package.json +1 -1
  101. package/packages/pm-calendar/package.json +1 -1
  102. package/packages/pm-command-kit/package.json +1 -1
  103. package/packages/pm-digital-twin/package.json +1 -1
  104. package/packages/pm-governance-audit/package.json +1 -1
  105. package/packages/pm-guide-shell/package.json +1 -1
  106. package/packages/pm-kanban/package.json +1 -1
  107. package/packages/pm-lifecycle-hooks/package.json +1 -1
  108. package/packages/pm-linked-test-adapters/package.json +1 -1
  109. package/packages/pm-search-advanced/package.json +1 -1
  110. package/packages/pm-templates/package.json +1 -1
  111. package/packages/pm-todos/package.json +1 -1
  112. package/packages/pm-vcs/package.json +1 -1
  113. package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
  114. package/plugins/pm-claude/package.json +2 -2
  115. package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
  116. package/plugins/pm-codex/package.json +2 -2
  117. package/plugins/pm-codex/plugin.json +1 -1
  118. package/dist/cli-bundle/chunks/chunk-YERBAGQD.js +0 -2
@@ -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};
@@ -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.
@@ -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. The installer preserves an
43
- absolute PATH symlink when it resolves to the executing runtime, so a runtime
44
- manager can retarget that launcher during upgrades without breaking every clone.
45
- Relative PATH entries, inaccessible launchers, and different runtimes are ignored;
46
- without a matching launcher, the installer retains the executing binary path.
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 the schedule-only runs API over an explicit half-open
37
- UTC window and checks returned counts and unique IDs. It retrieves attempt 1
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 failure-stage name. Gate
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