@unbrained/pm-cli 2026.10.9 → 2026.10.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -36
- package/CHANGELOG.md +32 -4
- package/dist/cli/register-mutation.js +2 -2
- package/dist/cli-bundle/bundle-manifest.json +1 -382
- package/dist/cli-bundle/chunks/{chunk-P5NS7EFC.js → chunk-2JYTSTQP.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-ZNXRGATT.js → chunk-BXSXT5Z6.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QQHNXCO3.js → chunk-ESRACL6K.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5MOFELIF.js → chunk-K2UTE75T.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-55MK3QQM.js → chunk-KNW3RSLS.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-MEWBSWE3.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-KOGF6O6W.js → chunk-OBLO45UM.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-XW3BLWFF.js → chunk-RBPOO46D.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-E4XR5LKU.js → chunk-RUT3CCAG.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-CPZOW6L2.js → chunk-S445XEYT.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5NR6TDYB.js → chunk-SSY4OSJR.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5TCQ5J3V.js → chunk-YMLF6QT7.js} +75 -75
- package/dist/cli-bundle/chunks/{chunk-NIF7JOPW.js → chunk-ZWRLAOA4.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-2YOWMNWY.js → register-list-query-T6BOR67M.js} +2 -2
- package/dist/cli-bundle/chunks/register-mutation-6NXR7SAJ.js +20 -0
- package/dist/cli-bundle/chunks/{register-operations-BMK65YM3.js → register-operations-PDOBQZUJ.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-24TFFK6N.js → register-setup-MAYFPKBX.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-362645PL.js → chunk-52UJEHEV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-EZ4367G4.js → chunk-EIOQNLBQ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SAN3KPX4.js → chunk-GVGFUD52.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-DSLNUL2Q.js → chunk-JGFSSR3G.js} +16 -16
- package/dist/cli-bundle/focused-chunks/{chunk-IARAEFMA.js → chunk-KNJR2WUO.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-MJQ253F3.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-MCVWZUGK.js → chunk-SIEGIPNT.js} +28 -28
- package/dist/cli-bundle/main.js +2 -2
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +1 -1
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +51 -51
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +9 -9
- package/dist/core/item/item-format.d.ts +2 -0
- package/dist/core/item/item-format.js +3 -3
- package/dist/core/schema/runtime-field-values.d.ts +2 -2
- package/dist/core/schema/runtime-field-values.js +1 -1
- package/dist/core/store/item-format-migration.js +1 -1
- package/dist/core/store/item-store.js +1 -1
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -1
- package/dist/sdk/governance/artifact-ledgers.d.ts +59 -0
- package/dist/sdk/governance/artifact-ledgers.js +2 -0
- package/dist/sdk/governance/reliability.d.ts +74 -0
- package/dist/sdk/governance/reliability.js +2 -0
- package/dist/sdk/governance.d.ts +2 -0
- package/dist/sdk/governance.js +1 -1
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/structured-mutations.d.ts +5 -1
- package/dist/sdk/structured-mutations.js +1 -1
- package/docs/QUALITY_EVIDENCE.md +52 -1
- package/docs/RELEASE_LEDGER_RECONCILIATION.md +148 -0
- package/docs/RELEASE_RELIABILITY.md +58 -3
- package/docs/RELEASING.md +34 -5
- package/docs/SDK.md +16 -2
- package/docs/TESTING.md +36 -0
- package/docs/agent-task-token-baseline.json +1 -291
- package/docs/agent-task-transcripts.json +1 -478
- package/docs/ecosystem-review-2026-09-28.json +1 -2866
- package/docs/examples/policy-restricted-extension/manifest.json +1 -22
- package/docs/examples/policy-restricted-extension/package.json +1 -8
- package/docs/examples/sdk-agent-environment/package.json +1 -8
- package/docs/examples/sdk-app-embedding/package.json +1 -9
- package/docs/examples/sdk-contract-consumer/package.json +1 -11
- package/docs/examples/sdk-custom-tool/package.json +1 -13
- package/docs/examples/starter-extension/manifest.json +1 -33
- package/docs/examples/starter-extension/package.json +1 -10
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +4 -3
- package/docs/performance/corpus-shape-comparison.json +1 -108
- package/docs/performance/latest-scale-report.json +1 -418
- package/docs/performance/scale-100k-report.json +1 -418
- package/docs/schemas/extension-manifest.schema.json +1 -165
- package/marketplace.json +1 -36
- package/package.json +1 -1
- package/packages/pm-beads/extensions/beads/manifest.json +1 -28
- package/packages/pm-beads/extensions/beads/package.json +1 -3
- package/packages/pm-beads/package.json +1 -50
- package/packages/pm-calendar/extensions/calendar/manifest.json +1 -22
- package/packages/pm-calendar/extensions/calendar/package.json +1 -3
- package/packages/pm-calendar/package.json +1 -51
- package/packages/pm-command-kit/extensions/command-kit/manifest.json +1 -22
- package/packages/pm-command-kit/extensions/command-kit/package.json +1 -3
- package/packages/pm-command-kit/package.json +1 -53
- package/packages/pm-digital-twin/extensions/digital-twin/manifest.json +1 -19
- package/packages/pm-digital-twin/extensions/digital-twin/package.json +1 -3
- package/packages/pm-digital-twin/package.json +1 -58
- package/packages/pm-governance-audit/extensions/governance-audit/manifest.json +1 -19
- package/packages/pm-governance-audit/extensions/governance-audit/package.json +1 -3
- package/packages/pm-governance-audit/package.json +1 -55
- package/packages/pm-guide-shell/extensions/guide-shell/manifest.json +1 -22
- package/packages/pm-guide-shell/extensions/guide-shell/package.json +1 -3
- package/packages/pm-guide-shell/package.json +1 -52
- package/packages/pm-kanban/extensions/kanban/manifest.json +1 -19
- package/packages/pm-kanban/extensions/kanban/package.json +1 -3
- package/packages/pm-kanban/package.json +1 -57
- package/packages/pm-lifecycle-hooks/extensions/lifecycle-hooks/manifest.json +1 -19
- package/packages/pm-lifecycle-hooks/extensions/lifecycle-hooks/package.json +1 -3
- package/packages/pm-lifecycle-hooks/package.json +1 -52
- package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/manifest.json +1 -29
- package/packages/pm-linked-test-adapters/extensions/linked-test-adapters/package.json +1 -3
- package/packages/pm-linked-test-adapters/package.json +1 -53
- package/packages/pm-search-advanced/extensions/search-advanced/manifest.json +1 -29
- package/packages/pm-search-advanced/extensions/search-advanced/package.json +1 -3
- package/packages/pm-search-advanced/package.json +1 -54
- package/packages/pm-templates/extensions/templates/manifest.json +1 -22
- package/packages/pm-templates/extensions/templates/package.json +1 -3
- package/packages/pm-templates/package.json +1 -50
- package/packages/pm-todos/extensions/todos/manifest.json +1 -29
- package/packages/pm-todos/extensions/todos/package.json +1 -3
- package/packages/pm-todos/package.json +1 -51
- package/packages/pm-vcs/extensions/vcs/manifest.json +1 -19
- package/packages/pm-vcs/extensions/vcs/package.json +1 -3
- package/packages/pm-vcs/package.json +1 -55
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -25
- package/plugins/pm-claude/.mcp.json +1 -8
- package/plugins/pm-claude/hooks/hooks.json +1 -17
- package/plugins/pm-claude/package.json +1 -9
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -45
- package/plugins/pm-codex/.mcp.json +1 -11
- package/plugins/pm-codex/mcp.json +1 -11
- package/plugins/pm-codex/package.json +1 -9
- package/plugins/pm-codex/plugin.json +1 -45
- package/runtime-dependencies.json +1 -512
- package/dist/cli-bundle/chunks/chunk-N7GKWO4G.js +0 -2
- package/dist/cli-bundle/chunks/register-mutation-ILOWFMTV.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-2ATAD56F.js +0 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BulkItemMutation } from "./item-transaction.js";
|
|
2
|
+
import { type RuntimeFieldDefinitionResolved } from "../core/schema/runtime-schema.js";
|
|
2
3
|
/** Controls for resolving one versioned mutation document. */
|
|
3
4
|
export interface ResolveItemMutationDocumentOptions {
|
|
4
5
|
/** Stable transaction identity used to derive replay-safe create ids. */
|
|
@@ -38,7 +39,10 @@ export declare function resolveItemMutationDocument(input: string, options: Reso
|
|
|
38
39
|
/**
|
|
39
40
|
* Convert a direct item JSON document or `pm get --json` envelope into the
|
|
40
41
|
* create/update option shape. Explicit CLI flags override document values.
|
|
42
|
+
* Supply the workspace's resolved schema fields to route metadata keys through
|
|
43
|
+
* declared option names and aliases. Extension assignments retain raw strings
|
|
44
|
+
* and JSON containers, with explicit assignments applied after document values.
|
|
41
45
|
*/
|
|
42
|
-
export declare function itemDocumentToMutationOptions(input: string, mode: "create" | "update", explicitOptions?: Record<string, unknown
|
|
46
|
+
export declare function itemDocumentToMutationOptions(input: string, mode: "create" | "update", explicitOptions?: Record<string, unknown>, runtimeFields?: readonly RuntimeFieldDefinitionResolved[]): Record<string, unknown>;
|
|
43
47
|
/** Validate an already-decoded MCP mutation array through the same strict parser as CLI stdin. */
|
|
44
48
|
export declare function validateItemMutationRows(value: unknown): BulkItemMutation[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="62e0ef49-30c8-56e0-985e-49c83d14695e")}catch{}})();import crypto from"node:crypto";import{normalizeItemId,normalizePrefix}from"../core/item/id.js";import{preserveMutationStdinTokenLiterals}from"../core/item/parse.js";import{EXIT_CODE,ITEM_PROJECT_CONTEXT_KEYS}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{levenshteinDistanceWithinLimit}from"../core/shared/levenshtein.js";import{CLOSE_FLAG_CONTRACTS,RELEASE_FLAG_CONTRACTS}from"./cli-contracts/flag-contracts.js";import{CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS,UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS}from"./cli-contracts/commander-mutation-options.js";const MUTATION_ROW_KEYS=["op","id","reason","options"],REFERENCED_MUTATION_ROW_KEYS=[...MUTATION_ROW_KEYS,"ref"],MUTATION_DOCUMENT_KEYS=["schema_version","mutations"],MUTATION_REFERENCE_SOURCE="[a-z][a-z0-9._-]{0,63}",MUTATION_REFERENCE_PATTERN=new RegExp(`^${MUTATION_REFERENCE_SOURCE}$`,"u"),MUTATION_DEPENDENCY_REFERENCE_PATTERN=new RegExp(`(^|,)id=@(${MUTATION_REFERENCE_SOURCE})(?=,|$)`,"gu"),flagToOptionKey=flag=>flag.slice(2).replaceAll(/-([a-z])/gu,(_match,letter)=>letter.toUpperCase()),ITEM_ENVELOPE_KEYS=["item","linked","schedule","claim_state","children","omission_receipt","row_contract","context_intent"],READ_ONLY_ITEM_KEYS=new Set(["created_at","updated_at","closed_at","version","format_version","path","author","collection_counts","notes_count","tests_count"]),UPDATE_READ_ONLY_ITEM_KEYS=new Set([...READ_ONLY_ITEM_KEYS,"completed_at","id"]),ITEM_FIELD_KEYS=new Set(["id","title","description","type","status","priority","tags","body","deadline","estimated_minutes","acceptance_criteria","definition_of_ready","order","rank","assignee",...ITEM_PROJECT_CONTEXT_KEYS,"affected_version","fixed_version","component","regression","customer_impact","close_reason","completed_at","dependencies","comments","notes","learnings","files","tests","docs","reminders","events","type_options"]),MUTATION_OPTION_KEYS={create:CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map(contract=>contract.target),update:UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map(contract=>contract.target),close:CLOSE_FLAG_CONTRACTS.map(contract=>flagToOptionKey(contract.flag)).filter(key=>key!=="reason"),release:RELEASE_FLAG_CONTRACTS.map(contract=>flagToOptionKey(contract.flag))};function parseAtomicMutationControls(value){const controls={},compensation=value.createCompensation;if(compensation!==void 0){if(compensation!=="close"&&compensation!=="delete")throw new PmCliError("createCompensation must be close or delete.",EXIT_CODE.USAGE);controls.createCompensation=compensation}for(const key of["lockTtlSeconds","lockWaitMs"]){const raw=value[key];if(raw===void 0)continue;const parsed=typeof raw=="number"||typeof raw=="string"&&raw.trim().length>0?Number(raw):Number.NaN;if(!Number.isSafeInteger(parsed)||parsed<=0)throw new PmCliError(`${key} must be a positive safe integer.`,EXIT_CODE.USAGE);controls[key]=parsed}return controls}function isPlainObject(value){if(typeof value!="object"||value===null||Array.isArray(value))return!1;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function nearestKey(key,candidates){const limit=Math.max(1,Math.min(3,Math.floor(key.length/4)+1));let best;for(const candidate of candidates){const distance=levenshteinDistanceWithinLimit(key,candidate,limit);distance!==null&&(best===void 0||distance<best.distance)&&(best={key:candidate,distance})}return best?.key}function unknownKeyError(scope,key,candidates){const suggestion=nearestKey(key,candidates);return new PmCliError(suggestion===void 0?`${scope} does not recognize key "${key}". Allowed keys: ${candidates.join(", ")}.`:`${scope} does not recognize key "${key}". Did you mean "${suggestion}"?`,EXIT_CODE.USAGE,{code:"stdin_json_unknown_key",required:`Use documented ${scope} keys only.`,why:"Strict structured input prevents silent typos from creating incomplete or unintended mutations.",nextSteps:[suggestion===void 0?`Remove or correct "${key}".`:`Replace "${key}" with "${suggestion}".`]})}function parseJsonValue(input,label){try{return JSON.parse(input)}catch(error){const detail=error instanceof Error?error.message:String(error);throw new PmCliError(`${label} must be valid JSON: ${detail}`,EXIT_CODE.USAGE,{code:"stdin_json_invalid",required:`Pipe one valid JSON ${label.toLowerCase()} into stdin.`,why:"Structured mutation input must parse deterministically before any tracker write begins."})}}function validateMutationObject(value,index){if(!isPlainObject(value))throw new PmCliError(`Mutation ${index+1} must be an object.`,EXIT_CODE.USAGE);for(const key of Object.keys(value))if(!MUTATION_ROW_KEYS.includes(key))throw unknownKeyError(`mutation ${index+1}`,key,MUTATION_ROW_KEYS);return value}function validateMutationOptions(options,op,index){if(options!==void 0){if(!isPlainObject(options))throw new PmCliError(`Mutation ${index+1} options must be an object.`,EXIT_CODE.USAGE);for(const key of Object.keys(options))if(!MUTATION_OPTION_KEYS[op].includes(key))throw unknownKeyError(`mutation ${index+1} ${op} options`,key,MUTATION_OPTION_KEYS[op]);return options}}function validateMutationRow(value,index){const mutation=validateMutationObject(value,index),{op,id,reason}=mutation;if(op!=="create"&&op!=="update"&&op!=="close"&&op!=="release")throw new PmCliError(`Mutation ${index+1} op must be create, update, close, or release.`,EXIT_CODE.USAGE);const options=validateMutationOptions(mutation.options,op,index);if(typeof id!="string"||id.trim().length===0)throw new PmCliError(`Mutation ${index+1} requires a non-empty id.`,EXIT_CODE.USAGE);const normalizedId=id.trim();if(op==="close"){if(typeof reason!="string"||reason.trim().length===0)throw new PmCliError(`Mutation ${index+1} close requires a non-empty reason.`,EXIT_CODE.USAGE);return{op,id:normalizedId,reason:reason.trim(),...options===void 0?{}:{options}}}if(op==="release")return{op,id:normalizedId,...options===void 0?{}:{options}};if(!isPlainObject(options))throw new PmCliError(`Mutation ${index+1} ${op} requires an options object.`,EXIT_CODE.USAGE);return{op,id:normalizedId,options}}function parseItemMutationBatch(input){const parsed=parseJsonValue(input,"Mutation batch"),rows=Array.isArray(parsed)?parsed:isPlainObject(parsed)&&Array.isArray(parsed.mutations)?parsed.mutations:void 0;if(rows===void 0||rows.length===0)throw new PmCliError("Mutation batch must be a non-empty JSON array or an object with a non-empty mutations array.",EXIT_CODE.USAGE);return rows.map((row,index)=>{const mutation=validateMutationRow(row,index);return mutation.options!==void 0&&preserveMutationStdinTokenLiterals(mutation.options),mutation})}function deriveReferencedItemId(transactionId,reference,idPrefix){const digest=crypto.createHash("sha256").update(transactionId).update("\0").update(reference).digest("hex"),token=BigInt(`0x${digest.slice(0,16)}`).toString(36).padStart(13,"0").slice(-12);return`${normalizePrefix(idPrefix)}${token}`}function replaceExactMutationReference(value,references){if(typeof value!="string"||!value.startsWith("@"))return value;const reference=value.slice(1),resolved=references[reference];if(resolved===void 0)throw new PmCliError(`Unknown mutation reference "${value}".`,EXIT_CODE.USAGE);return resolved}function replaceDependencyReference(value,references){if(typeof value!="string")return value;const resolvedValue=value.replace(MUTATION_DEPENDENCY_REFERENCE_PATTERN,(_match,prefix,reference)=>{const resolved=references[reference];if(resolved===void 0)throw new PmCliError(`Unknown mutation reference "@${reference}".`,EXIT_CODE.USAGE);return`${prefix}id=${resolved}`});if(/(^|,)id=@/u.test(resolvedValue))throw new PmCliError(`Malformed mutation reference in dependency "${value}".`,EXIT_CODE.USAGE);return resolvedValue}function referencedAliases(value){return typeof value!="string"?[]:value.startsWith("@")?[value.slice(1)]:[...value.matchAll(MUTATION_DEPENDENCY_REFERENCE_PATTERN)].map(match=>match[1]).filter(reference=>reference!==void 0)}function assertAcyclicCreateReferences(rows){const graph=new Map;for(const row of rows){if(row.op!=="create"||typeof row.ref!="string")continue;const mutationOptions=isPlainObject(row.options)?row.options:{},dependencies=Array.isArray(mutationOptions.dep)?mutationOptions.dep:mutationOptions.dep===void 0?[]:[mutationOptions.dep];graph.set(row.ref,[mutationOptions.parent,mutationOptions.blockedBy,...dependencies].flatMap(referencedAliases))}const visiting=new Set,visited=new Set,visit=reference=>{if(visiting.has(reference))throw new PmCliError(`Mutation reference cycle includes "@${reference}".`,EXIT_CODE.USAGE);if(!visited.has(reference)){visiting.add(reference);for(const dependency of graph.get(reference))graph.has(dependency)&&visit(dependency);visiting.delete(reference),visited.add(reference)}};for(const reference of graph.keys())visit(reference)}function parseMutationDocumentRows(input){const parsed=parseJsonValue(input,"Mutation document"),envelope=Array.isArray(parsed)?{schema_version:1,mutations:parsed}:parsed;if(!isPlainObject(envelope))throw new PmCliError("Mutation document must be a JSON array or object.",EXIT_CODE.USAGE);for(const key of Object.keys(envelope))if(!MUTATION_DOCUMENT_KEYS.includes(key))throw unknownKeyError("mutation document",key,MUTATION_DOCUMENT_KEYS);if(envelope.schema_version!==void 0&&envelope.schema_version!==1)throw new PmCliError("Mutation document schema_version must be 1.",EXIT_CODE.USAGE);if(!Array.isArray(envelope.mutations)||envelope.mutations.length===0)throw new PmCliError("Mutation document requires a non-empty mutations array.",EXIT_CODE.USAGE);return envelope.mutations.map((row,index)=>{if(!isPlainObject(row))throw new PmCliError(`Mutation ${index+1} must be an object.`,EXIT_CODE.USAGE);for(const key of Object.keys(row))if(!REFERENCED_MUTATION_ROW_KEYS.includes(key))throw unknownKeyError(`mutation ${index+1}`,key,REFERENCED_MUTATION_ROW_KEYS);return{...row}})}function collectMutationReferences(rows,options){const references=Object.create(null);for(const[index,row]of rows.entries())if(row.ref!==void 0){if(row.op!=="create")throw new PmCliError(`Mutation ${index+1} ref is only valid for create operations.`,EXIT_CODE.USAGE);if(typeof row.ref!="string"||!MUTATION_REFERENCE_PATTERN.test(row.ref))throw new PmCliError(`Mutation ${index+1} ref must match ${MUTATION_REFERENCE_PATTERN.source}.`,EXIT_CODE.USAGE);if(references[row.ref]!==void 0)throw new PmCliError(`Duplicate mutation ref "${row.ref}".`,EXIT_CODE.USAGE);references[row.ref]=typeof row.id=="string"&&row.id.trim().length>0?normalizeItemId(row.id,options.idPrefix):deriveReferencedItemId(options.transactionId,row.ref,options.idPrefix)}return references}function resolveItemMutationDocument(input,options){const rows=parseMutationDocumentRows(input),references=collectMutationReferences(rows,options);assertAcyclicCreateReferences(rows);const resolvedRows=rows.map(row=>{const resolved={...row},reference=typeof resolved.ref=="string"?resolved.ref:void 0;if(delete resolved.ref,reference!==void 0?resolved.id=references[reference]:resolved.id=replaceExactMutationReference(resolved.id,references),isPlainObject(resolved.options)){const mutationOptions={...resolved.options};for(const key of["parent","blockedBy"])mutationOptions[key]!==void 0&&(mutationOptions[key]=replaceExactMutationReference(mutationOptions[key],references));Array.isArray(mutationOptions.dep)?mutationOptions.dep=mutationOptions.dep.map(entry=>replaceDependencyReference(entry,references)):mutationOptions.dep!==void 0&&(mutationOptions.dep=replaceDependencyReference(mutationOptions.dep,references)),resolved.options=mutationOptions}return resolved}),mutations=parseItemMutationBatch(JSON.stringify(resolvedRows)),createIds=mutations.filter(mutation=>mutation.op==="create").map(mutation=>mutation.id);if(new Set(createIds).size!==createIds.length)throw new PmCliError("Duplicate resolved create id in mutation document.",EXIT_CODE.USAGE);return{schema_version:1,mutations,references}}function serializePairs(value,keys){return keys.filter(key=>value[key]!==void 0&&value[key]!==null).map(key=>{const rendered=String(value[key]);return`${key}=${/[",\n\r\\]/u.test(rendered)?JSON.stringify(rendered):rendered}`}).join(",")}function serializeObjectArray(value,keys,label){if(value!==void 0){if(!Array.isArray(value)||!value.every(isPlainObject))throw new PmCliError(`${label} must be an array of objects.`,EXIT_CODE.USAGE);return value.map(entry=>serializePairs(entry,keys))}}function scalarItemOption(key,value){return key==="tags"&&Array.isArray(value)&&value.every(entry=>typeof entry=="string")?JSON.stringify(value):typeof value=="number"||typeof value=="boolean"?String(value):value}function appendFacetOptions(target,source,skipPersistedAnnotations=!1){const mappings=[["dependencies","dep",["id","kind","created_at","author","source_kind"]],["comments","comment",["text","created_at","author"]],["notes","note",["text","created_at","author"]],["learnings","learning",["text","created_at","author"]],["files","file",["path","scope","note"]],["tests","test",["command","scope","timeout_seconds"]],["docs","doc",["path","scope","note"]],["reminders","reminder",["at","text","created_at"]],["events","event",["start_at","end_at","title","location"]],["type_options","typeOption",["key","value"]]];for(const[sourceKey,targetKey,keys]of mappings){const sourceValue=source[sourceKey],serialized=serializeObjectArray(skipPersistedAnnotations&&["comments","notes","learnings"].includes(sourceKey)&&Array.isArray(sourceValue)?sourceValue.filter(entry=>!isPlainObject(entry)||entry.created_at===void 0):sourceValue,keys,sourceKey);serialized!==void 0&&serialized.length>0&&(target[targetKey]=serialized)}}function validateItemDocumentKeys(parsed,envelope){for(const key of Object.keys(parsed)){if(envelope){if(!ITEM_ENVELOPE_KEYS.includes(key))throw unknownKeyError("item envelope",key,ITEM_ENVELOPE_KEYS);continue}if(!ITEM_FIELD_KEYS.has(key)&&nearestKey(key,[...ITEM_FIELD_KEYS])===void 0&&nearestKey(key,ITEM_ENVELOPE_KEYS)==="item")throw unknownKeyError("item envelope",key,ITEM_ENVELOPE_KEYS)}}function resolveItemDocument(parsed){const envelope=Object.hasOwn(parsed,"item");validateItemDocumentKeys(parsed,envelope);const item=envelope?parsed.item:parsed;if(!isPlainObject(item))throw new PmCliError("Item document item must be an object.",EXIT_CODE.USAGE);return{item,...envelope&&isPlainObject(parsed.linked)?{linked:parsed.linked}:{}}}function appendItemDocumentEntry(documentOptions,key,value,mode){if(!(mode==="update"?UPDATE_READ_ONLY_ITEM_KEYS:READ_ONLY_ITEM_KEYS).has(key)){if(!ITEM_FIELD_KEYS.has(key)){if(nearestKey(key,[...ITEM_FIELD_KEYS])!==void 0)throw unknownKeyError("item document",key,[...ITEM_FIELD_KEYS]);const fields=Array.isArray(documentOptions.field)?documentOptions.field:[];fields.push(serializePairs({key,value:typeof value=="string"?value:JSON.stringify(value)},["key","value"])),documentOptions.field=fields;return}["dependencies","comments","notes","learnings","files","tests","docs","reminders","events","type_options"].includes(key)||(documentOptions[key.replaceAll(/_([a-z])/gu,(_match,letter)=>letter.toUpperCase())]=scalarItemOption(key,value))}}function itemDocumentToMutationOptions(input,mode,explicitOptions={}){const parsed=parseJsonValue(input,"Item document");if(!isPlainObject(parsed))throw new PmCliError("Item document must be a JSON object.",EXIT_CODE.USAGE);const{item,linked}=resolveItemDocument(parsed),documentOptions={};for(const[key,value]of Object.entries(item))appendItemDocumentEntry(documentOptions,key,value,mode);if(appendFacetOptions(documentOptions,item,mode==="update"),linked!==void 0&&appendFacetOptions(documentOptions,linked),mode==="update"){const annotations=Object.fromEntries(["comments","notes","learnings"].filter(key=>Object.hasOwn(item,key)).map(key=>[key,item[key].filter(entry=>isPlainObject(entry)&&entry.created_at!==void 0)]));Object.keys(annotations).length>0&&(documentOptions.documentAnnotationSnapshot=annotations)}return{...documentOptions,...Object.fromEntries(Object.entries(explicitOptions).filter(([,value])=>value!==void 0)),stdinJson:void 0}}function validateItemMutationRows(value){return parseItemMutationBatch(JSON.stringify(value))}export{itemDocumentToMutationOptions,parseAtomicMutationControls,parseItemMutationBatch,resolveItemMutationDocument,validateItemMutationRows};
|
|
1
|
+
(function(){try{var e=typeof window<"u"?window:typeof global<"u"?global:typeof globalThis<"u"?globalThis:typeof self<"u"?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ccd80f77-f619-5cd8-a792-cc5d9c733ebf")}catch{}})();import crypto from"node:crypto";import{normalizeItemId,normalizePrefix}from"../core/item/id.js";import{preserveMutationStdinTokenLiterals}from"../core/item/parse.js";import{EXIT_CODE,ITEM_PROJECT_CONTEXT_KEYS}from"../core/shared/constants.js";import{PmCliError}from"../core/shared/errors.js";import{levenshteinDistanceWithinLimit}from"../core/shared/levenshtein.js";import{CLOSE_FLAG_CONTRACTS,RELEASE_FLAG_CONTRACTS}from"./cli-contracts/flag-contracts.js";import{CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS,UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS}from"./cli-contracts/commander-mutation-options.js";import{runtimeFieldOptionTarget}from"../core/schema/runtime-schema.js";import{readRuntimeFieldOptionValue}from"../core/schema/runtime-field-values.js";const MUTATION_ROW_KEYS=["op","id","reason","options"],REFERENCED_MUTATION_ROW_KEYS=[...MUTATION_ROW_KEYS,"ref"],MUTATION_DOCUMENT_KEYS=["schema_version","mutations"],MUTATION_REFERENCE_SOURCE="[a-z][a-z0-9._-]{0,63}",MUTATION_REFERENCE_PATTERN=new RegExp(`^${MUTATION_REFERENCE_SOURCE}$`,"u"),MUTATION_DEPENDENCY_REFERENCE_PATTERN=new RegExp(`(^|,)id=@(${MUTATION_REFERENCE_SOURCE})(?=,|$)`,"gu"),flagToOptionKey=flag=>flag.slice(2).replaceAll(/-([a-z])/gu,(_match,letter)=>letter.toUpperCase()),ITEM_ENVELOPE_KEYS=["item","linked","schedule","claim_state","children","omission_receipt","row_contract","context_intent"],READ_ONLY_ITEM_KEYS=new Set(["created_at","updated_at","closed_at","version","format_version","path","author","collection_counts","notes_count","tests_count"]),UPDATE_READ_ONLY_ITEM_KEYS=new Set([...READ_ONLY_ITEM_KEYS,"completed_at","id"]),ITEM_FIELD_KEYS=new Set(["id","title","description","type","status","priority","tags","body","deadline","estimated_minutes","acceptance_criteria","definition_of_ready","order","rank","assignee",...ITEM_PROJECT_CONTEXT_KEYS,"affected_version","fixed_version","component","regression","customer_impact","close_reason","completed_at","dependencies","comments","notes","learnings","files","tests","docs","reminders","events","type_options"]),MUTATION_OPTION_KEYS={create:CREATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map(contract=>contract.target),update:UPDATE_COMMANDER_OPTION_REGISTRATION_CONTRACTS.map(contract=>contract.target),close:CLOSE_FLAG_CONTRACTS.map(contract=>flagToOptionKey(contract.flag)).filter(key=>key!=="reason"),release:RELEASE_FLAG_CONTRACTS.map(contract=>flagToOptionKey(contract.flag))};function parseAtomicMutationControls(value){const controls={},compensation=value.createCompensation;if(compensation!==void 0){if(compensation!=="close"&&compensation!=="delete")throw new PmCliError("createCompensation must be close or delete.",EXIT_CODE.USAGE);controls.createCompensation=compensation}for(const key of["lockTtlSeconds","lockWaitMs"]){const raw=value[key];if(raw===void 0)continue;const parsed=typeof raw=="number"||typeof raw=="string"&&raw.trim().length>0?Number(raw):Number.NaN;if(!Number.isSafeInteger(parsed)||parsed<=0)throw new PmCliError(`${key} must be a positive safe integer.`,EXIT_CODE.USAGE);controls[key]=parsed}return controls}function isPlainObject(value){if(typeof value!="object"||value===null||Array.isArray(value))return!1;const prototype=Object.getPrototypeOf(value);return prototype===Object.prototype||prototype===null}function nearestKey(key,candidates){const limit=Math.max(1,Math.min(3,Math.floor(key.length/4)+1));let best;for(const candidate of candidates){const distance=levenshteinDistanceWithinLimit(key,candidate,limit);distance!==null&&(best===void 0||distance<best.distance)&&(best={key:candidate,distance})}return best?.key}function unknownKeyError(scope,key,candidates){const suggestion=nearestKey(key,candidates);return new PmCliError(suggestion===void 0?`${scope} does not recognize key "${key}". Allowed keys: ${candidates.join(", ")}.`:`${scope} does not recognize key "${key}". Did you mean "${suggestion}"?`,EXIT_CODE.USAGE,{code:"stdin_json_unknown_key",required:`Use documented ${scope} keys only.`,why:"Strict structured input prevents silent typos from creating incomplete or unintended mutations.",nextSteps:[suggestion===void 0?`Remove or correct "${key}".`:`Replace "${key}" with "${suggestion}".`]})}function parseJsonValue(input,label){try{return JSON.parse(input)}catch(error){const detail=error instanceof Error?error.message:String(error);throw new PmCliError(`${label} must be valid JSON: ${detail}`,EXIT_CODE.USAGE,{code:"stdin_json_invalid",required:`Pipe one valid JSON ${label.toLowerCase()} into stdin.`,why:"Structured mutation input must parse deterministically before any tracker write begins."})}}function validateMutationObject(value,index){if(!isPlainObject(value))throw new PmCliError(`Mutation ${index+1} must be an object.`,EXIT_CODE.USAGE);for(const key of Object.keys(value))if(!MUTATION_ROW_KEYS.includes(key))throw unknownKeyError(`mutation ${index+1}`,key,MUTATION_ROW_KEYS);return value}function validateMutationOptions(options,op,index){if(options!==void 0){if(!isPlainObject(options))throw new PmCliError(`Mutation ${index+1} options must be an object.`,EXIT_CODE.USAGE);for(const key of Object.keys(options))if(!MUTATION_OPTION_KEYS[op].includes(key))throw unknownKeyError(`mutation ${index+1} ${op} options`,key,MUTATION_OPTION_KEYS[op]);return options}}function validateMutationRow(value,index){const mutation=validateMutationObject(value,index),{op,id,reason}=mutation;if(op!=="create"&&op!=="update"&&op!=="close"&&op!=="release")throw new PmCliError(`Mutation ${index+1} op must be create, update, close, or release.`,EXIT_CODE.USAGE);const options=validateMutationOptions(mutation.options,op,index);if(typeof id!="string"||id.trim().length===0)throw new PmCliError(`Mutation ${index+1} requires a non-empty id.`,EXIT_CODE.USAGE);const normalizedId=id.trim();if(op==="close"){if(typeof reason!="string"||reason.trim().length===0)throw new PmCliError(`Mutation ${index+1} close requires a non-empty reason.`,EXIT_CODE.USAGE);return{op,id:normalizedId,reason:reason.trim(),...options===void 0?{}:{options}}}if(op==="release")return{op,id:normalizedId,...options===void 0?{}:{options}};if(!isPlainObject(options))throw new PmCliError(`Mutation ${index+1} ${op} requires an options object.`,EXIT_CODE.USAGE);return{op,id:normalizedId,options}}function parseItemMutationBatch(input){const parsed=parseJsonValue(input,"Mutation batch"),rows=Array.isArray(parsed)?parsed:isPlainObject(parsed)&&Array.isArray(parsed.mutations)?parsed.mutations:void 0;if(rows===void 0||rows.length===0)throw new PmCliError("Mutation batch must be a non-empty JSON array or an object with a non-empty mutations array.",EXIT_CODE.USAGE);return rows.map((row,index)=>{const mutation=validateMutationRow(row,index);return mutation.options!==void 0&&preserveMutationStdinTokenLiterals(mutation.options),mutation})}function deriveReferencedItemId(transactionId,reference,idPrefix){const digest=crypto.createHash("sha256").update(transactionId).update("\0").update(reference).digest("hex"),token=BigInt(`0x${digest.slice(0,16)}`).toString(36).padStart(13,"0").slice(-12);return`${normalizePrefix(idPrefix)}${token}`}function replaceExactMutationReference(value,references){if(typeof value!="string"||!value.startsWith("@"))return value;const reference=value.slice(1),resolved=references[reference];if(resolved===void 0)throw new PmCliError(`Unknown mutation reference "${value}".`,EXIT_CODE.USAGE);return resolved}function replaceDependencyReference(value,references){if(typeof value!="string")return value;const resolvedValue=value.replace(MUTATION_DEPENDENCY_REFERENCE_PATTERN,(_match,prefix,reference)=>{const resolved=references[reference];if(resolved===void 0)throw new PmCliError(`Unknown mutation reference "@${reference}".`,EXIT_CODE.USAGE);return`${prefix}id=${resolved}`});if(/(^|,)id=@/u.test(resolvedValue))throw new PmCliError(`Malformed mutation reference in dependency "${value}".`,EXIT_CODE.USAGE);return resolvedValue}function referencedAliases(value){return typeof value!="string"?[]:value.startsWith("@")?[value.slice(1)]:[...value.matchAll(MUTATION_DEPENDENCY_REFERENCE_PATTERN)].map(match=>match[1]).filter(reference=>reference!==void 0)}function assertAcyclicCreateReferences(rows){const graph=new Map;for(const row of rows){if(row.op!=="create"||typeof row.ref!="string")continue;const mutationOptions=isPlainObject(row.options)?row.options:{},dependencies=Array.isArray(mutationOptions.dep)?mutationOptions.dep:mutationOptions.dep===void 0?[]:[mutationOptions.dep];graph.set(row.ref,[mutationOptions.parent,mutationOptions.blockedBy,...dependencies].flatMap(referencedAliases))}const visiting=new Set,visited=new Set,visit=reference=>{if(visiting.has(reference))throw new PmCliError(`Mutation reference cycle includes "@${reference}".`,EXIT_CODE.USAGE);if(!visited.has(reference)){visiting.add(reference);for(const dependency of graph.get(reference))graph.has(dependency)&&visit(dependency);visiting.delete(reference),visited.add(reference)}};for(const reference of graph.keys())visit(reference)}function parseMutationDocumentRows(input){const parsed=parseJsonValue(input,"Mutation document"),envelope=Array.isArray(parsed)?{schema_version:1,mutations:parsed}:parsed;if(!isPlainObject(envelope))throw new PmCliError("Mutation document must be a JSON array or object.",EXIT_CODE.USAGE);for(const key of Object.keys(envelope))if(!MUTATION_DOCUMENT_KEYS.includes(key))throw unknownKeyError("mutation document",key,MUTATION_DOCUMENT_KEYS);if(envelope.schema_version!==void 0&&envelope.schema_version!==1)throw new PmCliError("Mutation document schema_version must be 1.",EXIT_CODE.USAGE);if(!Array.isArray(envelope.mutations)||envelope.mutations.length===0)throw new PmCliError("Mutation document requires a non-empty mutations array.",EXIT_CODE.USAGE);return envelope.mutations.map((row,index)=>{if(!isPlainObject(row))throw new PmCliError(`Mutation ${index+1} must be an object.`,EXIT_CODE.USAGE);for(const key of Object.keys(row))if(!REFERENCED_MUTATION_ROW_KEYS.includes(key))throw unknownKeyError(`mutation ${index+1}`,key,REFERENCED_MUTATION_ROW_KEYS);return{...row}})}function collectMutationReferences(rows,options){const references=Object.create(null);for(const[index,row]of rows.entries())if(row.ref!==void 0){if(row.op!=="create")throw new PmCliError(`Mutation ${index+1} ref is only valid for create operations.`,EXIT_CODE.USAGE);if(typeof row.ref!="string"||!MUTATION_REFERENCE_PATTERN.test(row.ref))throw new PmCliError(`Mutation ${index+1} ref must match ${MUTATION_REFERENCE_PATTERN.source}.`,EXIT_CODE.USAGE);if(references[row.ref]!==void 0)throw new PmCliError(`Duplicate mutation ref "${row.ref}".`,EXIT_CODE.USAGE);references[row.ref]=typeof row.id=="string"&&row.id.trim().length>0?normalizeItemId(row.id,options.idPrefix):deriveReferencedItemId(options.transactionId,row.ref,options.idPrefix)}return references}function resolveItemMutationDocument(input,options){const rows=parseMutationDocumentRows(input),references=collectMutationReferences(rows,options);assertAcyclicCreateReferences(rows);const resolvedRows=rows.map(row=>{const resolved={...row},reference=typeof resolved.ref=="string"?resolved.ref:void 0;if(delete resolved.ref,reference!==void 0?resolved.id=references[reference]:resolved.id=replaceExactMutationReference(resolved.id,references),isPlainObject(resolved.options)){const mutationOptions={...resolved.options};for(const key of["parent","blockedBy"])mutationOptions[key]!==void 0&&(mutationOptions[key]=replaceExactMutationReference(mutationOptions[key],references));Array.isArray(mutationOptions.dep)?mutationOptions.dep=mutationOptions.dep.map(entry=>replaceDependencyReference(entry,references)):mutationOptions.dep!==void 0&&(mutationOptions.dep=replaceDependencyReference(mutationOptions.dep,references)),resolved.options=mutationOptions}return resolved}),mutations=parseItemMutationBatch(JSON.stringify(resolvedRows)),createIds=mutations.filter(mutation=>mutation.op==="create").map(mutation=>mutation.id);if(new Set(createIds).size!==createIds.length)throw new PmCliError("Duplicate resolved create id in mutation document.",EXIT_CODE.USAGE);return{schema_version:1,mutations,references}}function serializePairs(value,keys){return keys.filter(key=>value[key]!==void 0&&value[key]!==null).map(key=>{const rendered=String(value[key]);return`${key}=${/[",\n\r\\]/u.test(rendered)?JSON.stringify(rendered):rendered}`}).join(",")}function serializeObjectArray(value,keys,label){if(value!==void 0){if(!Array.isArray(value)||!value.every(isPlainObject))throw new PmCliError(`${label} must be an array of objects.`,EXIT_CODE.USAGE);return value.map(entry=>serializePairs(entry,keys))}}function scalarItemOption(key,value){return key==="tags"&&Array.isArray(value)&&value.every(entry=>typeof entry=="string")?JSON.stringify(value):typeof value=="number"||typeof value=="boolean"?String(value):value}function appendFacetOptions(target,source,skipPersistedAnnotations=!1){const mappings=[["dependencies","dep",["id","kind","created_at","author","source_kind"]],["comments","comment",["text","created_at","author"]],["notes","note",["text","created_at","author"]],["learnings","learning",["text","created_at","author"]],["files","file",["path","scope","note"]],["tests","test",["command","scope","timeout_seconds"]],["docs","doc",["path","scope","note"]],["reminders","reminder",["at","text","created_at"]],["events","event",["start_at","end_at","title","location"]],["type_options","typeOption",["key","value"]]];for(const[sourceKey,targetKey,keys]of mappings){const sourceValue=source[sourceKey],serialized=serializeObjectArray(skipPersistedAnnotations&&["comments","notes","learnings"].includes(sourceKey)&&Array.isArray(sourceValue)?sourceValue.filter(entry=>!isPlainObject(entry)||entry.created_at===void 0):sourceValue,keys,sourceKey);serialized!==void 0&&serialized.length>0&&(target[targetKey]=serialized)}}function validateItemDocumentKeys(parsed,envelope){for(const key of Object.keys(parsed)){if(envelope){if(!ITEM_ENVELOPE_KEYS.includes(key))throw unknownKeyError("item envelope",key,ITEM_ENVELOPE_KEYS);continue}if(!ITEM_FIELD_KEYS.has(key)&&nearestKey(key,[...ITEM_FIELD_KEYS])===void 0&&nearestKey(key,ITEM_ENVELOPE_KEYS)==="item")throw unknownKeyError("item envelope",key,ITEM_ENVELOPE_KEYS)}}function resolveItemDocument(parsed){const envelope=Object.hasOwn(parsed,"item");validateItemDocumentKeys(parsed,envelope);const item=envelope?parsed.item:parsed;if(!isPlainObject(item))throw new PmCliError("Item document item must be an object.",EXIT_CODE.USAGE);return{item,...envelope&&isPlainObject(parsed.linked)?{linked:parsed.linked}:{}}}function appendItemDocumentEntry(documentOptions,key,value,mode,runtimeFields,explicitOptions){if((mode==="update"?UPDATE_READ_ONLY_ITEM_KEYS:READ_ONLY_ITEM_KEYS).has(key))return;const definition=runtimeFields.find(field=>field.metadata_key===key&&field.commands.includes(mode));if(definition!==void 0){const explicitValue=readRuntimeFieldOptionValue(explicitOptions,definition);documentOptions[runtimeFieldOptionTarget(definition)]=explicitValue!==void 0?explicitValue:definition.type==="array"||definition.type==="object"?JSON.stringify(value):value;return}if(!ITEM_FIELD_KEYS.has(key)){if(nearestKey(key,[...ITEM_FIELD_KEYS])!==void 0)throw unknownKeyError("item document",key,[...ITEM_FIELD_KEYS]);const fields=Array.isArray(documentOptions.field)?documentOptions.field:[];fields.push(`${key}=${typeof value=="string"?value:JSON.stringify(value)}`),documentOptions.field=fields;return}["dependencies","comments","notes","learnings","files","tests","docs","reminders","events","type_options"].includes(key)||(documentOptions[key.replaceAll(/_([a-z])/gu,(_match,letter)=>letter.toUpperCase())]=scalarItemOption(key,value))}function itemDocumentToMutationOptions(input,mode,explicitOptions={},runtimeFields=[]){const parsed=parseJsonValue(input,"Item document");if(!isPlainObject(parsed))throw new PmCliError("Item document must be a JSON object.",EXIT_CODE.USAGE);const{item,linked}=resolveItemDocument(parsed),documentOptions={};for(const[key,value]of Object.entries(item))appendItemDocumentEntry(documentOptions,key,value,mode,runtimeFields,explicitOptions);if(appendFacetOptions(documentOptions,item,mode==="update"),linked!==void 0&&appendFacetOptions(documentOptions,linked),mode==="update"){const annotations=Object.fromEntries(["comments","notes","learnings"].filter(key=>Object.hasOwn(item,key)).map(key=>[key,item[key].filter(entry=>isPlainObject(entry)&&entry.created_at!==void 0)]));Object.keys(annotations).length>0&&(documentOptions.documentAnnotationSnapshot=annotations)}return{...documentOptions,...Object.fromEntries(Object.entries(explicitOptions).filter(([,value])=>value!==void 0)),...Array.isArray(documentOptions.field)&&Array.isArray(explicitOptions.field)?{field:[...documentOptions.field,...explicitOptions.field]}:{},stdinJson:void 0}}function validateItemMutationRows(value){return parseItemMutationBatch(JSON.stringify(value))}export{itemDocumentToMutationOptions,parseAtomicMutationControls,parseItemMutationBatch,resolveItemMutationDocument,validateItemMutationRows};
|
|
2
2
|
//# sourceMappingURL=structured-mutations.js.map
|
package/docs/QUALITY_EVIDENCE.md
CHANGED
|
@@ -1,7 +1,58 @@
|
|
|
1
1
|
# Test strength and documentation content
|
|
2
2
|
|
|
3
3
|
Tracked by [pm-zclzll](../.agents/pm/tasks/pm-zclzll.toon) and
|
|
4
|
-
[pm-dvwm](../.agents/pm/chores/pm-dvwm.toon).
|
|
4
|
+
[pm-dvwm](../.agents/pm/chores/pm-dvwm.toon). Release acceptance and token
|
|
5
|
+
comparison contracts: [pm-gh1428](../.agents/pm/issues/pm-gh1428.toon),
|
|
6
|
+
[pm-gh1429](../.agents/pm/issues/pm-gh1429.toon), and
|
|
7
|
+
[pm-agent-token-path](../.agents/pm/issues/pm-agent-token-path.toon).
|
|
8
|
+
|
|
9
|
+
## Installed context and immutable release recovery
|
|
10
|
+
|
|
11
|
+
The installed-agent journey requests `context --for orient --limit 1` with an
|
|
12
|
+
explicit 512-token output budget. A complete successful result must report the
|
|
13
|
+
actual population: zero items before creation and one closed item after the
|
|
14
|
+
lifecycle. An omitted result cannot establish either fact. When the installed
|
|
15
|
+
version refuses an infeasible budget, the verifier checks exit 2, the omission
|
|
16
|
+
and budget receipts, and the exact advertised `outputBudget: "unbounded"`
|
|
17
|
+
recovery. It then repeats the same intent and amount with that recovery. Older
|
|
18
|
+
control releases may return complete bounded context immediately. Every step
|
|
19
|
+
keeps its existing character ceiling and process deadline; reports retain the
|
|
20
|
+
observed exit status and all refusal/recovery output costs.
|
|
21
|
+
|
|
22
|
+
Unexpected process failures retain classified status, signal, spawn error and
|
|
23
|
+
both output streams, with credential redaction before 512-character excerpts.
|
|
24
|
+
Unit tests own malformed receipts and failure diagnostics. Real npm/Bun
|
|
25
|
+
installations and the hosted candidate/control platform matrix own compatibility
|
|
26
|
+
with the actual SDK-backed CLI. A stubbed empty context object cannot satisfy
|
|
27
|
+
the population assertions.
|
|
28
|
+
|
|
29
|
+
Exact-tag recovery preserves reviewed artifact controls before selecting tagged
|
|
30
|
+
source. The preserved directory retains `scripts/release` and its parent script
|
|
31
|
+
dependencies, including temporary lifecycle and cleanup modules. The executable
|
|
32
|
+
workflow regression copies those real controls, packs a foreign workspace with
|
|
33
|
+
real npm, and checks the committed artifact budget. Removing a transitive import
|
|
34
|
+
must fail with its original module error. Preserved controls stay outside the
|
|
35
|
+
source checkout; published immutable versions are verified without republishing.
|
|
36
|
+
|
|
37
|
+
## Portable agent-task token comparisons
|
|
38
|
+
|
|
39
|
+
Version 5 of the agent-task baseline declares
|
|
40
|
+
`comparison_scope: "plan_recovery_tracker_root_canonicalized"`. Each replay
|
|
41
|
+
continues to report exact `emitted_bytes`, `estimated_tokens`, and accounting
|
|
42
|
+
receipt bytes for the actual transport. Its `comparison_emitted_bytes` and
|
|
43
|
+
`comparison_estimated_tokens` replace only the validated tracker suffixes in
|
|
44
|
+
Plan inspection, next-action and plan-detail recovery commands with the fixed
|
|
45
|
+
`/replay/.agents/pm` comparison root. The actual payload retains executable
|
|
46
|
+
commands targeting its real fixture. Other facts, paths and commands remain
|
|
47
|
+
part of the cost.
|
|
48
|
+
|
|
49
|
+
Step, completed-task and composite ceilings use those comparison measurements.
|
|
50
|
+
The orientation selector uses the same cost basis. Missing, negative,
|
|
51
|
+
non-integer or non-finite measurements and mismatched comparison scopes fail
|
|
52
|
+
closed. Short and long temporary roots must produce the same verdict while
|
|
53
|
+
their real emitted-byte counts may differ. Output growth still fails the
|
|
54
|
+
unchanged ceilings; normalization does not waive accounting, payload parity,
|
|
55
|
+
required-field or executable-recovery checks.
|
|
5
56
|
|
|
6
57
|
## SDK mutation testing
|
|
7
58
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Full-history release reconciliation
|
|
2
|
+
|
|
3
|
+
Owner: [pm-q91qyd](../.agents/pm/issues/pm-q91qyd.toon).
|
|
4
|
+
Upstream trigger evidence: [pm-prrlce](../.agents/pm/issues/pm-prrlce.toon).
|
|
5
|
+
|
|
6
|
+
The Release Reliability workflow compares every dated section in the
|
|
7
|
+
package-generated changelog, every release tag advertised by `origin`, and
|
|
8
|
+
every version in anonymous public npm metadata. It runs on the existing daily
|
|
9
|
+
schedule and completion triggers, even when a rolling reliability report fails.
|
|
10
|
+
Its separate `release-ledgers-<run>-<attempt>` artifact contains the complete
|
|
11
|
+
input inventories, counts, findings and exception dispositions for offline replay.
|
|
12
|
+
|
|
13
|
+
Missing public registry versions are declared-but-undelivered releases. Missing
|
|
14
|
+
changelog sections are delivered-but-undocumented releases. A tag missing its
|
|
15
|
+
section is tagged-but-unsectioned; an identity can belong to several classes.
|
|
16
|
+
Every missing ledger appears explicitly in the report, without counting one
|
|
17
|
+
identity several times or dropping overlapping classes.
|
|
18
|
+
The `classes` object names these three populations separately; `findings`
|
|
19
|
+
retains one row per discrepant identity.
|
|
20
|
+
|
|
21
|
+
The first complete census on 2026-10-10 found 14 discrepant identities among
|
|
22
|
+
143 sections, 151 tags and 143 public versions. The exact historical signatures
|
|
23
|
+
and their individual dispositions are recorded in
|
|
24
|
+
[the reviewed exception policy](../config/release-ledger-exceptions.json).
|
|
25
|
+
Unpublished historical preparations are retained as abandoned publications;
|
|
26
|
+
historical documentation gaps remain visible without inventing shipped content.
|
|
27
|
+
These are explicit exceptions, not claims that the three historical sets agree.
|
|
28
|
+
No tag is deleted and no old artifact is published by this reporting path.
|
|
29
|
+
|
|
30
|
+
An exception matches one identity and one exact missing-ledger signature.
|
|
31
|
+
Additional missing evidence invalidates it. Correcting a historical gap makes
|
|
32
|
+
its exception stale and requires removing or revising that policy entry in a
|
|
33
|
+
reviewed change. A new discrepancy or stale exception fails the report.
|
|
34
|
+
Empty total inventories, incomplete populations, duplicate identities, malformed
|
|
35
|
+
remote output, registry failures and missing exception evidence refuse evaluation.
|
|
36
|
+
|
|
37
|
+
The registry request uses the same public npm metadata surface as release
|
|
38
|
+
acceptance, with explicit public registry selection, empty user/global
|
|
39
|
+
configuration and a fresh temporary cache. It runs outside the checkout and
|
|
40
|
+
removes inherited npm authentication/configuration. The report never archives
|
|
41
|
+
credentials, command output or environment values.
|
|
42
|
+
On Windows the collector invokes `cmd.exe` explicitly for npm's `.cmd` launcher.
|
|
43
|
+
The package name must satisfy the restricted registry identity grammar before
|
|
44
|
+
any child starts; the remaining arguments are fixed. Required Windows acceptance
|
|
45
|
+
executes real Git and public npm requests from paths containing spaces and `&`,
|
|
46
|
+
first proves that direct `npm.cmd` execution fails, then verifies the complete
|
|
47
|
+
inventory and temporary-directory cleanup through the repaired launcher.
|
|
48
|
+
No `shell: true` argument-array fallback or resource ceiling increase is used.
|
|
49
|
+
The parser supports npm 11's object and npm 12's single-result array, with
|
|
50
|
+
[captured public npm 12 metadata](../tests/fixtures/release-ledgers/npm-view-12.json)
|
|
51
|
+
as its format regression. Multiple package results refuse evaluation.
|
|
52
|
+
|
|
53
|
+
To run the actual collector after building:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
RELEASE_LEDGERS_OUTPUT=/absolute/report.json node --input-type=module -e '
|
|
57
|
+
import { main } from "./scripts/release/collect-release-ledgers.mjs";
|
|
58
|
+
const report = main();
|
|
59
|
+
console.log(JSON.stringify({ok:report.ok,counts:report.counts,findings:report.findings}));
|
|
60
|
+
process.exitCode = report.ok ? 0 : 1;
|
|
61
|
+
'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Packages and non-release projects can use the same pure SDK primitive with
|
|
65
|
+
their own named artifact inventories and decision references:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { reconcileArtifactLedgers } from "@unbrained/pm-cli/sdk/governance";
|
|
69
|
+
|
|
70
|
+
const report = reconcileArtifactLedgers([
|
|
71
|
+
{ name: "declared", complete: true, identities: ["artifact-1"] },
|
|
72
|
+
{ name: "delivered", complete: true, identities: ["artifact-1"] },
|
|
73
|
+
]);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`ArtifactLedgerSnapshot`, `ArtifactLedgerException`, `ArtifactLedgerFinding`
|
|
77
|
+
and `ArtifactLedgerReport` describe this harness-independent contract.
|
|
78
|
+
The evaluator performs no filesystem, clock or network operations and preserves
|
|
79
|
+
caller inputs. For offline replay, feed the saved `snapshots` and `exceptions`
|
|
80
|
+
back into `reconcileArtifactLedgers`.
|
|
81
|
+
|
|
82
|
+
## Tag-carried trigger declarations
|
|
83
|
+
|
|
84
|
+
New preparations create annotated tags whose bounded JSON message binds the
|
|
85
|
+
declared upstream event/origin, repository, run/attempt, reviewed source commit
|
|
86
|
+
and prepared target commit. Before publication, the Release workflow checks
|
|
87
|
+
the tag's peeled commit and source ancestry and compares the source declaration
|
|
88
|
+
with the exact GitHub run attempt, including repository, event, source SHA,
|
|
89
|
+
the producer workflow path and declared Auto Release run name. It independently
|
|
90
|
+
fetches the repository's default branch and requires the source run's branch to
|
|
91
|
+
match it. Auto Release uploads an immutable producer binding before pushing;
|
|
92
|
+
publication independently downloads that run attempt's artifact and compares
|
|
93
|
+
the exact tag name, target commit, tag object and canonical declaration. Missing,
|
|
94
|
+
expired, ambiguous or mismatched evidence stops publication; the artifact is
|
|
95
|
+
retained for 90 days. The verifier never assumes the default branch is named
|
|
96
|
+
`main`.
|
|
97
|
+
|
|
98
|
+
The separate `release-publication-origin-<run>-<attempt>` artifact retains the
|
|
99
|
+
tag-object SHA, upstream declaration, independent verification result and the
|
|
100
|
+
current publication/recovery trigger. An operator recovering a dispatcher
|
|
101
|
+
release therefore retains both origins. A rejected atomic push after sealing
|
|
102
|
+
refuses to rebase into a different target; a new preparation must establish a
|
|
103
|
+
new independent binding. Local preparation remains supported without pushing;
|
|
104
|
+
local `--push` refuses before Git inspection or release-asset mutation.
|
|
105
|
+
|
|
106
|
+
Historical lightweight tags and unrelated legacy annotations stay explicitly
|
|
107
|
+
unattributed only when publication matches the repository, tag name and exact
|
|
108
|
+
content-addressed object in the
|
|
109
|
+
[frozen pre-rollout inventory](../config/release-tag-legacy.json). That reviewed
|
|
110
|
+
inventory identifies all 152 origin tag objects observed before this rollout;
|
|
111
|
+
it is not extended automatically. Unknown lightweight tags, unrelated new
|
|
112
|
+
annotations and rewritten historical objects stop before publication, including
|
|
113
|
+
operator recovery. A missing or unreadable inventory fails closed. No origin is
|
|
114
|
+
inferred from a tag-push event or actor. Local preparation records an operator
|
|
115
|
+
declaration but hosted publication refuses it without a hosted producer.
|
|
116
|
+
Tag objects are content-addressed evidence; this does not assert that GitHub
|
|
117
|
+
release/tag immutability policy has been enabled.
|
|
118
|
+
|
|
119
|
+
The downstream tag-push workflow's run name still names `tag_push`; its verified
|
|
120
|
+
upstream origin lives in the summary and artifact. The remaining run-name and
|
|
121
|
+
operator-only manual follow-up contracts retain their existing owners
|
|
122
|
+
[pm-prrlce](../.agents/pm/issues/pm-prrlce.toon) and
|
|
123
|
+
[pm-uenmu9](../.agents/pm/issues/pm-uenmu9.toon). The one-automatic-release-per-UTC-day
|
|
124
|
+
guard and exact-tag recovery semantics are unchanged.
|
|
125
|
+
|
|
126
|
+
## Installed acceptance path cost
|
|
127
|
+
|
|
128
|
+
Owner: [pm-release-acceptance-path-cost](../.agents/pm/issues/pm-release-acceptance-path-cost.toon).
|
|
129
|
+
|
|
130
|
+
The disposable installed-consumer layout uses concise directory names because
|
|
131
|
+
`init` repeats its workspace path throughout its complete JSON guidance. Long
|
|
132
|
+
macOS temporary ancestry previously made an otherwise valid public artifact
|
|
133
|
+
exceed the raw 12,000-character init ceiling. Shorter private paths retain every
|
|
134
|
+
output field, lifecycle assertion, deadline and ceiling. The
|
|
135
|
+
[captured public output regression](../tests/fixtures/release-acceptance/README.md)
|
|
136
|
+
checks the raw cost for independent control/candidate local npm, global npm and
|
|
137
|
+
Bun layouts; actual installed sessions remain separate acceptance evidence.
|
|
138
|
+
|
|
139
|
+
## Publication metadata weight
|
|
140
|
+
|
|
141
|
+
Owner: [pm-998juj](../.agents/pm/tasks/pm-998juj.toon).
|
|
142
|
+
|
|
143
|
+
The publication stage compacts first-party JSON while leaving checkout files
|
|
144
|
+
readable and third-party runtime files unchanged. Every parsed value must survive
|
|
145
|
+
serialization exactly; invalid JSON and lossy representations, including negative
|
|
146
|
+
zero or overflowing numbers, stop packing. Real tarball regressions verify nested
|
|
147
|
+
extension data, Unicode, escaped text and the runtime ledger. Both committed size
|
|
148
|
+
profiles, required files and source-map exclusions remain enforced.
|
|
@@ -7,7 +7,7 @@ installed-package diagnostics are tracked by
|
|
|
7
7
|
[pm-q7c36n](../.agents/pm/issues/pm-q7c36n.toon).
|
|
8
8
|
|
|
9
9
|
The **Release Reliability** workflow evaluates the preceding 30 days after a
|
|
10
|
-
|
|
10
|
+
default-branch Auto Release or Nightly Validation completes, daily at nominal 04:35 UTC, and on manual
|
|
11
11
|
dispatch. It saves a JSON report and fails its own check when policy is breached.
|
|
12
12
|
The report is independent of candidate correctness: historical failures must
|
|
13
13
|
remain visible while a reviewed repair can still pass publication gates.
|
|
@@ -34,6 +34,10 @@ by release preparation, not the inferred cron occurrence. A delayed run cannot
|
|
|
34
34
|
backdate a tag. Reporting never creates, retargets, retries, or publishes a tag.
|
|
35
35
|
The unchanged [release recovery rules](RELEASING.md#failure-handling) apply.
|
|
36
36
|
|
|
37
|
+
The independent [full-history ledger report](RELEASE_LEDGER_RECONCILIATION.md)
|
|
38
|
+
also reconciles public registry versions, release tags and generated sections.
|
|
39
|
+
It preserves every historical exception and fails on new or changed drift.
|
|
40
|
+
|
|
37
41
|
## Evidence and denominator
|
|
38
42
|
|
|
39
43
|
The collector paginates Auto Release runs over an explicit half-open UTC window
|
|
@@ -48,6 +52,11 @@ be uploaded, the collector reads the declared run name. Historical dispatches
|
|
|
48
52
|
without either declaration appear in `pre_attribution_gap`; the failed
|
|
49
53
|
2026-09-28 morning dispatch cannot be attributed from its old run record.
|
|
50
54
|
Dispatcher rows retain creation time, queue delay, outcome, and failure stage.
|
|
55
|
+
The `origins` map also retains independent `native_schedule`, `operator`, and
|
|
56
|
+
`blocker_retry` series, each with original counts, failure stages and outcome
|
|
57
|
+
evidence completeness. Undeclared historical runs remain explicit attribution
|
|
58
|
+
gaps; event or actor never supplies an invented origin. These diagnostic series
|
|
59
|
+
do not alter the native scheduled policy denominator.
|
|
51
60
|
They omit nominal occurrence and dispatch delay because the native 02:35 UTC
|
|
52
61
|
cron is not the dispatcher's clock; the scheduled timing policy does not apply
|
|
53
62
|
to those rows.
|
|
@@ -85,8 +94,54 @@ hosted acceptance job limit.
|
|
|
85
94
|
|
|
86
95
|
The report is a rolling observation, not a permanent archive of all releases.
|
|
87
96
|
Retain exported receipts externally if a longer historical window is needed.
|
|
88
|
-
It does not
|
|
89
|
-
|
|
97
|
+
It does not change the manual same-day version policy or propagate upstream
|
|
98
|
+
origin through historical lightweight tags. Those remaining outcomes retain
|
|
99
|
+
their existing PM owners.
|
|
100
|
+
|
|
101
|
+
## Nightly recurrence and public SDK
|
|
102
|
+
|
|
103
|
+
[pm-2zjs0g](../.agents/pm/issues/pm-2zjs0g.toon) owns the nightly recurrence meter.
|
|
104
|
+
The same reporting workflow builds trusted default-branch code and writes a
|
|
105
|
+
separate `nightly-reliability-<run>-<attempt>` artifact, including when release
|
|
106
|
+
reporting is already red. Its [versioned policy](../config/nightly-reliability-policy.json)
|
|
107
|
+
requires seven completed original attempts per family and at most 10% failures
|
|
108
|
+
over 30 days. Breaches fail the operational report and appear in the workflow
|
|
109
|
+
summary; they remain distinct from checks evaluating a candidate repair.
|
|
110
|
+
|
|
111
|
+
The collector derives current leg identities from `nightly.yml` and each run's
|
|
112
|
+
applicable population from the workflow at its original head SHA. Immutable
|
|
113
|
+
definitions are cached within one census; legacy OS/Node matrices and today's
|
|
114
|
+
labelled shards both retain their actual generations. Newly introduced families
|
|
115
|
+
never gain synthetic pending attempts in older runs. It retains historical
|
|
116
|
+
observed legs, retrieves attempt 1 after reruns, and reads both open
|
|
117
|
+
and closed alert issues plus their complete comment pages. Original failures,
|
|
118
|
+
distinct affected UTC days and observed closure durations survive alert repair.
|
|
119
|
+
Every new alert carries the stable PM recurrence owner and a platform/Node family
|
|
120
|
+
identity. Counts do not depend on a maintained list of occurrence IDs. An absent
|
|
121
|
+
job is pending evidence; an empty family has a null rate and fails coverage.
|
|
122
|
+
|
|
123
|
+
Alert closure is the recorded latency endpoint, not proof of a product fix.
|
|
124
|
+
When one alert episode links several failed runs, its shared closure is the
|
|
125
|
+
endpoint for each recorded occurrence's alert-closure latency. This does not
|
|
126
|
+
assert that an independent per-attempt product repair was verified.
|
|
127
|
+
Product versus infrastructure cause stays `unknown` until independent evidence
|
|
128
|
+
classifies it. Repairs at or beyond the report cutoff remain unresolved in that
|
|
129
|
+
historical window. API errors, incomplete search results, missing pages,
|
|
130
|
+
duplicate identities and ambiguous jobs refuse a complete-census claim.
|
|
131
|
+
|
|
132
|
+
Packages can reuse the harness-independent evaluator through
|
|
133
|
+
`evaluateReliabilityWindow` from `@unbrained/pm-cli/sdk/governance`. Supply
|
|
134
|
+
`ReliabilityAttempt` records with unique `id`, stable `family`, `started_at`,
|
|
135
|
+
original `outcome` and optional `repaired_at`/`cause`, plus an explicit
|
|
136
|
+
`ReliabilityWindowPolicy` population, half-open window, maximum failure fraction
|
|
137
|
+
and minimum completed count. The typed result preserves per-family findings,
|
|
138
|
+
unknown causes and repair observations. No GitHub dependency, filesystem tracker,
|
|
139
|
+
clock read or network call is required by this SDK primitive.
|
|
140
|
+
|
|
141
|
+
For reproducible offline evaluation, retain the report's `attempts` and `policy`
|
|
142
|
+
with its exact window; rerun the SDK evaluator against those records. The GitHub
|
|
143
|
+
adapter accepts a caller-chosen clock and a 1–90 day policy window, allowing
|
|
144
|
+
historical family reconstruction while keeping the production 30-day policy fixed.
|
|
90
145
|
|
|
91
146
|
Provider contracts: [workflow run attempts](https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run-attempt)
|
|
92
147
|
and [completion-triggered workflows](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#workflow_run).
|
package/docs/RELEASING.md
CHANGED
|
@@ -172,6 +172,17 @@ Pipeline entrypoint:
|
|
|
172
172
|
node scripts/release/run-release-pipeline.mjs
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
+
Tracked by [pm-prrlce](../.agents/pm/issues/pm-prrlce.toon): hosted preparation
|
|
176
|
+
uses `--push --defer-push` to write an exact producer binding. Auto Release uploads
|
|
177
|
+
`release-producer-binding-<attempt>` before verifying and atomically pushing its
|
|
178
|
+
prepared refs. Publication independently fetches the verified run's artifact and
|
|
179
|
+
compares the tag name, target commit, tag object and declaration. Missing, expired,
|
|
180
|
+
ambiguous or mismatched evidence fails closed; artifact retention is 90 days.
|
|
181
|
+
A branch advance after sealing refuses publication rather than rebasing into a
|
|
182
|
+
different target. Local `--push` refuses before Git inspection or asset mutation;
|
|
183
|
+
local preparation without pushing remains supported. Historical admission remains
|
|
184
|
+
limited to the reviewed exact-object inventory.
|
|
185
|
+
|
|
175
186
|
The pipeline performs:
|
|
176
187
|
|
|
177
188
|
1. change detection + one-release-per-day guard
|
|
@@ -421,14 +432,28 @@ maintainer supplies
|
|
|
421
432
|
pair that acknowledgement with migration guidance, compatible extension
|
|
422
433
|
version bounds where applicable, and the next eligible date-based release.
|
|
423
434
|
|
|
424
|
-
5.
|
|
435
|
+
5. Land the implementation through a reviewed pull request, then use hosted
|
|
436
|
+
preparation for publication.
|
|
437
|
+
|
|
438
|
+
Local preparation without `--push` is a diagnostic preview. Its local producer
|
|
439
|
+
declaration cannot authorize publication. Once the reviewed implementation is on
|
|
440
|
+
`main`, dispatch the existing Auto Release workflow from that default branch:
|
|
425
441
|
|
|
426
442
|
```bash
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
git push origin v<version>
|
|
443
|
+
gh workflow run auto-release.yml --ref main \
|
|
444
|
+
-f push=true -f dry_run=false -f telemetry_mode=off -f trigger_origin=operator
|
|
430
445
|
```
|
|
431
446
|
|
|
447
|
+
Hosted preparation runs the mandatory gates, creates the version commit and
|
|
448
|
+
annotated tag, and seals their exact producer binding in an immutable artifact
|
|
449
|
+
before verifying and atomically pushing either ref. It waits for the single
|
|
450
|
+
tag-driven Release workflow to publish and accept that exact package. Do not push
|
|
451
|
+
locally prepared tags or invoke `npm publish` separately.
|
|
452
|
+
|
|
453
|
+
If today's immutable tag already exists, use the exact-tag recovery procedure
|
|
454
|
+
below. A local diagnostic tag does not prove hosted preparation or publication;
|
|
455
|
+
never move a published tag or mint a second automatic version for the same UTC day.
|
|
456
|
+
|
|
432
457
|
## GitHub Workflow
|
|
433
458
|
|
|
434
459
|
`.github/workflows/release.yml` runs on `v*.*.*` tags and handles:
|
|
@@ -454,7 +479,11 @@ git push origin v<version>
|
|
|
454
479
|
- a base npm pack dry run, optional Sentry debug-ID injection, a second
|
|
455
480
|
`sentry-injected` packlist budget over the exact publishable bytes, and only
|
|
456
481
|
then optional Sentry release metadata/upload/finalization and the npx tarball
|
|
457
|
-
smoke test
|
|
482
|
+
smoke test. Sentry release metadata and sourcemaps are uploaded only from
|
|
483
|
+
tag sources. Recovery of an already-published version from reviewed `main`
|
|
484
|
+
preserves that version's existing Sentry commits and sourcemaps; local debug-ID
|
|
485
|
+
injection still validates the candidate artifact budget without changing the
|
|
486
|
+
published release's symbolication data.
|
|
458
487
|
- generated release notes from changelog plus sanitized tracker metadata. The
|
|
459
488
|
tracker read projects only release-relevant fields and explicitly removes row
|
|
460
489
|
and token limits. The generator validates the completeness receipt before
|
package/docs/SDK.md
CHANGED
|
@@ -1552,8 +1552,22 @@ accepts either a direct `ItemDocument` or the envelope returned by
|
|
|
1552
1552
|
`pm get <id> --json`, strips read-only metadata, maps canonical snake-case item
|
|
1553
1553
|
fields to typed mutation options, and serializes linked facets through the same
|
|
1554
1554
|
repeatable option forms as create/update. Explicit options win over document
|
|
1555
|
-
values.
|
|
1556
|
-
|
|
1555
|
+
values. Pass `resolveRuntimeFieldRegistry(settings.schema).definitions` as the
|
|
1556
|
+
fourth argument to route workspace metadata keys through their declared SDK
|
|
1557
|
+
option names. CLI stdin automatically resolves the selected workspace schema.
|
|
1558
|
+
Schema aliases override document values through the same field registry.
|
|
1559
|
+
Declared extension fields use raw `name=value` assignments; explicit `field`
|
|
1560
|
+
entries override only matching names, preserving other document fields. Strings
|
|
1561
|
+
retain commas, equals signs, quotes, Unicode, and newlines; numbers, booleans,
|
|
1562
|
+
objects, and arrays retain their types. Unknown fields and near-miss built-in
|
|
1563
|
+
keys refuse before item or history writes.
|
|
1564
|
+
|
|
1565
|
+
Tracked by [pm-stdin-schema-fields](../.agents/pm/issues/pm-stdin-schema-fields.toon).
|
|
1566
|
+
Stored schema containers retain their decoded shape; repeated CLI container
|
|
1567
|
+
flags still select the final supplied value. Normal SDK item operations refuse
|
|
1568
|
+
ahead-of-runtime storage markers before schema or body validation and retain
|
|
1569
|
+
item identity/path in the structured error. Integrity diagnostics preserve
|
|
1570
|
+
future markers for inspection.
|
|
1557
1571
|
|
|
1558
1572
|
The built-in adapters stay deliberately thin:
|
|
1559
1573
|
|
package/docs/TESTING.md
CHANGED
|
@@ -142,6 +142,42 @@ failed, skipped, stale, ambiguous, and different-tree results all fail.
|
|
|
142
142
|
`pnpm release:gates` includes the same non-skippable verification, so run it
|
|
143
143
|
only after the pushed reviewed head's hosted analyzers are terminal.
|
|
144
144
|
|
|
145
|
+
## Published Workspace Compatibility
|
|
146
|
+
|
|
147
|
+
Tracked by [pm-pae3wo](../.agents/pm/tasks/pm-pae3wo.toon).
|
|
148
|
+
Run `node scripts/release/workspace-compatibility.mjs --json` after building.
|
|
149
|
+
The policy in `scripts/release/workspace-compatibility-policy.json` requires
|
|
150
|
+
the two immediately preceding stable calendar releases. Each installed release
|
|
151
|
+
writes a disposable workspace read by the current runtime, and the current
|
|
152
|
+
runtime writes one read by that release. Exact scalar and container values must
|
|
153
|
+
survive CLI and public SDK reads, ordinary writes with their registration
|
|
154
|
+
disabled, history restoration, native Git merges, reconciliation, and history
|
|
155
|
+
verification. Missing releases, metadata loss, or invalid merged histories fail
|
|
156
|
+
the gate. CI, nightly quality, release publication, and local preflight require
|
|
157
|
+
this matrix alongside the independent legacy Markdown migration check.
|
|
158
|
+
|
|
159
|
+
Metadata probes and installs explicitly use the public npm registry with
|
|
160
|
+
separate empty user/global configurations and a fresh temporary cache. Inherited
|
|
161
|
+
npm settings and registry-auth environment variables are removed, so host
|
|
162
|
+
configuration cannot change the artifacts selected by the declared window.
|
|
163
|
+
|
|
164
|
+
The current item-operation boundary refuses a stored `pm_format_version`
|
|
165
|
+
ahead of its supported version with `item_format_version_unsupported` and
|
|
166
|
+
upgrade guidance. Reads, updates, deletion, and restoration preserve the
|
|
167
|
+
original item/history on refusal, including documents whose metadata/body
|
|
168
|
+
shapes are invalid for the current runtime. Admission precedes schema validation
|
|
169
|
+
so restore cannot misclassify newer data as ordinary corruption. Static fixture
|
|
170
|
+
modules read separate JSON data; published SDK probes use the public package
|
|
171
|
+
entrypoint from their own consumer directory. Raw parsers still preserve future version
|
|
172
|
+
markers for integrity inspection; inventory diagnostics are not permission to
|
|
173
|
+
rewrite an unsupported item.
|
|
174
|
+
|
|
175
|
+
Run the matrix from the built workspace root. Exact-tag release recovery retains
|
|
176
|
+
the reviewed matrix controls, policy and static fixtures outside the checkout,
|
|
177
|
+
then tests the selected tagged runtime through its manifest and public SDK. A
|
|
178
|
+
historical tag need not contain newly added gate scripts; recovery still runs
|
|
179
|
+
the mandatory matrix without rewriting the tagged source or skipping policy.
|
|
180
|
+
|
|
145
181
|
## Focused Test Runs
|
|
146
182
|
|
|
147
183
|
```bash
|