@unbrained/pm-cli 2026.9.4 → 2026.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/HARNESS_COMPATIBILITY.md +1 -1
- package/.agents/skills/README.md +2 -2
- package/.agents/skills/pm-developer/SKILL.md +4 -1
- package/.agents/skills/pm-developer/references/COMMAND_PLAYBOOK.md +10 -0
- package/.agents/skills/pm-developer/references/GRAPH_AND_RELATIONSHIPS.md +3 -1
- package/.agents/skills/pm-extensions/SKILL.md +2 -2
- package/.agents/skills/pm-extensions/references/LIFECYCLE.md +3 -3
- package/.agents/skills/pm-extensions/references/TROUBLESHOOTING.md +3 -3
- package/.agents/skills/pm-user/SKILL.md +2 -0
- package/.agents/skills/pm-user/references/BACKLOG_SHAPING.md +5 -0
- package/.agents/skills/pm-user/references/WORKFLOWS.md +20 -5
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +58 -3
- package/dist/cli/commander-usage.d.ts +2 -0
- package/dist/cli/commander-usage.js +15 -12
- package/dist/cli/help-json-payload.js +12 -7
- package/dist/cli/main.js +22 -11
- package/dist/cli/register-list-query.js +4 -2
- package/dist/cli/register-mutation.js +4 -2
- package/dist/cli/register-operations.js +17 -8
- package/dist/cli-bundle/bundle-manifest.json +153 -161
- package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +9 -0
- package/dist/cli-bundle/chunks/{chunk-V72UV7QS.js → chunk-BAX2BYUW.js} +73 -73
- package/dist/cli-bundle/chunks/{chunk-HI6BHPCU.js → chunk-CKOL5U7J.js} +51 -51
- package/dist/cli-bundle/chunks/{chunk-A24WAVE6.js → chunk-JT6XF6GE.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-I2AS367G.js → chunk-JX4DH4WC.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-YW4QK2G7.js → chunk-NPN5I67J.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-OF76PSDQ.js +5 -0
- package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +232 -0
- package/dist/cli-bundle/chunks/{chunk-RI4XNOYS.js → chunk-RWWBOWJ5.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +40 -0
- package/dist/cli-bundle/chunks/{chunk-X6CMGV6L.js → chunk-WI2AYPA7.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-Z5YYSJJG.js → register-list-query-5JL52D33.js} +3 -3
- package/dist/cli-bundle/chunks/{register-mutation-ELSP6I4I.js → register-mutation-ZPG7KMVP.js} +4 -4
- package/dist/cli-bundle/chunks/register-operations-I5W3KMGB.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-E2AE72QG.js → register-setup-H4SVAVX7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-3646BZ4W.js → chunk-3BZEOXLT.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +6 -0
- package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-NBMJD6MK.js → chunk-EZR3GHFE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +56 -0
- package/dist/cli-bundle/focused-chunks/{chunk-53IHPICX.js → chunk-LIJIA4TY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-25UJBH4M.js → chunk-M6PBRFSH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-AJR3OP2E.js → chunk-NTPY5GSO.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +177 -0
- package/dist/cli-bundle/focused-chunks/{chunk-MHKUIPJW.js → chunk-PEFABF7E.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-6PWPQKDN.js → chunk-QTVCLMSZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-7N2TV7WZ.js → chunk-QTZRBUYA.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +12 -0
- package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +10 -0
- package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +2 -0
- package/dist/cli-bundle/main.js +22 -15
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +37 -37
- 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 +42 -39
- 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/extensions/extension-types.d.ts +2 -0
- package/dist/core/extensions/extension-types.js +2 -2
- package/dist/core/extensions/module-graph-snapshot.js +5 -4
- package/dist/core/fs/host-environment-errors.d.ts +27 -0
- package/dist/core/fs/host-environment-errors.js +142 -0
- package/dist/core/history/drift-scan.d.ts +5 -0
- package/dist/core/history/drift-scan.js +16 -6
- package/dist/core/history/event-classification.d.ts +1 -1
- package/dist/core/history/event-classification.js +3 -2
- package/dist/core/history/history.js +37 -5
- package/dist/core/history/identity.d.ts +15 -0
- package/dist/core/history/identity.js +31 -0
- package/dist/core/history/projection.js +50 -17
- package/dist/core/history/read.js +6 -3
- package/dist/core/history/replay.d.ts +1 -2
- package/dist/core/history/replay.js +4 -4
- package/dist/core/history/version-address.d.ts +13 -0
- package/dist/core/history/version-address.js +33 -0
- package/dist/core/item/actionability.d.ts +21 -1
- package/dist/core/item/actionability.js +45 -10
- package/dist/core/item/id.js +8 -4
- package/dist/core/item/item-type-definition.d.ts +2 -0
- package/dist/core/item/item-type-definition.js +9 -2
- package/dist/core/item/type-registry.d.ts +5 -1
- package/dist/core/item/type-registry.js +9 -4
- package/dist/core/output/mutation-projection.js +12 -5
- package/dist/core/shared/errors.d.ts +8 -0
- package/dist/core/shared/errors.js +2 -2
- package/dist/core/store/item-metadata-query-index.js +13 -10
- package/dist/core/store/item-metadata-read-work.d.ts +19 -0
- package/dist/core/store/item-metadata-read-work.js +44 -0
- package/dist/core/store/item-store.d.ts +14 -0
- package/dist/core/store/item-store.js +52 -10
- package/dist/core/store/settings-validator.js +3 -2
- package/dist/core/validate/fix-planning.d.ts +1 -1
- package/dist/core/validate/fix-planning.js +2 -2
- package/dist/core/validate/missing-link-owners.d.ts +1 -1
- package/dist/core/validate/missing-link-owners.js +5 -3
- package/dist/core/validate/stale-file-classification.d.ts +6 -4
- package/dist/core/validate/stale-file-classification.js +20 -5
- package/dist/mcp/server.js +12 -5
- package/dist/sdk/actionability.d.ts +1 -1
- package/dist/sdk/actionability.js +3 -3
- package/dist/sdk/cli-contracts/command-aliases.js +11 -2
- package/dist/sdk/cli-contracts/completeness.js +4 -3
- package/dist/sdk/cli-contracts/flag-contracts.d.ts +2 -0
- package/dist/sdk/cli-contracts/flag-contracts.js +31 -5
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +21 -13
- package/dist/sdk/cli-contracts/grammar-contracts.js +11 -2
- package/dist/sdk/cli-contracts/runtime-contracts.js +5 -2
- package/dist/sdk/cli-contracts/schemas/assurance-definition.d.ts +254 -5
- package/dist/sdk/cli-contracts/schemas/assurance-definition.js +52 -2
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +30 -6
- package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
- package/dist/sdk/cli-contracts/tool-schema.js +32 -7
- package/dist/sdk/context/usage-ledger.d.ts +41 -0
- package/dist/sdk/context/usage-ledger.js +186 -0
- package/dist/sdk/context-intent-contracts.js +3 -3
- package/dist/sdk/context-usage.d.ts +18 -9
- package/dist/sdk/context-usage.js +44 -121
- package/dist/sdk/define.d.ts +1 -1
- package/dist/sdk/define.js +2 -2
- package/dist/sdk/dependency-provenance.d.ts +7 -0
- package/dist/sdk/dependency-provenance.js +14 -4
- package/dist/sdk/duplicates.d.ts +2 -0
- package/dist/sdk/duplicates.js +3 -2
- package/dist/sdk/environment/host-environment-errors.d.ts +5 -27
- package/dist/sdk/environment/host-environment-errors.js +4 -137
- package/dist/sdk/extension/install-runtime.js +9 -6
- package/dist/sdk/extension/install-snapshot.js +7 -6
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +104 -56
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +50 -2
- package/dist/sdk/governance/assurance-action.d.ts +3 -2
- package/dist/sdk/governance/assurance-action.js +23 -11
- package/dist/sdk/governance/assurance-runtime.d.ts +2 -0
- package/dist/sdk/governance/assurance-runtime.js +6 -3
- package/dist/sdk/governance/defect-recurrence-coverage.d.ts +93 -0
- package/dist/sdk/governance/defect-recurrence-coverage.js +189 -0
- package/dist/sdk/governance/defect-recurrence-graph.d.ts +33 -0
- package/dist/sdk/governance/defect-recurrence-graph.js +121 -0
- package/dist/sdk/governance/defect-recurrence.d.ts +8 -0
- package/dist/sdk/governance/defect-recurrence.js +118 -17
- package/dist/sdk/governance/health.d.ts +17 -2
- package/dist/sdk/governance/health.js +31 -10
- package/dist/sdk/governance/linked-file-report.d.ts +41 -0
- package/dist/sdk/governance/linked-file-report.js +72 -0
- package/dist/sdk/governance/validate-history-drift.js +16 -5
- package/dist/sdk/governance/validate.d.ts +5 -2
- package/dist/sdk/governance/validate.js +21 -37
- package/dist/sdk/governance.d.ts +2 -0
- package/dist/sdk/governance.js +4 -2
- package/dist/sdk/guide-topics.js +116 -2
- package/dist/sdk/history/salvage.d.ts +35 -0
- package/dist/sdk/history/salvage.js +248 -0
- package/dist/sdk/history-compact.js +32 -37
- package/dist/sdk/history-read.d.ts +3 -2
- package/dist/sdk/history-read.js +6 -3
- package/dist/sdk/history-repair.d.ts +12 -0
- package/dist/sdk/history-repair.js +60 -13
- package/dist/sdk/lifecycle/claim.d.ts +4 -0
- package/dist/sdk/lifecycle/claim.js +9 -3
- package/dist/sdk/lifecycle/close.d.ts +8 -0
- package/dist/sdk/lifecycle/close.js +16 -8
- package/dist/sdk/lifecycle/create.js +9 -12
- package/dist/sdk/lifecycle/restore.d.ts +1 -0
- package/dist/sdk/lifecycle/restore.js +19 -7
- package/dist/sdk/lifecycle/update.js +12 -12
- package/dist/sdk/lifecycle-policy.d.ts +3 -0
- package/dist/sdk/lifecycle-policy.js +2 -2
- package/dist/sdk/merge/abandoned-receipts.d.ts +15 -0
- package/dist/sdk/merge/abandoned-receipts.js +47 -0
- package/dist/sdk/merge/driver.js +4 -2
- package/dist/sdk/merge/index.d.ts +1 -0
- package/dist/sdk/merge/index.js +2 -2
- package/dist/sdk/merge/receipt-operation.d.ts +15 -0
- package/dist/sdk/merge/receipt-operation.js +97 -0
- package/dist/sdk/merge/receipt-schema.d.ts +20 -0
- package/dist/sdk/merge/receipt-schema.js +398 -0
- package/dist/sdk/merge/receipts.d.ts +16 -7
- package/dist/sdk/merge/receipts.js +54 -340
- package/dist/sdk/merge/reconcile.d.ts +2 -0
- package/dist/sdk/merge/reconcile.js +11 -4
- package/dist/sdk/output-projection.d.ts +3 -1
- package/dist/sdk/output-projection.js +11 -3
- package/dist/sdk/public-surface.json +1 -0
- package/dist/sdk/query/context.d.ts +9 -0
- package/dist/sdk/query/context.js +33 -21
- package/dist/sdk/query/duplicate-candidates.d.ts +7 -0
- package/dist/sdk/query/duplicate-candidates.js +71 -0
- package/dist/sdk/query/get.d.ts +2 -2
- package/dist/sdk/query/get.js +9 -8
- package/dist/sdk/query/history.d.ts +11 -0
- package/dist/sdk/query/history.js +34 -10
- package/dist/sdk/query/next.d.ts +15 -1
- package/dist/sdk/query/next.js +43 -20
- package/dist/sdk/query.d.ts +3 -0
- package/dist/sdk/query.js +5 -2
- package/dist/sdk/read-output-session.js +5 -3
- package/dist/sdk/runtime-primitives.d.ts +1 -1
- package/dist/sdk/runtime-primitives.js +3 -3
- package/dist/sdk/runtime.js +4 -2
- package/dist/sdk/similarity.d.ts +24 -3
- package/dist/sdk/similarity.js +50 -55
- package/dist/types.d.ts +2 -0
- package/dist/types.js +2 -2
- package/docs/AGENT_GUIDE.md +58 -1
- package/docs/BOUNDED_CONTEXT_READS.md +185 -0
- package/docs/CLOSE_EVIDENCE_RECOVERY.md +35 -0
- package/docs/COMMANDS.md +30 -8
- package/docs/CONTEXT_RELEVANCE.md +16 -4
- package/docs/DEFECT_RECURRENCE.md +80 -6
- package/docs/EXTENSIONS.md +18 -19
- package/docs/HISTORY_RECOVERY.md +146 -0
- package/docs/MERGE_SAFETY.md +50 -0
- package/docs/OUTPUT_PROJECTION_CONTRACTS.md +66 -0
- package/docs/PR_REVIEW_LOOP.md +18 -3
- package/docs/README.md +19 -1
- package/docs/RELEASING.md +31 -13
- package/docs/SDK_CONTEXT_TRUTH_CONTRACTS.md +24 -1
- package/docs/SDK_EXECUTION_CONTRACTS.md +105 -0
- package/docs/SDK_LIFECYCLE.md +27 -0
- package/docs/SDK_RUNTIME_BOUNDARIES.md +11 -0
- package/docs/TESTING.md +5 -0
- package/docs/agent-task-token-baseline.json +17 -17
- package/docs/agent-task-transcripts.json +2 -1
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +11 -11
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +11 -3
- package/marketplace.json +2 -2
- package/package.json +8 -6
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/README.md +10 -1
- package/packages/pm-governance-audit/extensions/governance-audit/dedupe-audit.ts +50 -65
- package/packages/pm-governance-audit/extensions/governance-audit/index.ts +2 -2
- package/packages/pm-governance-audit/extensions/governance-audit/sdk.ts +1 -0
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/skills/pm-audit/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-developer/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-planner/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-release/SKILL.md +2 -2
- package/plugins/pm-claude/skills/pm-workflow/SKILL.md +3 -3
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/skills/pm-auditor/SKILL.md +2 -2
- package/plugins/pm-codex/skills/pm-native/SKILL.md +3 -4
- package/plugins/pm-codex/skills/pm-release/SKILL.md +2 -2
- package/scripts/finalize-build.mjs +13 -1
- package/dist/cli-bundle/chunks/chunk-6XSKPNCF.js +0 -230
- package/dist/cli-bundle/chunks/chunk-OGBVU67G.js +0 -5
- package/dist/cli-bundle/chunks/chunk-TXW6VL33.js +0 -38
- package/dist/cli-bundle/chunks/chunk-VGLHECAP.js +0 -9
- package/dist/cli-bundle/chunks/register-operations-OH7R3ZT4.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-4YRAVI43.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-5P4HVOWG.js +0 -176
- package/dist/cli-bundle/focused-chunks/chunk-636VVQ7F.js +0 -54
- package/dist/cli-bundle/focused-chunks/chunk-B3A6MJYE.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-BFDDJ2T2.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-BHUIRZWQ.js +0 -9
- package/dist/cli-bundle/focused-chunks/chunk-JADHHPY6.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-NSKBLHVI.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-P2GOAOQN.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-YARWJM2B.js +0 -2
- package/sdk/public-surface.json +0 -33034
|
@@ -839,6 +839,8 @@ export interface SchemaItemTypeDefinition {
|
|
|
839
839
|
description?: string;
|
|
840
840
|
/** Initial status assigned when the contributed item type is created. */
|
|
841
841
|
default_status?: string;
|
|
842
|
+
/** Dispatch role inherited by next, context, and automatic claims. Gates track outcomes; human roles require decisions. */
|
|
843
|
+
execution_role?: "agent" | "human" | "gate";
|
|
842
844
|
/** Value that configures or reports required create fields for this contract. */
|
|
843
845
|
required_create_fields?: string[];
|
|
844
846
|
/** Value that configures or reports required create repeatables for this contract. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Public contract for known extension capabilities, shared by SDK and presentation-layer consumers. */
|
|
2
2
|
|
|
3
|
-
!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]="
|
|
3
|
+
!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]="a2f05fd9-1331-5e75-b8c1-42ad6deb425e")}catch(e){}}();
|
|
4
4
|
export const KNOWN_EXTENSION_CAPABILITIES = [
|
|
5
5
|
"commands",
|
|
6
6
|
"renderers",
|
|
@@ -100,4 +100,4 @@ export function createDefaultExtensionGovernancePolicy() {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
//# sourceMappingURL=extension-types.js.map
|
|
103
|
-
//# debugId=
|
|
103
|
+
//# debugId=a2f05fd9-1331-5e75-b8c1-42ad6deb425e
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* verification.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
!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]="
|
|
8
|
+
!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]="361b49b6-942e-55d9-9979-d72bc94721c6")}catch(e){}}();
|
|
9
|
+
import { withHostEnvironmentBoundary } from "../fs/host-environment-errors.js";
|
|
9
10
|
import fs from "node:fs/promises";
|
|
10
11
|
import path from "node:path";
|
|
11
12
|
/**
|
|
@@ -23,11 +24,11 @@ export async function snapshotExtensionModuleGraph(snapshotRoot, extension) {
|
|
|
23
24
|
throw new Error(`Cannot snapshot extension entry outside its package directory: ${extension.entry_path}`);
|
|
24
25
|
}
|
|
25
26
|
const snapshotDirectory = path.join(snapshotRoot, `${extension.layer}-${extension.directory.replace(/[^a-zA-Z0-9._-]/gu, "_")}`);
|
|
26
|
-
await fs.cp(sourceDirectory, snapshotDirectory, {
|
|
27
|
+
await withHostEnvironmentBoundary("extension_module_graph_snapshot", () => fs.cp(sourceDirectory, snapshotDirectory, {
|
|
27
28
|
recursive: true,
|
|
28
29
|
force: true,
|
|
29
|
-
});
|
|
30
|
+
}));
|
|
30
31
|
return path.join(snapshotDirectory, relativeEntryPath);
|
|
31
32
|
}
|
|
32
33
|
//# sourceMappingURL=module-graph-snapshot.js.map
|
|
33
|
-
//# debugId=
|
|
34
|
+
//# debugId=361b49b6-942e-55d9-9979-d72bc94721c6
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PmCliError } from "../shared/errors.js";
|
|
2
|
+
/** Optional stable codes used by an existing SDK surface for each fault class. */
|
|
3
|
+
export interface HostEnvironmentFaultCodeOverrides {
|
|
4
|
+
/** Code used for quota and storage exhaustion. */
|
|
5
|
+
capacity?: string;
|
|
6
|
+
/** Code used for access policy and read-only filesystems. */
|
|
7
|
+
permission?: string;
|
|
8
|
+
/** Code used for file-descriptor and memory exhaustion. */
|
|
9
|
+
resource?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Context and compatibility controls for a host-environment boundary. */
|
|
12
|
+
export interface HostEnvironmentBoundaryOptions {
|
|
13
|
+
/** Existing surface-specific codes that must remain compatible. */
|
|
14
|
+
codes?: HostEnvironmentFaultCodeOverrides;
|
|
15
|
+
/** Human explanation of why the operation needs host resources. */
|
|
16
|
+
why?: string;
|
|
17
|
+
/** Additional ordered recovery guidance. */
|
|
18
|
+
nextSteps?: readonly string[];
|
|
19
|
+
/** Copyable retry command when one is safe. */
|
|
20
|
+
suggestedRetry?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Return a declared host errno only when it represents recoverable environment state. */
|
|
23
|
+
export declare function classifyHostEnvironmentFault(error: unknown): string | null;
|
|
24
|
+
/** Translate one known host fault into a stable path-redacted SDK refusal. */
|
|
25
|
+
export declare function translateHostEnvironmentFault(error: unknown, operation: string, options?: HostEnvironmentBoundaryOptions): PmCliError | null;
|
|
26
|
+
/** Run one SDK filesystem stage and translate only declared host faults. */
|
|
27
|
+
export declare function withHostEnvironmentBoundary<T>(operation: string, run: () => Promise<T>, options?: HostEnvironmentBoundaryOptions): Promise<T>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/fs/host-environment-errors
|
|
3
|
+
*
|
|
4
|
+
* Classifies host filesystem/resource failures at SDK boundaries without
|
|
5
|
+
* leaking local paths or swallowing genuine implementation defects.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
!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]="1262c682-2b8a-506f-a3e1-155ad682d763")}catch(e){}}();
|
|
9
|
+
import { constants as osConstants } from "node:os";
|
|
10
|
+
import { EXIT_CODE } from "../shared/constants.js";
|
|
11
|
+
import { PmCliError } from "../shared/errors.js";
|
|
12
|
+
const HOST_ENVIRONMENT_FAULTS = new Map([
|
|
13
|
+
[
|
|
14
|
+
"EACCES",
|
|
15
|
+
{
|
|
16
|
+
summary: "was denied filesystem permission",
|
|
17
|
+
recovery: "Grant the required filesystem access, then retry.",
|
|
18
|
+
category: "permission",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"EDQUOT",
|
|
23
|
+
{
|
|
24
|
+
summary: "exceeded the filesystem quota",
|
|
25
|
+
recovery: "Raise or reclaim the filesystem quota, then retry.",
|
|
26
|
+
category: "capacity",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
"EMFILE",
|
|
31
|
+
{
|
|
32
|
+
summary: "exhausted the process file-descriptor limit",
|
|
33
|
+
recovery: "Close unused file handles or raise the process limit, then retry.",
|
|
34
|
+
category: "resource",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
"ENFILE",
|
|
39
|
+
{
|
|
40
|
+
summary: "exhausted the host file-descriptor limit",
|
|
41
|
+
recovery: "Free host file descriptors or raise the host limit, then retry.",
|
|
42
|
+
category: "resource",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
"ENOMEM",
|
|
47
|
+
{
|
|
48
|
+
summary: "ran out of available memory",
|
|
49
|
+
recovery: "Free memory or reduce concurrent work, then retry.",
|
|
50
|
+
category: "resource",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"ENOSPC",
|
|
55
|
+
{
|
|
56
|
+
summary: "ran out of storage space",
|
|
57
|
+
recovery: "Free storage on the filesystem holding the project, then retry.",
|
|
58
|
+
category: "capacity",
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"EPERM",
|
|
63
|
+
{
|
|
64
|
+
summary: "was denied by the host permission policy",
|
|
65
|
+
recovery: "Grant the required filesystem permission, then retry.",
|
|
66
|
+
category: "permission",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"EROFS",
|
|
71
|
+
{
|
|
72
|
+
summary: "targeted a read-only filesystem",
|
|
73
|
+
recovery: "Use a writable project filesystem or remount it read-write, then retry.",
|
|
74
|
+
category: "permission",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
]);
|
|
78
|
+
const OPERATION_PATTERN = /^[a-z][a-z0-9_]{0,63}$/u;
|
|
79
|
+
const HOST_ENVIRONMENT_ERRNO_NAMES = new Map([
|
|
80
|
+
[osConstants.errno.EACCES, "EACCES"],
|
|
81
|
+
[osConstants.errno.EDQUOT, "EDQUOT"],
|
|
82
|
+
[osConstants.errno.EMFILE, "EMFILE"],
|
|
83
|
+
[osConstants.errno.ENFILE, "ENFILE"],
|
|
84
|
+
[osConstants.errno.ENOMEM, "ENOMEM"],
|
|
85
|
+
[osConstants.errno.ENOSPC, "ENOSPC"],
|
|
86
|
+
[osConstants.errno.EPERM, "EPERM"],
|
|
87
|
+
[osConstants.errno.EROFS, "EROFS"],
|
|
88
|
+
].filter(([errno]) => Number.isInteger(errno)));
|
|
89
|
+
const HOST_ENVIRONMENT_FAULT_CODES = {
|
|
90
|
+
capacity: { code: "host_environment_capacity_fault" },
|
|
91
|
+
permission: { code: "host_environment_permission_fault" },
|
|
92
|
+
resource: { code: "host_environment_resource_fault" },
|
|
93
|
+
};
|
|
94
|
+
/** Return a declared host errno only when it represents recoverable environment state. */
|
|
95
|
+
export function classifyHostEnvironmentFault(error) {
|
|
96
|
+
if (typeof error !== "object" || error === null) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const errorRecord = error;
|
|
100
|
+
if (typeof errorRecord.code === "string" &&
|
|
101
|
+
HOST_ENVIRONMENT_FAULTS.has(errorRecord.code)) {
|
|
102
|
+
return errorRecord.code;
|
|
103
|
+
}
|
|
104
|
+
if (typeof errorRecord.errno !== "number" ||
|
|
105
|
+
!Number.isInteger(errorRecord.errno)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return HOST_ENVIRONMENT_ERRNO_NAMES.get(Math.abs(errorRecord.errno)) ?? null;
|
|
109
|
+
}
|
|
110
|
+
/** Translate one known host fault into a stable path-redacted SDK refusal. */
|
|
111
|
+
export function translateHostEnvironmentFault(error, operation, options = {}) {
|
|
112
|
+
const errno = classifyHostEnvironmentFault(error);
|
|
113
|
+
if (errno === null)
|
|
114
|
+
return null;
|
|
115
|
+
if (!OPERATION_PATTERN.test(operation)) {
|
|
116
|
+
throw new PmCliError("Host environment operation labels must be bounded identifiers.", EXIT_CODE.USAGE, { code: "host_environment_operation_invalid", field: "operation" });
|
|
117
|
+
}
|
|
118
|
+
const fault = HOST_ENVIRONMENT_FAULTS.get(errno);
|
|
119
|
+
return new PmCliError(`Host filesystem operation ${operation} ${fault.summary}.`, EXIT_CODE.GENERIC_FAILURE, {
|
|
120
|
+
code: options.codes?.[fault.category] ??
|
|
121
|
+
HOST_ENVIRONMENT_FAULT_CODES[fault.category].code,
|
|
122
|
+
reason: errno,
|
|
123
|
+
required: fault.recovery,
|
|
124
|
+
why: options.why ??
|
|
125
|
+
"The operating system refused a required project filesystem operation.",
|
|
126
|
+
nextSteps: [fault.recovery, ...(options.nextSteps ?? [])],
|
|
127
|
+
...(options.suggestedRetry
|
|
128
|
+
? { recovery: { suggested_retry: options.suggestedRetry } }
|
|
129
|
+
: {}),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/** Run one SDK filesystem stage and translate only declared host faults. */
|
|
133
|
+
export async function withHostEnvironmentBoundary(operation, run, options = {}) {
|
|
134
|
+
try {
|
|
135
|
+
return await run();
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
throw translateHostEnvironmentFault(error, operation, options) ?? error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=host-environment-errors.js.map
|
|
142
|
+
//# debugId=1262c682-2b8a-506f-a3e1-155ad682d763
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { type HistoryIdentityDiscontinuity } from "./identity.js";
|
|
1
2
|
import type { ItemMetadata } from "../../types/index.js";
|
|
2
3
|
/** Documents the drift scan result payload exchanged by command, SDK, and package integrations. */
|
|
3
4
|
export interface DriftScanResult {
|
|
5
|
+
/** Repeated genesis ordinals and observed sequence, keyed by affected item. */
|
|
6
|
+
identityDiscontinuities?: Array<HistoryIdentityDiscontinuity & {
|
|
7
|
+
item_id: string;
|
|
8
|
+
}>;
|
|
4
9
|
/** Value that configures or reports missing streams for this contract. */
|
|
5
10
|
missingStreams: string[];
|
|
6
11
|
/** Value that configures or reports unreadable streams for this contract. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!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]="
|
|
2
|
+
!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]="80648bfe-2436-5b5a-8a16-b9c2875f95fd")}catch(e){}}();
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import path from "node:path";
|
|
@@ -7,12 +7,14 @@ import { getHistoryPath } from "../store/paths.js";
|
|
|
7
7
|
import { isFileMissingError, writeFileAtomic } from "../fs/fs-utils.js";
|
|
8
8
|
import { CURRENT_HISTORY_ITEM_HASH_VERSION, SUPPORTED_HISTORY_ITEM_HASH_VERSIONS, hashDocumentVerificationCandidates, } from "./history.js";
|
|
9
9
|
import { verifyHistoryChainWithVersion } from "./replay.js";
|
|
10
|
+
import { findHistoryIdentityDiscontinuities, } from "./identity.js";
|
|
10
11
|
import { getWorkspaceHistoryPath, inspectWorkspaceHistoryState, WORKSPACE_HISTORY_ID, } from "./workspace-history.js";
|
|
11
|
-
const DRIFT_CACHE_VERSION =
|
|
12
|
+
const DRIFT_CACHE_VERSION = 10;
|
|
12
13
|
const DRIFT_CACHE_FILENAME = "history-drift-cache.json";
|
|
13
14
|
function getDriftCachePath(pmRoot) {
|
|
14
15
|
return path.join(pmRoot, "runtime", DRIFT_CACHE_FILENAME);
|
|
15
16
|
}
|
|
17
|
+
/** Discard unreadable or stale cache envelopes, including prior verification semantics. */
|
|
16
18
|
async function loadDriftCache(pmRoot) {
|
|
17
19
|
try {
|
|
18
20
|
const raw = await fs.readFile(getDriftCachePath(pmRoot), "utf8");
|
|
@@ -123,6 +125,7 @@ async function verifyHistoryStream(historyPath) {
|
|
|
123
125
|
: verification.item_hash_version;
|
|
124
126
|
return {
|
|
125
127
|
latestAfterHash,
|
|
128
|
+
identityDiscontinuities: findHistoryIdentityDiscontinuities(entries),
|
|
126
129
|
chainOk: verification.ok,
|
|
127
130
|
versionSkew,
|
|
128
131
|
latestHashComparable: latestHashVersion !== undefined &&
|
|
@@ -153,15 +156,15 @@ async function loadFreshStreamVerification(itemId, historyPath, accumulator) {
|
|
|
153
156
|
return null;
|
|
154
157
|
}
|
|
155
158
|
}
|
|
159
|
+
/** Reverify invalid streams and content-sensitive cache hits; reuse only compatible healthy evidence. */
|
|
156
160
|
async function resolveStreamVerification(params) {
|
|
157
161
|
const cachedContentHash = typeof params.cached?.content_hash === "string" &&
|
|
158
162
|
params.cached.content_hash.length > 0
|
|
159
163
|
? params.cached.content_hash
|
|
160
164
|
: undefined;
|
|
161
165
|
const canUseCache = cacheMetadataMatches(params.cached, params.stat) &&
|
|
162
|
-
cachedContentHash !== undefined
|
|
163
|
-
|
|
164
|
-
if (!canUseCache || !params.cached) {
|
|
166
|
+
cachedContentHash !== undefined;
|
|
167
|
+
if (!canUseCache || !params.cached || !params.cached.chain_ok) {
|
|
165
168
|
return {
|
|
166
169
|
verification: await loadFreshStreamVerification(params.itemId, params.historyPath, params.accumulator),
|
|
167
170
|
cacheDirty: true,
|
|
@@ -183,6 +186,7 @@ async function resolveStreamVerification(params) {
|
|
|
183
186
|
}
|
|
184
187
|
return {
|
|
185
188
|
verification: {
|
|
189
|
+
identityDiscontinuities: [],
|
|
186
190
|
latestAfterHash: params.cached.latest_after_hash,
|
|
187
191
|
chainOk: params.cached.chain_ok,
|
|
188
192
|
versionSkew: params.cached.version_skew === true,
|
|
@@ -217,6 +221,12 @@ async function scanItemHistory(pmRoot, item, cache, accumulator) {
|
|
|
217
221
|
});
|
|
218
222
|
if (!resolved.verification)
|
|
219
223
|
return resolved.cacheDirty;
|
|
224
|
+
if (resolved.verification.identityDiscontinuities.length > 0) {
|
|
225
|
+
(accumulator.identityDiscontinuities ??= []).push(...resolved.verification.identityDiscontinuities.map((finding) => ({
|
|
226
|
+
...finding,
|
|
227
|
+
item_id: item.id,
|
|
228
|
+
})));
|
|
229
|
+
}
|
|
220
230
|
if (resolved.verification.versionSkew) {
|
|
221
231
|
accumulator.versionSkews.push(item.id);
|
|
222
232
|
}
|
|
@@ -330,4 +340,4 @@ export async function scanHistoryDrift(pmRoot, items, options = {}) {
|
|
|
330
340
|
return { ...accumulator, driftedItems };
|
|
331
341
|
}
|
|
332
342
|
//# sourceMappingURL=drift-scan.js.map
|
|
333
|
-
//# debugId=
|
|
343
|
+
//# debugId=80648bfe-2436-5b5a-8a16-b9c2875f95fd
|
|
@@ -10,7 +10,7 @@ export declare const HISTORY_EVENT_CLASSIFICATION_VERSION: 1;
|
|
|
10
10
|
/** Semantic effect of an immutable history event on project recency. */
|
|
11
11
|
export type HistoryEventClass = "substantive" | "maintenance";
|
|
12
12
|
/** Operations whose meaning is always governance or storage maintenance. */
|
|
13
|
-
export declare const MAINTENANCE_HISTORY_OPERATIONS: readonly ["docs_add", "files_add", "history:author-acknowledge", "history_author_acknowledge", "history_compact", "history_compact_baseline", "history_redact", "history_repair", "normalize", "release", "test_run_track", "tests_add", "tests_remove", "update_audit", "update_ownership_bypass"];
|
|
13
|
+
export declare const MAINTENANCE_HISTORY_OPERATIONS: readonly ["docs_add", "files_add", "history:author-acknowledge", "history_author_acknowledge", "history_compact", "history_compact_baseline", "history_redact", "history_repair", "history_salvage", "normalize", "release", "test_run_track", "tests_add", "tests_remove", "update_audit", "update_ownership_bypass"];
|
|
14
14
|
/** Operations whose meaning always advances lifecycle or working context. */
|
|
15
15
|
export declare const SUBSTANTIVE_HISTORY_OPERATIONS: readonly ["append", "cancel", "claim", "close", "comment_add", "create", "learning_add", "note_add", "reopen"];
|
|
16
16
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Current version of the public history-event classification policy. */
|
|
2
2
|
|
|
3
|
-
!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]="
|
|
3
|
+
!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]="9ad6614e-44bc-549c-915c-6177c3a43c1f")}catch(e){}}();
|
|
4
4
|
export const HISTORY_EVENT_CLASSIFICATION_VERSION = 1;
|
|
5
5
|
/** Operations whose meaning is always governance or storage maintenance. */
|
|
6
6
|
export const MAINTENANCE_HISTORY_OPERATIONS = [
|
|
@@ -12,6 +12,7 @@ export const MAINTENANCE_HISTORY_OPERATIONS = [
|
|
|
12
12
|
"history_compact_baseline",
|
|
13
13
|
"history_redact",
|
|
14
14
|
"history_repair",
|
|
15
|
+
"history_salvage",
|
|
15
16
|
"normalize",
|
|
16
17
|
"release",
|
|
17
18
|
"test_run_track",
|
|
@@ -106,4 +107,4 @@ export function classifyHistoryEvent(entry) {
|
|
|
106
107
|
: "substantive";
|
|
107
108
|
}
|
|
108
109
|
//# sourceMappingURL=event-classification.js.map
|
|
109
|
-
//# debugId=
|
|
110
|
+
//# debugId=9ad6614e-44bc-549c-915c-6177c3a43c1f
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
* Implements append-only history and replay behavior for History.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
!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]="
|
|
7
|
+
!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]="a5449fec-c5b4-5cf4-9a7f-b7c5dc7161c2")}catch(e){}}();
|
|
8
8
|
import jsonPatch from "fast-json-patch";
|
|
9
|
+
import fs from "node:fs/promises";
|
|
10
|
+
import path from "node:path";
|
|
9
11
|
import { EXIT_CODE, ITEM_METADATA_KEY_ORDER } from "../shared/constants.js";
|
|
10
12
|
import { runActiveServiceOverride } from "../extensions/index.js";
|
|
11
13
|
import { appendLineAtomic } from "../fs/fs-utils.js";
|
|
@@ -539,7 +541,8 @@ async function applyHistoryAppendOverride(historyPath, entry, result) {
|
|
|
539
541
|
if (result === false)
|
|
540
542
|
return true;
|
|
541
543
|
if (typeof result === "string") {
|
|
542
|
-
|
|
544
|
+
const line = serializeHistoryLine(result, entry);
|
|
545
|
+
await appendHistoryLineWithIdentityReservation(historyPath, line);
|
|
543
546
|
await invalidateHistoryDriftCacheForPath(historyPath);
|
|
544
547
|
await removeHistoryEventIndexForHistoryPath(historyPath);
|
|
545
548
|
return true;
|
|
@@ -550,11 +553,40 @@ async function applyHistoryAppendOverride(historyPath, entry, result) {
|
|
|
550
553
|
if (record.skip === true)
|
|
551
554
|
return true;
|
|
552
555
|
const nextHistoryPath = typeof record.history_path === "string" ? record.history_path : historyPath;
|
|
553
|
-
|
|
556
|
+
const line = serializeHistoryLine(typeof record.line === "string" ? record.line : (record.entry ?? entry), entry);
|
|
557
|
+
await appendHistoryLineWithIdentityReservation(nextHistoryPath, line);
|
|
554
558
|
await invalidateHistoryDriftCacheForPath(nextHistoryPath);
|
|
555
559
|
await removeHistoryEventIndexForHistoryPath(nextHistoryPath);
|
|
556
560
|
return true;
|
|
557
561
|
}
|
|
562
|
+
/** Exclusively create genesis streams at their effective destination; never remove a failed partial reservation. */
|
|
563
|
+
async function appendHistoryLineWithIdentityReservation(historyPath, line) {
|
|
564
|
+
let record;
|
|
565
|
+
try {
|
|
566
|
+
record = JSON.parse(line);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
// Legacy service overrides may provide non-JSON lines; verification diagnoses them.
|
|
570
|
+
record = null;
|
|
571
|
+
}
|
|
572
|
+
if (!isRecord(record) || record.op !== "create") {
|
|
573
|
+
await appendLineAtomic(historyPath, line);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
await fs.mkdir(path.dirname(historyPath), { recursive: true });
|
|
577
|
+
try {
|
|
578
|
+
await fs.writeFile(historyPath, `${line}\n`, { encoding: "utf8", flag: "wx" });
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
if (error.code !== "EEXIST")
|
|
582
|
+
throw error;
|
|
583
|
+
throw new PmCliError("This item identity is reserved by an existing history stream.", EXIT_CODE.CONFLICT, {
|
|
584
|
+
code: "item_identity_reserved",
|
|
585
|
+
required: "Choose a new item ID or restore the original item from its history.",
|
|
586
|
+
nextSteps: ["Run pm history <id> --full, then pm restore <id> <version> to recover the original item."],
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
558
590
|
/** Implements append history entry for the public runtime surface of this module. */
|
|
559
591
|
export async function appendHistoryEntry(historyPath, entry) {
|
|
560
592
|
const override = await runActiveServiceOverride("history_append", {
|
|
@@ -573,7 +605,7 @@ export async function appendHistoryEntry(historyPath, entry) {
|
|
|
573
605
|
event_class: classifyHistoryEvent(timestampedEntry),
|
|
574
606
|
});
|
|
575
607
|
await appendHistoryEntryWithEventIndex(historyPath, normalizedEntry, async () => {
|
|
576
|
-
await
|
|
608
|
+
await appendHistoryLineWithIdentityReservation(historyPath, serializeHistoryLine(normalizedEntry, normalizedEntry));
|
|
577
609
|
});
|
|
578
610
|
await invalidateHistoryDriftCacheForPath(historyPath);
|
|
579
611
|
}
|
|
@@ -583,4 +615,4 @@ export const _testOnly = {
|
|
|
583
615
|
isDefinedPointerPath,
|
|
584
616
|
};
|
|
585
617
|
//# sourceMappingURL=history.js.map
|
|
586
|
-
//# debugId=
|
|
618
|
+
//# debugId=a5449fec-c5b4-5cf4-9a7f-b7c5dc7161c2
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** @module core/history/identity
|
|
2
|
+
* Identifies multiple genesis records without inferring an unrecorded writer.
|
|
3
|
+
*/
|
|
4
|
+
import type { HistoryEntry } from "../../types/index.js";
|
|
5
|
+
/** Evidence of a second subject occupying one append-only identity stream. */
|
|
6
|
+
export interface HistoryIdentityDiscontinuity {
|
|
7
|
+
/** One-based physical ordinal of the original create or retained baseline. */
|
|
8
|
+
prior_genesis_index: number;
|
|
9
|
+
/** One-based physical ordinal of the later create record. */
|
|
10
|
+
repeated_create_index: number;
|
|
11
|
+
/** Observed record sequence, not an assertion about branch or writer origin. */
|
|
12
|
+
sequence: "delete_then_create" | "multiple_creates" | "checkpoint_then_create";
|
|
13
|
+
}
|
|
14
|
+
/** Find every repeated genesis, including histories that start at a checkpoint. */
|
|
15
|
+
export declare function findHistoryIdentityDiscontinuities(entries: readonly HistoryEntry[]): HistoryIdentityDiscontinuity[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Find every repeated genesis, including histories that start at a checkpoint. */
|
|
2
|
+
|
|
3
|
+
!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]="ffaea7bd-daa4-5ff5-8cd5-82ad04e2256e")}catch(e){}}();
|
|
4
|
+
export function findHistoryIdentityDiscontinuities(entries) {
|
|
5
|
+
const findings = [];
|
|
6
|
+
const checkpoint = entries[0]?.op === "history_compact_baseline";
|
|
7
|
+
let genesis = checkpoint ? 1 : undefined;
|
|
8
|
+
let deleted = false;
|
|
9
|
+
for (const [index, entry] of entries.entries()) {
|
|
10
|
+
if (entry.op === "delete")
|
|
11
|
+
deleted = true;
|
|
12
|
+
if (entry.op !== "create")
|
|
13
|
+
continue;
|
|
14
|
+
if (genesis !== undefined) {
|
|
15
|
+
findings.push({
|
|
16
|
+
prior_genesis_index: genesis,
|
|
17
|
+
repeated_create_index: index + 1,
|
|
18
|
+
sequence: deleted
|
|
19
|
+
? "delete_then_create"
|
|
20
|
+
: checkpoint
|
|
21
|
+
? "checkpoint_then_create"
|
|
22
|
+
: "multiple_creates",
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
genesis ??= index + 1;
|
|
26
|
+
deleted = false;
|
|
27
|
+
}
|
|
28
|
+
return findings;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=identity.js.map
|
|
31
|
+
//# debugId=ffaea7bd-daa4-5ff5-8cd5-82ad04e2256e
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* replay kernel so read and write paths cannot disagree about a version.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
!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]="
|
|
9
|
+
!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]="2efbffc1-722c-5f3a-9ca5-e4e7a4f97729")}catch(e){}}();
|
|
10
10
|
import { EXIT_CODE } from "../shared/constants.js";
|
|
11
11
|
import { PmCliError } from "../shared/errors.js";
|
|
12
|
-
import {
|
|
12
|
+
import { historyVersionOffset } from "./version-address.js";
|
|
13
|
+
import { EMPTY_REPLAY_DOCUMENT, verifyHistoryChain, tryApplyReplayPatch, } from "./replay.js";
|
|
13
14
|
/** Find the final history entry recorded at or before a parsed timestamp. */
|
|
14
15
|
function findHistoryIndexAtTimestamp(history, parsedTarget) {
|
|
15
16
|
let historyIndex = -1;
|
|
@@ -25,7 +26,7 @@ function findHistoryIndexAtTimestamp(history, parsedTarget) {
|
|
|
25
26
|
return historyIndex;
|
|
26
27
|
}
|
|
27
28
|
/** Build structured target-range guidance once for every resolver error path. */
|
|
28
|
-
function buildHistoryTargetGuidance(history) {
|
|
29
|
+
function buildHistoryTargetGuidance(history, offset) {
|
|
29
30
|
const firstTimestamp = history[0]?.ts ?? null;
|
|
30
31
|
const lastTimestamp = history.at(-1)?.ts ?? null;
|
|
31
32
|
const guidance = {
|
|
@@ -44,9 +45,12 @@ function buildHistoryTargetGuidance(history) {
|
|
|
44
45
|
},
|
|
45
46
|
};
|
|
46
47
|
if (lastTimestamp !== null) {
|
|
47
|
-
guidance.examples = [
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
guidance.examples = [lastTimestamp];
|
|
49
|
+
if (offset !== null) {
|
|
50
|
+
guidance.examples.unshift(String(offset + 1), String(offset + history.length));
|
|
51
|
+
guidance.valid_range.first_version = offset + 1;
|
|
52
|
+
guidance.valid_range.last_version = offset + history.length;
|
|
53
|
+
}
|
|
50
54
|
}
|
|
51
55
|
return { guidance, lastTimestamp };
|
|
52
56
|
}
|
|
@@ -90,18 +94,25 @@ export function resolveHistoryTarget(target, history, options = {}) {
|
|
|
90
94
|
const trimmed = target.trim();
|
|
91
95
|
const errorSubject = options.errorSubject ?? "history";
|
|
92
96
|
const errorSubjectTitle = errorSubject === "restore" ? "Restore" : "History";
|
|
93
|
-
const { guidance, lastTimestamp } = buildHistoryTargetGuidance(history);
|
|
97
|
+
const { guidance, lastTimestamp } = buildHistoryTargetGuidance(history, historyVersionOffset(history, true));
|
|
94
98
|
if (!trimmed) {
|
|
95
99
|
throw new PmCliError(`Missing ${errorSubject} target. Use a timestamp or version number.`, EXIT_CODE.USAGE, guidance);
|
|
96
100
|
}
|
|
97
101
|
if (/^\d+$/.test(trimmed)) {
|
|
102
|
+
const offset = historyVersionOffset(history);
|
|
98
103
|
const version = Number(trimmed);
|
|
104
|
+
if (version >= 1 && version <= offset) {
|
|
105
|
+
throw new PmCliError(`History version ${version} was pruned by compaction.`, EXIT_CODE.USAGE, {
|
|
106
|
+
...guidance,
|
|
107
|
+
code: "history_version_pruned",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
99
110
|
if (!Number.isSafeInteger(version) ||
|
|
100
111
|
version < 1 ||
|
|
101
|
-
version > history.length) {
|
|
102
|
-
throw new PmCliError(`${errorSubjectTitle} version must be between 1 and ${history.length} for this item.`, EXIT_CODE.USAGE, guidance);
|
|
112
|
+
version > offset + history.length) {
|
|
113
|
+
throw new PmCliError(`${errorSubjectTitle} version must be between ${offset + 1} and ${offset + history.length} for this item.`, EXIT_CODE.USAGE, guidance);
|
|
103
114
|
}
|
|
104
|
-
return { kind: "version", raw: trimmed, historyIndex: version - 1 };
|
|
115
|
+
return { kind: "version", raw: trimmed, historyIndex: version - offset - 1 };
|
|
105
116
|
}
|
|
106
117
|
const parsedTarget = Date.parse(trimmed);
|
|
107
118
|
if (!Number.isFinite(parsedTarget)) {
|
|
@@ -137,28 +148,50 @@ export function applyHistoryPatch(current, patch, entryNumber, entryOp) {
|
|
|
137
148
|
}
|
|
138
149
|
/** Replay and hash-verify a history stream through a zero-based target entry. */
|
|
139
150
|
export function replayHistoryToTarget(history, targetIndex) {
|
|
151
|
+
const first = history[0];
|
|
152
|
+
if (first && first.op !== "create" && first.op !== "history_compact_baseline") {
|
|
153
|
+
const baseline = tryApplyReplayPatch(EMPTY_REPLAY_DOCUMENT, first.patch);
|
|
154
|
+
if (!baseline.ok || typeof baseline.document.metadata.id !== "string") {
|
|
155
|
+
throw new PmCliError("History starts without a reconstructable item baseline.", EXIT_CODE.GENERIC_FAILURE, {
|
|
156
|
+
code: "history_baseline_unavailable",
|
|
157
|
+
required: "Recover the original create entry or a complete checkpoint from version control.",
|
|
158
|
+
nextSteps: ["Run pm history <id> --full to inspect the missing baseline."],
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const selected = history.slice(0, targetIndex + 1);
|
|
163
|
+
const verification = verifyHistoryChain([...selected]);
|
|
140
164
|
let document = structuredClone(EMPTY_REPLAY_DOCUMENT);
|
|
141
165
|
for (let index = 0; index <= targetIndex; index += 1) {
|
|
142
166
|
const entry = history[index];
|
|
143
167
|
if (!entry) {
|
|
144
168
|
throw new PmCliError(`History target entry ${index + 1} does not exist.`, EXIT_CODE.USAGE);
|
|
145
169
|
}
|
|
146
|
-
if (replayHash(document) !== entry.before_hash) {
|
|
147
|
-
throw new PmCliError(`History hash mismatch before replay at entry ${index + 1}.`, EXIT_CODE.GENERIC_FAILURE);
|
|
148
|
-
}
|
|
149
170
|
document = applyHistoryPatch(document, entry.patch, index + 1, entry.op);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
171
|
+
}
|
|
172
|
+
if (!verification.ok) {
|
|
173
|
+
throw new PmCliError(`History cannot be replayed: ${verification.errors.join(", ")}.`, EXIT_CODE.GENERIC_FAILURE, {
|
|
174
|
+
code: "history_replay_invalid",
|
|
175
|
+
verification_errors: verification.errors,
|
|
176
|
+
required: "Inspect the history integrity findings before attempting recovery.",
|
|
177
|
+
nextSteps: ["Run pm history <id> --verify --full."],
|
|
178
|
+
});
|
|
153
179
|
}
|
|
154
180
|
return document;
|
|
155
181
|
}
|
|
156
182
|
/** Reject a version whose reconstructed state represents a deleted item. */
|
|
157
183
|
export function ensureMaterializedHistoryTarget(replayDocument, target) {
|
|
158
184
|
if (Object.keys(replayDocument.metadata).length > 0) {
|
|
185
|
+
if (typeof replayDocument.metadata.id !== "string" || !Array.isArray(replayDocument.metadata.tags)) {
|
|
186
|
+
throw new PmCliError("History has no complete item baseline for this target.", EXIT_CODE.GENERIC_FAILURE, {
|
|
187
|
+
code: "history_baseline_unavailable",
|
|
188
|
+
required: "Recover the original create entry or a complete checkpoint from version control.",
|
|
189
|
+
nextSteps: ["Run pm history <id> --full to inspect the first recorded state."],
|
|
190
|
+
});
|
|
191
|
+
}
|
|
159
192
|
return replayDocument;
|
|
160
193
|
}
|
|
161
194
|
throw new PmCliError(`History target ${target.raw} resolves to a deleted state; choose a version or timestamp where the item exists.`, EXIT_CODE.USAGE);
|
|
162
195
|
}
|
|
163
196
|
//# sourceMappingURL=projection.js.map
|
|
164
|
-
//# debugId=
|
|
197
|
+
//# debugId=2efbffc1-722c-5f3a-9ca5-e4e7a4f97729
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* maintenance APIs from depending on presentation-layer modules.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
!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]="
|
|
10
|
+
!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]="999c126a-112a-50f6-b53e-bd36dbc5b885")}catch(e){}}();
|
|
11
11
|
import { readFileIfExists } from "../fs/fs-utils.js";
|
|
12
12
|
import { runActiveOnReadHooks } from "../extensions/index.js";
|
|
13
13
|
import { EXIT_CODE } from "../shared/constants.js";
|
|
@@ -46,10 +46,13 @@ export async function readHistoryEntries(historyPath, itemId) {
|
|
|
46
46
|
entries.push(JSON.parse(line));
|
|
47
47
|
}
|
|
48
48
|
catch {
|
|
49
|
-
throw new PmCliError(`History for ${itemId} contains invalid JSON at line ${index + 1}. Repair or restore the history stream and retry.`, EXIT_CODE.GENERIC_FAILURE
|
|
49
|
+
throw new PmCliError(`History for ${itemId} contains invalid JSON at line ${index + 1}. Repair or restore the history stream and retry.`, EXIT_CODE.GENERIC_FAILURE, {
|
|
50
|
+
code: "history_json_invalid",
|
|
51
|
+
nextSteps: [`Run pm history-repair ${itemId} --salvage-tail --dry-run to check whether only an invalid suffix is damaged.`],
|
|
52
|
+
});
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
return entries;
|
|
53
56
|
}
|
|
54
57
|
//# sourceMappingURL=read.js.map
|
|
55
|
-
//# debugId=
|
|
58
|
+
//# debugId=999c126a-112a-50f6-b53e-bd36dbc5b885
|
|
@@ -47,7 +47,6 @@ export type ReplayApplyResult = {
|
|
|
47
47
|
};
|
|
48
48
|
/** Strictly apply a history patch (front_matter->metadata normalized) to a replay document. Returns a result envelope rather than throwing so each caller can format its own error contract. */
|
|
49
49
|
export declare function tryApplyReplayPatch(current: ReplayDocument, patch: HistoryPatchOp[]): ReplayApplyResult;
|
|
50
|
-
/** Deterministically verify a history chain: each entry's before_hash must equal the prior replayed after_hash, the patch must strictly apply, and the recorded after_hash must equal the replayed result. */
|
|
51
50
|
/** Verify a chain and report the explicit or auto-detected item hash epoch. */
|
|
52
51
|
export declare function verifyHistoryChainWithVersion(entries: HistoryEntry[]): {
|
|
53
52
|
ok: boolean;
|
|
@@ -119,6 +118,6 @@ export interface ReanchorHistoryOptions {
|
|
|
119
118
|
*/
|
|
120
119
|
export declare function resolveHistoryRepairItemHashVersion(entries: HistoryEntry[]): HistoryItemHashVersion;
|
|
121
120
|
/** Re-anchor a drifted history chain: replay every entry from empty, recompute the before/after hashes, and only rewrite a patch when the original op set no longer strictly applies (legacy drift). Clean entries keep their patch verbatim so the on-disk diff stays minimal. The returned chain verifies via verifyHistoryChain. */
|
|
122
|
-
export declare function reanchorHistoryEntries(entries: HistoryEntry[], itemHashVersion?:
|
|
121
|
+
export declare function reanchorHistoryEntries(entries: HistoryEntry[], itemHashVersion?: HistoryItemHashVersion, options?: ReanchorHistoryOptions): ReanchorResult;
|
|
123
122
|
/** Implements history entries to raw for the public runtime surface of this module. */
|
|
124
123
|
export declare function historyEntriesToRaw(entries: HistoryEntry[]): string;
|