@unbrained/pm-cli 2026.8.16 → 2026.8.18
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 +2 -2
- package/AGENTS.md +4 -3
- package/CHANGELOG.md +65 -6
- package/README.md +16 -16
- package/dist/cli/error-guidance.js +7 -4
- package/dist/cli/public.d.ts +2 -0
- package/dist/cli/public.js +21 -0
- package/dist/cli/register-list-query.js +132 -51
- package/dist/cli/register-mutation.js +4 -4
- package/dist/cli-bundle/bundle-manifest.json +148 -148
- package/dist/cli-bundle/chunks/chunk-5UZZAJKR.js +3 -0
- package/dist/cli-bundle/chunks/chunk-ALDJGKAK.js +2 -0
- package/dist/cli-bundle/chunks/chunk-BSK2IN3C.js +8 -0
- package/dist/cli-bundle/chunks/chunk-DY4DMUMC.js +197 -0
- package/dist/cli-bundle/chunks/{chunk-73UGEBRS.js → chunk-GNFAFIJI.js} +10 -10
- package/dist/cli-bundle/chunks/chunk-L5Q2CLPE.js +35 -0
- package/dist/cli-bundle/chunks/{chunk-RIS565OA.js → chunk-PVRUN5ZS.js} +5 -5
- package/dist/cli-bundle/chunks/chunk-RZQTVMRQ.js +2 -0
- package/dist/cli-bundle/chunks/chunk-SARFF5H5.js +3 -0
- package/dist/cli-bundle/chunks/chunk-UYCLQVL2.js +13 -0
- package/dist/cli-bundle/chunks/{chunk-URW4QRMI.js → chunk-YRGOLZA7.js} +59 -55
- package/dist/cli-bundle/chunks/register-list-query-I23LALEE.js +11 -0
- package/dist/cli-bundle/chunks/register-mutation-6YGU3GPH.js +20 -0
- package/dist/cli-bundle/chunks/{register-operations-KGDNRMCL.js → register-operations-4HTFC6J7.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-NVXBOD5I.js → register-setup-PPPEF3SN.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-2PN4TJXH.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-C7JUBRFP.js → chunk-3TKWMNKF.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-5AF3ZBNB.js +155 -0
- package/dist/cli-bundle/focused-chunks/chunk-C5IW2NDA.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-CODV5LUT.js → chunk-EUEGXZ2A.js} +3 -3
- package/dist/cli-bundle/focused-chunks/{chunk-4JPEBFFC.js → chunk-EX6MKP2X.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-SSNDYZTM.js → chunk-K43KKAFS.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-6ROKKT4X.js → chunk-M62NO7EI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-T46KLOIB.js → chunk-ODOVCP45.js} +47 -47
- package/dist/cli-bundle/focused-chunks/chunk-RNDNMARJ.js +16 -0
- package/dist/cli-bundle/focused-chunks/chunk-TN4AE665.js +26 -0
- package/dist/cli-bundle/focused-chunks/chunk-TVV2DONO.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-UI6AJ5TD.js +4 -0
- package/dist/cli-bundle/focused-chunks/chunk-V5XTGLK7.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-WGHQVU6P.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-XYNBXTDQ.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-Z56ECT7I.js +2 -0
- package/dist/cli-bundle/main.js +7 -7
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +31 -31
- 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 +32 -32
- 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 +4 -2
- package/dist/core/config/nested-settings.js +8 -2
- package/dist/core/extensions/exporter-output-contract.d.ts +12 -0
- package/dist/core/extensions/exporter-output-contract.js +67 -0
- package/dist/core/extensions/extension-types.d.ts +36 -1
- package/dist/core/extensions/extension-types.js +2 -2
- package/dist/core/extensions/loader.js +43 -48
- package/dist/core/extensions/manifest-schema.d.ts +20 -0
- package/dist/core/extensions/manifest-schema.js +52 -0
- package/dist/core/history/event-index.d.ts +5 -0
- package/dist/core/history/event-index.js +133 -27
- package/dist/core/history/replay.js +32 -14
- package/dist/core/schema/fields-file.d.ts +1 -1
- package/dist/core/schema/fields-file.js +2 -2
- package/dist/core/schema/runtime-field-values.js +115 -9
- package/dist/core/schema/runtime-schema.d.ts +3 -1
- package/dist/core/schema/runtime-schema.js +124 -3
- package/dist/core/sentry/helpers.js +3 -3
- package/dist/core/shared/constants.js +5 -2
- package/dist/core/shared/errors.d.ts +10 -0
- package/dist/core/shared/errors.js +2 -2
- package/dist/core/shared/time.d.ts +2 -0
- package/dist/core/shared/time.js +27 -2
- package/dist/core/store/settings-validator.d.ts +4 -0
- package/dist/core/store/settings-validator.js +3 -2
- package/dist/core/store/settings.js +8 -2
- package/dist/mcp/tool-definitions.js +12 -7
- package/dist/sdk/agent/refusal-reachability.d.ts +46 -6
- package/dist/sdk/agent/refusal-reachability.js +350 -8
- package/dist/sdk/authoring.d.ts +1 -1
- package/dist/sdk/authoring.js +2 -2
- package/dist/sdk/cli-bootstrap.d.ts +2 -2
- package/dist/sdk/cli-bootstrap.js +4 -4
- package/dist/sdk/cli-contracts/command-aliases.d.ts +34 -2
- package/dist/sdk/cli-contracts/command-aliases.js +64 -13
- package/dist/sdk/cli-contracts/enum-contracts.d.ts +4 -0
- package/dist/sdk/cli-contracts/enum-contracts.js +9 -2
- package/dist/sdk/cli-contracts/flag-contracts.js +11 -7
- package/dist/sdk/cli-contracts/grammar-contracts.d.ts +70 -0
- package/dist/sdk/cli-contracts/grammar-contracts.js +345 -0
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +19 -2
- package/dist/sdk/cli-contracts/runtime-contracts.js +61 -14
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +10 -4
- package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
- package/dist/sdk/cli-contracts/tool-schema.js +20 -11
- package/dist/sdk/cli-contracts.d.ts +2 -2
- package/dist/sdk/cli-contracts.js +4 -4
- package/dist/sdk/completion.js +13 -15
- package/dist/sdk/compose.d.ts +9 -5
- package/dist/sdk/compose.js +33 -3
- package/dist/sdk/context-intent-contracts.d.ts +8 -4
- package/dist/sdk/context-intent-contracts.js +76 -31
- package/dist/sdk/contracts.d.ts +1 -0
- package/dist/sdk/contracts.js +3 -2
- package/dist/sdk/core.d.ts +1 -1
- package/dist/sdk/core.js +3 -3
- package/dist/sdk/dependency-flag-validation.d.ts +9 -2
- package/dist/sdk/dependency-flag-validation.js +101 -35
- package/dist/sdk/environment/host-environment-errors.js +25 -7
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +78 -14
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +62 -14
- package/dist/sdk/governance/assurance-action.d.ts +3 -7
- package/dist/sdk/governance/assurance-action.js +97 -2
- package/dist/sdk/governance/boundary-fixtures.d.ts +86 -0
- package/dist/sdk/governance/boundary-fixtures.js +201 -0
- package/dist/sdk/governance/defect-recurrence-signals.d.ts +10 -0
- package/dist/sdk/governance/defect-recurrence-signals.js +25 -0
- package/dist/sdk/governance/defect-recurrence.d.ts +242 -0
- package/dist/sdk/governance/defect-recurrence.js +523 -0
- package/dist/sdk/governance.d.ts +2 -0
- package/dist/sdk/governance.js +4 -2
- package/dist/sdk/graph/governance.js +3 -2
- package/dist/sdk/guide-topics.js +193 -4
- package/dist/sdk/index.d.ts +7 -3
- package/dist/sdk/index.js +8 -5
- package/dist/sdk/init-agent-guidance.js +3 -3
- package/dist/sdk/lifecycle/create.js +4 -2
- package/dist/sdk/lifecycle/update-many.js +15 -2
- package/dist/sdk/lifecycle/update.js +5 -2
- package/dist/sdk/linked-artifacts.js +63 -14
- package/dist/sdk/mutation-events.d.ts +18 -2
- package/dist/sdk/mutation-events.js +59 -16
- package/dist/sdk/output.d.ts +44 -0
- package/dist/sdk/output.js +28 -2
- package/dist/sdk/query/complete-list.d.ts +94 -0
- package/dist/sdk/query/complete-list.js +173 -0
- package/dist/sdk/query/get.d.ts +2 -0
- package/dist/sdk/query/get.js +32 -7
- package/dist/sdk/query/list.js +5 -2
- package/dist/sdk/query.d.ts +1 -0
- package/dist/sdk/query.js +3 -2
- package/dist/sdk/read-output-contracts.d.ts +2 -2
- package/dist/sdk/read-output-contracts.js +36 -9
- package/dist/sdk/relationship-analytics.js +90 -14
- package/dist/sdk/relationship-kinds/contract.d.ts +4 -0
- package/dist/sdk/relationship-kinds/contract.js +3 -2
- package/dist/sdk/relationships.d.ts +1 -1
- package/dist/sdk/relationships.js +11 -2
- package/dist/sdk/runtime-extended-actions.js +10 -2
- package/dist/sdk/runtime.d.ts +8 -2
- package/dist/sdk/runtime.js +11 -8
- package/dist/sdk/schema.d.ts +1 -0
- package/dist/sdk/schema.js +2 -2
- package/dist/sdk/test/execution.d.ts +6 -2
- package/dist/sdk/test/execution.js +39 -13
- package/dist/types.d.ts +34 -2
- package/dist/types.js +4 -2
- package/docs/AGENT_GUIDE.md +25 -9
- package/docs/CLI_GRAMMAR.md +106 -0
- package/docs/COMMANDS.md +23 -23
- package/docs/CONFIGURATION.md +58 -1
- package/docs/CONTEXT_INTEGRITY_CONTRACTS.md +58 -0
- package/docs/DEFECT_RECURRENCE.md +134 -0
- package/docs/DEPENDENCY_KIND_CONTRACT.md +12 -2
- package/docs/ONBOARDING.md +4 -4
- package/docs/QUICKSTART.md +1 -1
- package/docs/README.md +4 -0
- package/docs/RELATIONSHIP_GRAPH.md +16 -2
- package/docs/RELEASING.md +30 -6
- package/docs/SDK.md +74 -7
- package/docs/SDK_ARTIFACT_OUTPUT.md +72 -0
- package/docs/SDK_CONTEXT_COORDINATION.md +46 -12
- package/docs/SDK_RUNTIME_BOUNDARIES.md +9 -1
- package/docs/TESTING.md +18 -1
- package/docs/TRUSTWORTHY_CONTEXT_EVIDENCE.md +1 -1
- package/docs/agent-task-token-baseline.json +5 -5
- package/marketplace.json +2 -2
- package/package.json +8 -5
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-claude/skills/pm-audit/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-developer/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-planner/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-release/SKILL.md +26 -0
- package/plugins/pm-claude/skills/pm-workflow/SKILL.md +26 -0
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/pm-codex/skills/pm-auditor/SKILL.md +26 -0
- package/plugins/pm-codex/skills/pm-native/SKILL.md +26 -0
- package/plugins/pm-codex/skills/pm-release/SKILL.md +26 -0
- package/sdk/public-surface.json +1243 -54
- package/dist/cli-bundle/chunks/chunk-47OQEOQB.js +0 -2
- package/dist/cli-bundle/chunks/chunk-HCIYWD6M.js +0 -35
- package/dist/cli-bundle/chunks/chunk-I2LUWWR5.js +0 -3
- package/dist/cli-bundle/chunks/chunk-IC5W2T34.js +0 -2
- package/dist/cli-bundle/chunks/chunk-OAC5NIV4.js +0 -3
- package/dist/cli-bundle/chunks/chunk-PANOOBIS.js +0 -13
- package/dist/cli-bundle/chunks/chunk-TK6N7HGM.js +0 -8
- package/dist/cli-bundle/chunks/chunk-TOFG2URD.js +0 -197
- package/dist/cli-bundle/chunks/register-list-query-EYCXHZAG.js +0 -10
- package/dist/cli-bundle/chunks/register-mutation-7YVIG54J.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-5NMFSX26.js +0 -155
- package/dist/cli-bundle/focused-chunks/chunk-725JSCMP.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-DQ6SMTBF.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-HAACPSUW.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-J4UFYHOD.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-JX5Z344Q.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-L4RDYBIQ.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-NJ5RLEFD.js +0 -26
- package/dist/cli-bundle/focused-chunks/chunk-QYZERYR5.js +0 -4
- package/dist/cli-bundle/focused-chunks/chunk-TZATVC4G.js +0 -16
- package/dist/cli-bundle/focused-chunks/chunk-WY3WRPVN.js +0 -2
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements the pm linked artifacts command surface and its agent-facing runtime behavior.
|
|
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]="72f57c4d-65c5-57f1-a16a-f6c970aca680")}catch(e){}}();
|
|
8
8
|
import fs from "node:fs/promises";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import fg from "fast-glob";
|
|
@@ -83,24 +83,73 @@ function expandBareCommaSeparatedAddEntries(raw) {
|
|
|
83
83
|
return splitCommaList(trimmed);
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
|
+
/** Whether a Markdown destination contains only balanced parentheses. */
|
|
87
|
+
function hasBalancedParentheses(value) {
|
|
88
|
+
let depth = 0;
|
|
89
|
+
for (const character of value) {
|
|
90
|
+
if (character === "(")
|
|
91
|
+
depth += 1;
|
|
92
|
+
if (character === ")")
|
|
93
|
+
depth -= 1;
|
|
94
|
+
if (depth < 0)
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return depth === 0;
|
|
98
|
+
}
|
|
99
|
+
/** Parse one lossless bare, Markdown, or CSV-label remote documentation reference. */
|
|
100
|
+
function parseDocumentationReference(trimmed) {
|
|
101
|
+
if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(trimmed) &&
|
|
102
|
+
!looksLikeStructuredPathEntry(trimmed)) {
|
|
103
|
+
return { path: trimmed, scope: "project", note: undefined };
|
|
104
|
+
}
|
|
105
|
+
const labelEnd = trimmed.indexOf("](");
|
|
106
|
+
if (trimmed.startsWith("[") && labelEnd > 0 && trimmed.endsWith(")")) {
|
|
107
|
+
const destination = trimmed.slice(labelEnd + 2, -1).trim();
|
|
108
|
+
if (destination.length > 0 && hasBalancedParentheses(destination)) {
|
|
109
|
+
return {
|
|
110
|
+
path: destination,
|
|
111
|
+
scope: "project",
|
|
112
|
+
note: trimmed.slice(1, labelEnd).trim() || undefined,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const delimiterIndex = trimmed.indexOf(",");
|
|
117
|
+
if (delimiterIndex < 0)
|
|
118
|
+
return undefined;
|
|
119
|
+
const label = trimmed.slice(0, delimiterIndex).trim();
|
|
120
|
+
const destination = trimmed.slice(delimiterIndex + 1).trim();
|
|
121
|
+
if (label.length === 0 ||
|
|
122
|
+
!/^[A-Za-z][A-Za-z0-9+.-]*:\/\//u.test(destination)) {
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
return { path: destination, scope: "project", note: label };
|
|
126
|
+
}
|
|
86
127
|
/** Implements parse add entries for the public runtime surface of this module. */
|
|
87
128
|
export function parseAddEntries(raw, bareNoun) {
|
|
88
129
|
if (!raw)
|
|
89
130
|
return [];
|
|
90
|
-
return
|
|
131
|
+
return raw.flatMap((entry) => {
|
|
91
132
|
const trimmed = entry.trim();
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (!kv.path) {
|
|
97
|
-
throw new PmCliError(`--add requires path=<value> or a bare ${bareNoun} path`, EXIT_CODE.USAGE);
|
|
133
|
+
if (bareNoun === "doc") {
|
|
134
|
+
const reference = parseDocumentationReference(trimmed);
|
|
135
|
+
if (reference !== undefined)
|
|
136
|
+
return [reference];
|
|
98
137
|
}
|
|
99
|
-
return {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
138
|
+
return expandBareCommaSeparatedAddEntries([entry]).map((expanded) => {
|
|
139
|
+
const expandedTrimmed = expanded.trim();
|
|
140
|
+
const kv = looksLikeStructuredPathEntry(expandedTrimmed)
|
|
141
|
+
? parseCsvKv(expanded, "--add")
|
|
142
|
+
: { path: expandedTrimmed };
|
|
143
|
+
assertNoUnknownCsvKeys(kv, "--add", LINKED_ARTIFACT_ADD_KEYS);
|
|
144
|
+
if (!kv.path) {
|
|
145
|
+
throw new PmCliError(`--add requires path=<value> or a bare ${bareNoun} path`, EXIT_CODE.USAGE);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
path: kv.path,
|
|
149
|
+
scope: ensureScope(kv.scope),
|
|
150
|
+
note: kv.note?.trim() || undefined,
|
|
151
|
+
};
|
|
152
|
+
});
|
|
104
153
|
});
|
|
105
154
|
}
|
|
106
155
|
/** Implements parse add glob entries for the public runtime surface of this module. */
|
|
@@ -490,4 +539,4 @@ export function renameArtifactsResultKey(result, key) {
|
|
|
490
539
|
return out;
|
|
491
540
|
}
|
|
492
541
|
//# sourceMappingURL=linked-artifacts.js.map
|
|
493
|
-
//# debugId=
|
|
542
|
+
//# debugId=72f57c4d-65c5-57f1-a16a-f6c970aca680
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { type IndexedHistoryEvent } from "../core/history/event-index.js";
|
|
2
2
|
import type { HistoryEntry } from "../types/index.js";
|
|
3
3
|
import { type HistoryProvenanceRow, type HistoryProvenanceSummary } from "./history-provenance.js";
|
|
4
|
+
/** Regression budgets for the canonical 200-event wire-cost fixture. */
|
|
5
|
+
export declare const MUTATION_EVENT_WIRE_BUDGET: Readonly<{
|
|
6
|
+
/** Maximum default batch bytes divided by legacy row-cursor bytes. */
|
|
7
|
+
batch_to_row_ratio: 0.6;
|
|
8
|
+
/** Fixture size used to keep the ratio representative of fleet catch-up. */
|
|
9
|
+
fixture_events: 200;
|
|
10
|
+
}>;
|
|
4
11
|
interface MutationEventCursorEnvelope {
|
|
5
12
|
version: number;
|
|
6
13
|
fingerprint: string;
|
|
@@ -36,11 +43,13 @@ export interface ListMutationEventsOptions {
|
|
|
36
43
|
agentInstance?: string | readonly string[];
|
|
37
44
|
/** Exact provenance dimension predicates (`dimension=value`). */
|
|
38
45
|
provenanceFilter?: string | readonly string[];
|
|
46
|
+
/** Cursor framing mode; batch is the token-efficient default. */
|
|
47
|
+
cursorMode?: "batch" | "row";
|
|
39
48
|
}
|
|
40
49
|
/** One ordered cross-item mutation event. */
|
|
41
50
|
export interface MutationEvent {
|
|
42
51
|
/** Cursor that resumes strictly after this event. */
|
|
43
|
-
cursor
|
|
52
|
+
cursor?: string;
|
|
44
53
|
/** Item id or `_workspace` stream subject. */
|
|
45
54
|
item_id: string;
|
|
46
55
|
/** One-based version within the subject's stream. */
|
|
@@ -68,10 +77,12 @@ export interface MutationEventPage {
|
|
|
68
77
|
count: number;
|
|
69
78
|
/** Whether another matching event is immediately available. */
|
|
70
79
|
has_more: boolean;
|
|
80
|
+
/** Cursor framing used by this page. */
|
|
81
|
+
cursor_mode: "batch" | "row";
|
|
71
82
|
/** Cursor that resumes after the final returned event. */
|
|
72
83
|
next_cursor?: string;
|
|
73
84
|
/** Persistent derived projection used for the read. */
|
|
74
|
-
source: "derived_index";
|
|
85
|
+
source: "derived_index" | "authoritative_history";
|
|
75
86
|
/** Constant-size provenance completeness metrics for the returned page. */
|
|
76
87
|
provenance_summary?: HistoryProvenanceSummary;
|
|
77
88
|
}
|
|
@@ -94,6 +105,11 @@ declare function resolveMutationEventStart(sinceValue: string | undefined, finge
|
|
|
94
105
|
};
|
|
95
106
|
/** Read one bounded page of mutation events from the persistent projection. */
|
|
96
107
|
export declare function listMutationEvents(options?: ListMutationEventsOptions): Promise<MutationEventPage>;
|
|
108
|
+
/**
|
|
109
|
+
* Subscribe to complete event batches. Every yield is a recoverable boundary;
|
|
110
|
+
* an empty batch is an idle heartbeat carrying the last known cursor.
|
|
111
|
+
*/
|
|
112
|
+
export declare function subscribeMutationEventBatches(options?: SubscribeMutationEventsOptions): AsyncGenerator<MutationEventPage, void, void>;
|
|
97
113
|
/**
|
|
98
114
|
* Subscribe to committed mutation facts as an async iterable. The iterator
|
|
99
115
|
* performs cursor catch-up reads and waits only when no new event is available.
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* history store without requiring a daemon or shell subprocess.
|
|
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]="e8cd5790-eacf-5ef7-bc0a-11ea3fe17d2d")}catch(e){}}();
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
10
|
import { setTimeout as delay } from "node:timers/promises";
|
|
11
|
-
import { queryHistoryEventIndex, rebuildHistoryEventIndex, } from "../core/history/event-index.js";
|
|
11
|
+
import { queryHistoryEventIndex, queryHistoryEventStreams, rebuildHistoryEventIndex, } from "../core/history/event-index.js";
|
|
12
12
|
import { pathExists } from "../core/fs/fs-utils.js";
|
|
13
13
|
import { EXIT_CODE } from "../core/shared/constants.js";
|
|
14
14
|
import { PmCliError } from "../core/shared/errors.js";
|
|
@@ -21,6 +21,13 @@ const MUTATION_EVENT_CURSOR_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
|
21
21
|
const DEFAULT_EVENT_LIMIT = 100;
|
|
22
22
|
const MAX_EVENT_LIMIT = 1_000;
|
|
23
23
|
const DEFAULT_FOLLOW_INTERVAL_MS = 250;
|
|
24
|
+
/** Regression budgets for the canonical 200-event wire-cost fixture. */
|
|
25
|
+
export const MUTATION_EVENT_WIRE_BUDGET = Object.freeze({
|
|
26
|
+
/** Maximum default batch bytes divided by legacy row-cursor bytes. */
|
|
27
|
+
batch_to_row_ratio: 0.6,
|
|
28
|
+
/** Fixture size used to keep the ratio representative of fleet catch-up. */
|
|
29
|
+
fixture_events: 200,
|
|
30
|
+
});
|
|
24
31
|
function normalizeFilter(value) {
|
|
25
32
|
if (value === undefined)
|
|
26
33
|
return undefined;
|
|
@@ -120,14 +127,25 @@ async function resolveIndexedMutationEvents(pmRoot, query) {
|
|
|
120
127
|
let indexed = await queryHistoryEventIndex(pmRoot, query);
|
|
121
128
|
if (indexed === null) {
|
|
122
129
|
if (!(await rebuildHistoryEventIndex(pmRoot))) {
|
|
123
|
-
|
|
130
|
+
return {
|
|
131
|
+
...(await queryHistoryEventStreams(pmRoot, query)),
|
|
132
|
+
source: "authoritative_history",
|
|
133
|
+
};
|
|
124
134
|
}
|
|
125
135
|
indexed = await queryHistoryEventIndex(pmRoot, query);
|
|
126
136
|
}
|
|
127
137
|
if (indexed === null) {
|
|
128
138
|
throw new PmCliError("Mutation event index could not be opened after rebuild.", EXIT_CODE.GENERIC_FAILURE, { code: "event_index_unavailable" });
|
|
129
139
|
}
|
|
130
|
-
return indexed;
|
|
140
|
+
return { ...indexed, source: "derived_index" };
|
|
141
|
+
}
|
|
142
|
+
/** Resolves the event cursor emission mode and rejects unsupported transport spellings. */
|
|
143
|
+
function resolveMutationEventCursorMode(value) {
|
|
144
|
+
const cursorMode = value ?? "batch";
|
|
145
|
+
if (cursorMode !== "batch" && cursorMode !== "row") {
|
|
146
|
+
throw new PmCliError("Mutation event cursor mode must be batch or row.", EXIT_CODE.USAGE, { code: "invalid_event_cursor_mode" });
|
|
147
|
+
}
|
|
148
|
+
return cursorMode;
|
|
131
149
|
}
|
|
132
150
|
/** Read one bounded page of mutation events from the persistent projection. */
|
|
133
151
|
export async function listMutationEvents(options = {}) {
|
|
@@ -136,6 +154,7 @@ export async function listMutationEvents(options = {}) {
|
|
|
136
154
|
throw new PmCliError(`Tracker is not initialized at ${pmRoot}. Run pm init first.`, EXIT_CODE.NOT_FOUND);
|
|
137
155
|
}
|
|
138
156
|
const requestedLimit = parseMutationEventLimit(options.limit);
|
|
157
|
+
const cursorMode = resolveMutationEventCursorMode(options.cursorMode);
|
|
139
158
|
if (options.full === true && options.provenance === true) {
|
|
140
159
|
throw new PmCliError("Mutation event projections are mutually exclusive. Use --provenance or --full.", EXIT_CODE.USAGE);
|
|
141
160
|
}
|
|
@@ -174,7 +193,9 @@ export async function listMutationEvents(options = {}) {
|
|
|
174
193
|
};
|
|
175
194
|
const indexed = await resolveIndexedMutationEvents(pmRoot, query);
|
|
176
195
|
const events = indexed.events.map((event) => ({
|
|
177
|
-
|
|
196
|
+
...(cursorMode === "row"
|
|
197
|
+
? { cursor: encodeMutationEventCursor(event, fingerprint) }
|
|
198
|
+
: {}),
|
|
178
199
|
item_id: event.stream_id,
|
|
179
200
|
version: event.stream_offset + 1,
|
|
180
201
|
ts: event.entry.ts,
|
|
@@ -198,10 +219,13 @@ export async function listMutationEvents(options = {}) {
|
|
|
198
219
|
events,
|
|
199
220
|
count: events.length,
|
|
200
221
|
has_more: indexed.has_more,
|
|
201
|
-
|
|
222
|
+
cursor_mode: cursorMode,
|
|
223
|
+
...(indexed.events.length === 0
|
|
202
224
|
? {}
|
|
203
|
-
: {
|
|
204
|
-
|
|
225
|
+
: {
|
|
226
|
+
next_cursor: encodeMutationEventCursor(indexed.events[indexed.events.length - 1], fingerprint),
|
|
227
|
+
}),
|
|
228
|
+
source: indexed.source,
|
|
205
229
|
...(options.provenanceSummary === true
|
|
206
230
|
? {
|
|
207
231
|
provenance_summary: summarizeHistoryProvenance(indexed.events.map((event) => event.entry), vocabulary, dimensions),
|
|
@@ -210,21 +234,26 @@ export async function listMutationEvents(options = {}) {
|
|
|
210
234
|
};
|
|
211
235
|
}
|
|
212
236
|
/**
|
|
213
|
-
* Subscribe to
|
|
214
|
-
*
|
|
237
|
+
* Subscribe to complete event batches. Every yield is a recoverable boundary;
|
|
238
|
+
* an empty batch is an idle heartbeat carrying the last known cursor.
|
|
215
239
|
*/
|
|
216
|
-
export async function*
|
|
240
|
+
export async function* subscribeMutationEventBatches(options = {}) {
|
|
217
241
|
const intervalMs = options.intervalMs ?? DEFAULT_FOLLOW_INTERVAL_MS;
|
|
218
242
|
if (!Number.isSafeInteger(intervalMs) || intervalMs < 10) {
|
|
219
243
|
throw new PmCliError("Mutation event follow interval must be an integer of at least 10ms.", EXIT_CODE.USAGE, { code: "invalid_event_follow_interval" });
|
|
220
244
|
}
|
|
221
245
|
let cursor = options.since;
|
|
222
246
|
while (options.signal?.aborted !== true) {
|
|
223
|
-
const page = await listMutationEvents({
|
|
247
|
+
const page = await listMutationEvents({
|
|
248
|
+
...options,
|
|
249
|
+
cursorMode: options.cursorMode ?? "batch",
|
|
250
|
+
since: cursor,
|
|
251
|
+
});
|
|
252
|
+
const boundary = page.next_cursor === undefined && cursor !== undefined
|
|
253
|
+
? { ...page, next_cursor: cursor }
|
|
254
|
+
: page;
|
|
255
|
+
yield boundary;
|
|
224
256
|
if (page.events.length > 0) {
|
|
225
|
-
for (const event of page.events) {
|
|
226
|
-
yield event;
|
|
227
|
-
}
|
|
228
257
|
cursor = page.next_cursor;
|
|
229
258
|
continue;
|
|
230
259
|
}
|
|
@@ -238,6 +267,20 @@ export async function* subscribeMutationEvents(options = {}) {
|
|
|
238
267
|
}
|
|
239
268
|
}
|
|
240
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* Subscribe to committed mutation facts as an async iterable. The iterator
|
|
272
|
+
* performs cursor catch-up reads and waits only when no new event is available.
|
|
273
|
+
*/
|
|
274
|
+
export async function* subscribeMutationEvents(options = {}) {
|
|
275
|
+
for await (const page of subscribeMutationEventBatches({
|
|
276
|
+
...options,
|
|
277
|
+
cursorMode: "row",
|
|
278
|
+
})) {
|
|
279
|
+
for (const event of page.events) {
|
|
280
|
+
yield event;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
241
284
|
/** Public test seams for cursor contract verification. */
|
|
242
285
|
export const _testOnlyMutationEvents = {
|
|
243
286
|
decodeMutationEventCursor,
|
|
@@ -249,4 +292,4 @@ export const _testOnlyMutationEvents = {
|
|
|
249
292
|
resolveMutationEventStart,
|
|
250
293
|
};
|
|
251
294
|
//# sourceMappingURL=mutation-events.js.map
|
|
252
|
-
//# debugId=
|
|
295
|
+
//# debugId=e8cd5790-eacf-5ef7-bc0a-11ea3fe17d2d
|
package/dist/sdk/output.d.ts
CHANGED
|
@@ -14,3 +14,47 @@ export { SUPPRESS_HOST_OUTPUT_MARKER, isHostOutputSuppressed, suppressHostOutput
|
|
|
14
14
|
* non-array object.
|
|
15
15
|
*/
|
|
16
16
|
export declare function serializeNdjsonRows(rows: readonly unknown[]): string;
|
|
17
|
+
/** Terminal metadata that makes an NDJSON batch independently resumable. */
|
|
18
|
+
export interface PmNdjsonStreamTrailer {
|
|
19
|
+
/** Producer-owned constant-size fields may extend the shared trailer. */
|
|
20
|
+
readonly [key: string]: unknown;
|
|
21
|
+
/** Stable discriminator that cannot be confused with a domain row. */
|
|
22
|
+
record_type: "pm.stream.trailer";
|
|
23
|
+
/** Number of domain rows preceding this trailer in the batch. */
|
|
24
|
+
count: number;
|
|
25
|
+
/** Whether another row was available when the batch was read. */
|
|
26
|
+
has_more: boolean;
|
|
27
|
+
/** Opaque cursor that resumes strictly after this batch. */
|
|
28
|
+
next_cursor: string | null;
|
|
29
|
+
/** Name of the authoritative or derived source that produced the batch. */
|
|
30
|
+
source: string;
|
|
31
|
+
/** Optional constant-size metadata owned by the stream producer. */
|
|
32
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
33
|
+
}
|
|
34
|
+
/** Metadata accepted by {@link serializeNdjsonStream}. */
|
|
35
|
+
export interface PmNdjsonStreamTrailerInput {
|
|
36
|
+
/** Producer-owned constant-size fields may extend the shared trailer. */
|
|
37
|
+
readonly [key: string]: unknown;
|
|
38
|
+
/** The serializer owns the discriminator and callers cannot override it. */
|
|
39
|
+
readonly record_type?: never;
|
|
40
|
+
/** Number of domain rows preceding the terminal trailer. */
|
|
41
|
+
count: number;
|
|
42
|
+
/** Whether another row was available when the batch was read. */
|
|
43
|
+
has_more: boolean;
|
|
44
|
+
/** Opaque cursor that resumes strictly after this batch. */
|
|
45
|
+
next_cursor: string | null;
|
|
46
|
+
/** Name of the authoritative or derived source that produced the batch. */
|
|
47
|
+
source: string;
|
|
48
|
+
/** Optional constant-size metadata owned by the stream producer. */
|
|
49
|
+
metadata?: Readonly<Record<string, unknown>>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Serialize a bounded NDJSON batch followed by exactly one typed terminal
|
|
53
|
+
* trailer. The result has no trailing newline so callers retain framing
|
|
54
|
+
* control while consumers always receive count and recovery metadata, even
|
|
55
|
+
* for an empty batch.
|
|
56
|
+
*
|
|
57
|
+
* @throws {TypeError} When the trailer count differs from the number of domain
|
|
58
|
+
* rows or a domain row uses the reserved terminal discriminator.
|
|
59
|
+
*/
|
|
60
|
+
export declare function serializeNdjsonStream(rows: readonly unknown[], trailer: PmNdjsonStreamTrailerInput): string;
|
package/dist/sdk/output.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* renderers, and custom SDK-built hosts.
|
|
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]="38facf38-c1e1-5574-9e70-98977494d939")}catch(e){}}();
|
|
9
9
|
export { SUPPRESS_HOST_OUTPUT_MARKER, isHostOutputSuppressed, suppressHostOutput, } from "../core/output/output-control.js";
|
|
10
10
|
/**
|
|
11
11
|
* Serialize object rows as newline-delimited JSON without adding a trailing
|
|
@@ -35,5 +35,31 @@ export function serializeNdjsonRows(rows) {
|
|
|
35
35
|
})
|
|
36
36
|
.join("\n");
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Serialize a bounded NDJSON batch followed by exactly one typed terminal
|
|
40
|
+
* trailer. The result has no trailing newline so callers retain framing
|
|
41
|
+
* control while consumers always receive count and recovery metadata, even
|
|
42
|
+
* for an empty batch.
|
|
43
|
+
*
|
|
44
|
+
* @throws {TypeError} When the trailer count differs from the number of domain
|
|
45
|
+
* rows or a domain row uses the reserved terminal discriminator.
|
|
46
|
+
*/
|
|
47
|
+
export function serializeNdjsonStream(rows, trailer) {
|
|
48
|
+
if (trailer.count !== rows.length) {
|
|
49
|
+
throw new TypeError(`NDJSON trailer count ${trailer.count} does not match ${rows.length} ${rows.length === 1 ? "row" : "rows"}.`);
|
|
50
|
+
}
|
|
51
|
+
for (const [index, row] of rows.entries()) {
|
|
52
|
+
if (typeof row === "object" &&
|
|
53
|
+
row !== null &&
|
|
54
|
+
!Array.isArray(row) &&
|
|
55
|
+
Reflect.get(row, "record_type") === "pm.stream.trailer") {
|
|
56
|
+
throw new TypeError(`NDJSON row ${index} uses reserved record_type pm.stream.trailer`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return serializeNdjsonRows([
|
|
60
|
+
...rows,
|
|
61
|
+
{ ...trailer, record_type: "pm.stream.trailer" },
|
|
62
|
+
]);
|
|
63
|
+
}
|
|
38
64
|
//# sourceMappingURL=output.js.map
|
|
39
|
-
//# debugId=
|
|
65
|
+
//# debugId=38facf38-c1e1-5574-9e70-98977494d939
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/query/complete-list
|
|
3
|
+
*
|
|
4
|
+
* Certifies that a list result represents the complete, unfiltered workspace.
|
|
5
|
+
*/
|
|
6
|
+
import type { PmReadOutputReceipt } from "../read-output-contracts.js";
|
|
7
|
+
import type { ListFullResult, ListOptions } from "./list.js";
|
|
8
|
+
/** Stable reasons a purported whole-workspace list cannot be trusted. */
|
|
9
|
+
export declare const PM_COMPLETE_LIST_FINDING_CODES: readonly ["invalid_envelope", "source_incomplete", "source_unchecked", "filtered_corpus", "terminal_items_excluded", "strict_read_unproven", "page_incomplete", "count_mismatch", "projection_incomplete", "field_omission", "budget_compaction", "budget_omission", "session_projection", "invalid_item_id", "duplicate_item_id"];
|
|
10
|
+
/** Machine-readable reason a complete-list candidate failed certification. */
|
|
11
|
+
export type PmCompleteListFindingCode = (typeof PM_COMPLETE_LIST_FINDING_CODES)[number];
|
|
12
|
+
/** One independently actionable complete-list contract violation. */
|
|
13
|
+
export interface PmCompleteListFinding {
|
|
14
|
+
/** Stable finding identifier suitable for automation. */
|
|
15
|
+
code: PmCompleteListFindingCode;
|
|
16
|
+
/** Concise explanation of the evidence that failed. */
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
/** Proof attached only after every whole-corpus invariant passes. */
|
|
20
|
+
export interface PmCompleteListCertificate {
|
|
21
|
+
/** Certificate schema version. */
|
|
22
|
+
contract_version: 1;
|
|
23
|
+
/** Number of full item rows certified. */
|
|
24
|
+
item_count: number;
|
|
25
|
+
/** Number of distinct non-empty item identifiers certified. */
|
|
26
|
+
unique_item_id_count: number;
|
|
27
|
+
/** Confirms the request did not exclude closed or canceled work. */
|
|
28
|
+
terminal_items_included: true;
|
|
29
|
+
/** Confirms the underlying tracker scan reported complete. */
|
|
30
|
+
source_complete: true;
|
|
31
|
+
/** Confirms every row used the full metadata projection. */
|
|
32
|
+
full_projection: true;
|
|
33
|
+
/** Confirms neither field nor whole-result omission occurred. */
|
|
34
|
+
no_omissions: true;
|
|
35
|
+
/** Confirms no page, row, or token ceiling reduced the corpus. */
|
|
36
|
+
unbounded: true;
|
|
37
|
+
}
|
|
38
|
+
/** Full list result augmented with a fail-closed whole-corpus certificate. */
|
|
39
|
+
export interface PmCompleteListResult extends ListFullResult {
|
|
40
|
+
/** Exact proof that the returned rows represent the full tracker corpus. */
|
|
41
|
+
complete_list: PmCompleteListCertificate;
|
|
42
|
+
/** Universal read receipt when the runtime attached one. */
|
|
43
|
+
read_output?: PmReadOutputReceipt;
|
|
44
|
+
}
|
|
45
|
+
/** Inspection report returned without throwing for policy and UI consumers. */
|
|
46
|
+
export interface PmCompleteListInspection {
|
|
47
|
+
/** Whether the candidate is safe to certify as the whole tracker corpus. */
|
|
48
|
+
ok: boolean;
|
|
49
|
+
/** Every independently detected contract violation. */
|
|
50
|
+
findings: PmCompleteListFinding[];
|
|
51
|
+
}
|
|
52
|
+
/** Failure receipt with both CLI and SDK recovery paths. */
|
|
53
|
+
export interface PmCompleteListFailureReceipt {
|
|
54
|
+
/** Stable receipt schema version. */
|
|
55
|
+
contract_version: 1;
|
|
56
|
+
/** Failed contract name. */
|
|
57
|
+
contract: "complete_list";
|
|
58
|
+
/** Every independently detected contract violation. */
|
|
59
|
+
findings: PmCompleteListFinding[];
|
|
60
|
+
/** Executable recovery instructions for both public transports. */
|
|
61
|
+
recovery: {
|
|
62
|
+
/** Exact CLI command that requests the canonical whole corpus. */
|
|
63
|
+
suggested_retry: string;
|
|
64
|
+
/** Equivalent SDK operation. */
|
|
65
|
+
sdk: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Typed fail-closed error raised when whole-corpus proof is absent. */
|
|
69
|
+
export declare class PmCompleteListValidationError extends Error {
|
|
70
|
+
/** Structured failure and executable recovery evidence. */
|
|
71
|
+
readonly receipt: PmCompleteListFailureReceipt;
|
|
72
|
+
/** Construct an immutable complete-list validation failure. */
|
|
73
|
+
constructor(findings: PmCompleteListFinding[]);
|
|
74
|
+
}
|
|
75
|
+
/** Options accepted by the whole-corpus list helper. */
|
|
76
|
+
export interface PmCompleteListOptions {
|
|
77
|
+
/** Include item bodies in addition to complete metadata. */
|
|
78
|
+
includeBody?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/** Build the canonical all-status, full, strict, and unbounded list request. */
|
|
81
|
+
export declare function createCompleteListOptions(options?: PmCompleteListOptions): ListOptions & {
|
|
82
|
+
excludeTerminal: false;
|
|
83
|
+
full: true;
|
|
84
|
+
noTruncate: true;
|
|
85
|
+
outputBudget: "unbounded";
|
|
86
|
+
outputLimit: "unbounded";
|
|
87
|
+
strictRead: true;
|
|
88
|
+
};
|
|
89
|
+
/** Inspect an unknown result and report every missing whole-corpus invariant. */
|
|
90
|
+
export declare function inspectCompleteListResult(candidate: unknown): PmCompleteListInspection;
|
|
91
|
+
/** Certify a result as the whole tracker corpus or throw a typed recovery error. */
|
|
92
|
+
export declare function certifyCompleteListResult(candidate: unknown): PmCompleteListResult;
|
|
93
|
+
/** Assert whole-corpus completeness while narrowing the candidate type in place. */
|
|
94
|
+
export declare function assertCompleteListResult(candidate: unknown): asserts candidate is ListFullResult;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/** Stable reasons a purported whole-workspace list cannot be trusted. */
|
|
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]="cc342d27-aa2d-5e84-8441-46a35c42dc00")}catch(e){}}();
|
|
4
|
+
export const PM_COMPLETE_LIST_FINDING_CODES = [
|
|
5
|
+
"invalid_envelope",
|
|
6
|
+
"source_incomplete",
|
|
7
|
+
"source_unchecked",
|
|
8
|
+
"filtered_corpus",
|
|
9
|
+
"terminal_items_excluded",
|
|
10
|
+
"strict_read_unproven",
|
|
11
|
+
"page_incomplete",
|
|
12
|
+
"count_mismatch",
|
|
13
|
+
"projection_incomplete",
|
|
14
|
+
"field_omission",
|
|
15
|
+
"budget_compaction",
|
|
16
|
+
"budget_omission",
|
|
17
|
+
"session_projection",
|
|
18
|
+
"invalid_item_id",
|
|
19
|
+
"duplicate_item_id",
|
|
20
|
+
];
|
|
21
|
+
/** Typed fail-closed error raised when whole-corpus proof is absent. */
|
|
22
|
+
export class PmCompleteListValidationError extends Error {
|
|
23
|
+
/** Structured failure and executable recovery evidence. */
|
|
24
|
+
receipt;
|
|
25
|
+
/** Construct an immutable complete-list validation failure. */
|
|
26
|
+
constructor(findings) {
|
|
27
|
+
super(`Complete-list certification failed: ${findings.map((finding) => finding.code).join(", ")}.`);
|
|
28
|
+
this.name = "PmCompleteListValidationError";
|
|
29
|
+
this.receipt = {
|
|
30
|
+
contract_version: 1,
|
|
31
|
+
contract: "complete_list",
|
|
32
|
+
findings,
|
|
33
|
+
recovery: {
|
|
34
|
+
suggested_retry: "pm list-all --full --strict-read --no-truncate --output-budget unbounded --json",
|
|
35
|
+
sdk: "await client.listAllComplete()",
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Build the canonical all-status, full, strict, and unbounded list request. */
|
|
41
|
+
export function createCompleteListOptions(options = {}) {
|
|
42
|
+
return {
|
|
43
|
+
excludeTerminal: false,
|
|
44
|
+
full: true,
|
|
45
|
+
...(options.includeBody === undefined
|
|
46
|
+
? {}
|
|
47
|
+
: { includeBody: options.includeBody }),
|
|
48
|
+
noTruncate: true,
|
|
49
|
+
outputBudget: "unbounded",
|
|
50
|
+
outputLimit: "unbounded",
|
|
51
|
+
strictRead: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Narrow an unknown envelope fragment to a non-array object. */
|
|
55
|
+
function isRecord(value) {
|
|
56
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
57
|
+
}
|
|
58
|
+
/** Append one finding only when its invariant failed. */
|
|
59
|
+
function addFinding(findings, condition, code, message) {
|
|
60
|
+
if (condition)
|
|
61
|
+
findings.push({ code, message });
|
|
62
|
+
}
|
|
63
|
+
/** Append source-scan and filter-scope findings for a list envelope. */
|
|
64
|
+
function inspectSourceScope(candidate, findings) {
|
|
65
|
+
const completeness = isRecord(candidate.completeness)
|
|
66
|
+
? candidate.completeness
|
|
67
|
+
: {};
|
|
68
|
+
addFinding(findings, completeness.status === "partial", "source_incomplete", "The source scan reported unreadable tracker artifacts.");
|
|
69
|
+
addFinding(findings, completeness.status !== "complete" && completeness.status !== "partial", "source_unchecked", "The source scan did not prove completeness.");
|
|
70
|
+
const filters = isRecord(candidate.filters) ? candidate.filters : {};
|
|
71
|
+
const runtimeFilters = isRecord(filters.runtime_filters)
|
|
72
|
+
? filters.runtime_filters
|
|
73
|
+
: {};
|
|
74
|
+
const filterEchoKeys = new Set([
|
|
75
|
+
"exclude_terminal",
|
|
76
|
+
"include_body",
|
|
77
|
+
"no_truncate",
|
|
78
|
+
"runtime_filters",
|
|
79
|
+
"status",
|
|
80
|
+
"strict_read",
|
|
81
|
+
]);
|
|
82
|
+
addFinding(findings, filters.status !== "all" ||
|
|
83
|
+
Object.keys(runtimeFilters).length > 0 ||
|
|
84
|
+
Object.keys(filters).some((key) => !filterEchoKeys.has(key)), "filtered_corpus", "The list did not explicitly select every lifecycle status.");
|
|
85
|
+
addFinding(findings, filters.exclude_terminal === true, "terminal_items_excluded", "Terminal lifecycle states were explicitly excluded.");
|
|
86
|
+
addFinding(findings, filters.strict_read !== true, "strict_read_unproven", "The result does not echo strict source-read enforcement.");
|
|
87
|
+
return filters;
|
|
88
|
+
}
|
|
89
|
+
/** Append pagination, count, projection, and field-omission findings. */
|
|
90
|
+
function inspectEnvelopeShape(candidate, filters, findings) {
|
|
91
|
+
addFinding(findings, candidate.has_more !== false ||
|
|
92
|
+
candidate.next_cursor !== null ||
|
|
93
|
+
candidate.truncated !== false ||
|
|
94
|
+
candidate.applied_limit !== undefined ||
|
|
95
|
+
filters.no_truncate !== true, "page_incomplete", "Pagination or a row ceiling may have withheld items.");
|
|
96
|
+
addFinding(findings, candidate.count !== candidate.items.length ||
|
|
97
|
+
candidate.total !== candidate.items.length, "count_mismatch", "Envelope counts do not equal the returned item count.");
|
|
98
|
+
const projection = isRecord(candidate.projection) ? candidate.projection : {};
|
|
99
|
+
addFinding(findings, projection.mode !== "full" || projection.fields !== null, "projection_incomplete", "The result is not the full metadata projection.");
|
|
100
|
+
const omissionReceipt = isRecord(candidate.omission_receipt)
|
|
101
|
+
? candidate.omission_receipt
|
|
102
|
+
: undefined;
|
|
103
|
+
addFinding(findings, omissionReceipt?.has_omissions === true, "field_omission", "The omission receipt reports withheld field groups.");
|
|
104
|
+
}
|
|
105
|
+
/** Append universal-output and cross-call-session findings. */
|
|
106
|
+
function inspectOutputScope(candidate, findings) {
|
|
107
|
+
const readOutput = isRecord(candidate.read_output)
|
|
108
|
+
? candidate.read_output
|
|
109
|
+
: undefined;
|
|
110
|
+
addFinding(findings, readOutput?.strings_compacted === true ||
|
|
111
|
+
readOutput?.rows_compacted === true ||
|
|
112
|
+
candidate.output_budget_truncation !== undefined, "budget_compaction", "Universal output shaping compacted the result.");
|
|
113
|
+
const budgetExceeded = isRecord(candidate.output_budget_exceeded)
|
|
114
|
+
? candidate.output_budget_exceeded
|
|
115
|
+
: undefined;
|
|
116
|
+
addFinding(findings, readOutput?.result_omitted === true ||
|
|
117
|
+
budgetExceeded?.omitted_result === true, "budget_omission", "Universal output shaping omitted the useful result.");
|
|
118
|
+
addFinding(findings, candidate.read_session !== undefined, "session_projection", "A cross-call read session can suppress previously served facts.");
|
|
119
|
+
}
|
|
120
|
+
/** Append row-identity findings that prevent exact set reasoning. */
|
|
121
|
+
function inspectItemIds(items, findings) {
|
|
122
|
+
const ids = items.map((item) => isRecord(item) && typeof item.id === "string" ? item.id.trim() : "");
|
|
123
|
+
addFinding(findings, ids.some((id) => id.length === 0), "invalid_item_id", "Every full item row must carry a non-empty string id.");
|
|
124
|
+
addFinding(findings, new Set(ids).size !== ids.length, "duplicate_item_id", "Every item id must occur exactly once.");
|
|
125
|
+
}
|
|
126
|
+
/** Inspect an unknown result and report every missing whole-corpus invariant. */
|
|
127
|
+
export function inspectCompleteListResult(candidate) {
|
|
128
|
+
if (!isRecord(candidate) || !Array.isArray(candidate.items)) {
|
|
129
|
+
return {
|
|
130
|
+
ok: false,
|
|
131
|
+
findings: [
|
|
132
|
+
{
|
|
133
|
+
code: PM_COMPLETE_LIST_FINDING_CODES[0],
|
|
134
|
+
message: "The result must be an object with an items array.",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const envelope = candidate;
|
|
140
|
+
const findings = [];
|
|
141
|
+
const filters = inspectSourceScope(envelope, findings);
|
|
142
|
+
inspectEnvelopeShape(envelope, filters, findings);
|
|
143
|
+
inspectOutputScope(envelope, findings);
|
|
144
|
+
inspectItemIds(envelope.items, findings);
|
|
145
|
+
return { ok: findings.length === 0, findings };
|
|
146
|
+
}
|
|
147
|
+
/** Certify a result as the whole tracker corpus or throw a typed recovery error. */
|
|
148
|
+
export function certifyCompleteListResult(candidate) {
|
|
149
|
+
const inspection = inspectCompleteListResult(candidate);
|
|
150
|
+
if (!inspection.ok) {
|
|
151
|
+
throw new PmCompleteListValidationError(inspection.findings);
|
|
152
|
+
}
|
|
153
|
+
const result = candidate;
|
|
154
|
+
return {
|
|
155
|
+
...result,
|
|
156
|
+
complete_list: {
|
|
157
|
+
contract_version: 1,
|
|
158
|
+
item_count: result.items.length,
|
|
159
|
+
unique_item_id_count: new Set(result.items.map((item) => item.id)).size,
|
|
160
|
+
terminal_items_included: true,
|
|
161
|
+
source_complete: true,
|
|
162
|
+
full_projection: true,
|
|
163
|
+
no_omissions: true,
|
|
164
|
+
unbounded: true,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** Assert whole-corpus completeness while narrowing the candidate type in place. */
|
|
169
|
+
export function assertCompleteListResult(candidate) {
|
|
170
|
+
certifyCompleteListResult(candidate);
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=complete-list.js.map
|
|
173
|
+
//# debugId=cc342d27-aa2d-5e84-8441-46a35c42dc00
|