pm-ops 2026.8.29 → 2026.9.2

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/dist/index.d.ts CHANGED
@@ -1,5 +1,98 @@
1
1
  import type { ExtensionApi } from "@unbrained/pm-cli/sdk/authoring";
2
- import { type MergeDecisionReceipt } from "@unbrained/pm-cli/sdk/merge";
2
+ import { type MergeDecisionReceipt, type MergeDriverConfigurationAuditResult, type MergeFenceAuditResult } from "@unbrained/pm-cli/sdk/merge";
3
+ /** One recoverable scalar decision recorded by the field-aware merge driver. */
4
+ interface MergeReceiptDecisionView {
5
+ /** Metadata field name, or `body`. */
6
+ field: string;
7
+ /** Common-ancestor value. */
8
+ base: unknown;
9
+ /** Current-branch value. */
10
+ ours: unknown;
11
+ /** Other-branch value. */
12
+ theirs: unknown;
13
+ /** Value retained in the merged item. */
14
+ retained: unknown;
15
+ /** Value not retained in the merged item. */
16
+ discarded: unknown;
17
+ }
18
+ /** Clone-local merge decision receipt projected for the fleet report. */
19
+ interface MergeReceiptView {
20
+ /** Opaque clone-local receipt identity. */
21
+ id: string;
22
+ /** Item whose merge produced the receipt. */
23
+ item_id: string;
24
+ /** Repo-relative item path reported by the current pm SDK. */
25
+ item_path: string;
26
+ /** Raw `item_path` exactly as Git recorded it (quotes preserved). */
27
+ item_path_raw: string;
28
+ /** Whether a merge reconciliation history event consumed this receipt. */
29
+ state: "pending" | "reconciled";
30
+ /**
31
+ * Side the merge requested for unresolvable scalar conflicts (the SDK's
32
+ * `requested_preference`, folding the legacy `preferred` key). Under the
33
+ * SDK's `stable_value_order` contract the *retained* value is the
34
+ * direction-independent stable one, so this field reports the requested
35
+ * side while `decisions[].retained` reports what actually won.
36
+ */
37
+ preferred: "ours" | "theirs";
38
+ /** Fields selected cleanly from the other branch. */
39
+ fields_from_theirs: string[];
40
+ /** Collections combined from both branches. */
41
+ union_fields: string[];
42
+ /** Full recoverable scalar decisions, retained only in the clone. */
43
+ decisions: MergeReceiptDecisionView[];
44
+ /** Receipt creation timestamp. */
45
+ created_at: string;
46
+ /** Reconciliation timestamp, when consumed. */
47
+ reconciled_at?: string;
48
+ }
49
+ /** Per-repo merge-receipt report combining driver/fence audit and receipts. */
50
+ interface RepoMergeReceipts {
51
+ /** Absolute repo path. */
52
+ path: string;
53
+ /** Package name, else directory basename. */
54
+ name: string | null;
55
+ /** Whether a git workspace was found (receipts are clone-local under `.git`). */
56
+ available: boolean;
57
+ /** Clone-local merge-driver configuration audit, or null outside git. */
58
+ driver: MergeDriverConfigurationAuditResult | null;
59
+ /** Committed `.gitattributes` merge-fence audit, or null outside git. */
60
+ fence: MergeFenceAuditResult | null;
61
+ /** Receipts projected for the report. */
62
+ receipts: MergeReceiptView[];
63
+ /** Receipts in `state: "pending"`. */
64
+ pending_count: number;
65
+ /** Receipts in `state: "reconciled"`. */
66
+ reconciled_count: number;
67
+ }
68
+ /** Aggregated merge-receipt report across one or many repos. */
69
+ interface MergeReceiptsResult {
70
+ /** ISO timestamp for the report. */
71
+ generated_at: string;
72
+ /** Per-repo reports in the order passed on `--repos`. */
73
+ repos: RepoMergeReceipts[];
74
+ /** Fleet-wide rollups driving the gate. */
75
+ summary: {
76
+ /** Total repos scanned. */
77
+ total: number;
78
+ /** Repos with at least one pending receipt. */
79
+ with_pending: number;
80
+ /** Total pending receipts across the fleet. */
81
+ total_pending: number;
82
+ /** Total reconciled receipts across the fleet. */
83
+ total_reconciled: number;
84
+ /** Repos whose merge driver is missing from clone-local git config. */
85
+ missing_driver: number;
86
+ /** Repos with no committed `.gitattributes` merge fence. */
87
+ missing_fence: number;
88
+ /** Repos whose driver commands do not match this installation (reported, not gated — upstream #773). */
89
+ drifted_driver: number;
90
+ /** Repos whose committed fence drifted from the active schema in either direction (reported). */
91
+ drifted_fence: number;
92
+ /** Repos whose fence leaves item paths UNCOVERED, so they fall back to git's line merge (gated). */
93
+ unprotected_fence: number;
94
+ };
95
+ }
3
96
  /**
4
97
  * Project a receipt's requested preference side for the fleet view.
5
98
  *
@@ -11,6 +104,13 @@ import { type MergeDecisionReceipt } from "@unbrained/pm-cli/sdk/merge";
11
104
  * gives the legacy/default arms a directly testable home.
12
105
  */
13
106
  export declare function receiptPreferredSide(receipt: Readonly<Pick<MergeDecisionReceipt, "requested_preference" | "preferred">>): "ours" | "theirs";
107
+ /**
108
+ * Render the merge-receipt report as a GitHub-flavoured markdown document: a
109
+ * fleet summary table, a per-repo driver/fence status line, and one row per
110
+ * receipt with its state, item id, repository-relative path, preferred side, and the
111
+ * retained/discarded values for every scalar conflict decision.
112
+ */
113
+ export declare function renderMergeReceiptsMarkdown(result: MergeReceiptsResult): string;
14
114
  interface RepoMetrics {
15
115
  path: string;
16
116
  repo: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAA0C,MAAM,iCAAiC,CAAC;AAY5G,OAAO,EAKL,KAAK,oBAAoB,EAG1B,MAAM,6BAA6B,CAAC;AAiqDrC;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,sBAAsB,GAAG,WAAW,CAAC,CAAC,GAClF,MAAM,GAAG,QAAQ,CAEnB;AA4UD,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,wEAAwE;IACxE,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sDAAsD;IACtD,yBAAyB,EAAE,MAAM,CAAC;IAClC,4EAA4E;IAC5E,sBAAsB,EAAE,MAAM,CAAC;IAC/B,wFAAwF;IACxF,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AA6KD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,WAAW,EAAE,GAAG,IAAI,CA0BvE;;;;kBAqNe,YAAY"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1499,7 +1499,16 @@ function markdownCell(text, limit) {
1499
1499
  const truncated = escaped.slice(0, limit);
1500
1500
  return truncated.replace(/\\+$/, (slashes) => slashes.length % 2 === 0 ? slashes : slashes.slice(0, -1));
1501
1501
  }
1502
- /** Render an unknown retained/discarded value as a short, single-line markdown cell. */
1502
+ /**
1503
+ * Render an unknown retained/discarded value as a short, single-line markdown cell.
1504
+ *
1505
+ * `retained` and `discarded` are typed `unknown`, and a receipt written by an
1506
+ * older pm merge driver can omit either one. pm 2026.8.31 always populates both
1507
+ * for the conflict shapes the test lab produces, so this `undefined` arm is no
1508
+ * longer reachable through a live merge — it is covered directly instead. Do not
1509
+ * delete it: it is what keeps an older or partial receipt rendering as `-`
1510
+ * rather than the string `"undefined"`.
1511
+ */
1503
1512
  function describeDecisionValue(value) {
1504
1513
  if (value === undefined)
1505
1514
  return "-";
@@ -1512,7 +1521,7 @@ function describeDecisionValue(value) {
1512
1521
  * receipt with its state, item id, repository-relative path, preferred side, and the
1513
1522
  * retained/discarded values for every scalar conflict decision.
1514
1523
  */
1515
- function renderMergeReceiptsMarkdown(result) {
1524
+ export function renderMergeReceiptsMarkdown(result) {
1516
1525
  const lines = [];
1517
1526
  lines.push("# pm-ops merge-receipts");
1518
1527
  lines.push("");
@@ -1541,6 +1550,10 @@ function renderMergeReceiptsMarkdown(result) {
1541
1550
  lines.push(renderMarkdownRow(["receipt", "item_id", "state", "item_path", "preferred", "field", "retained", "discarded"]));
1542
1551
  lines.push(renderMarkdownRow(["---", "---", "---", "---", "---", "---", "---", "---"]));
1543
1552
  for (const receipt of repo.receipts) {
1553
+ // A receipt with no recoverable scalar decisions still deserves a row, so the
1554
+ // report never silently omits a pending receipt. pm 2026.8.31 records at least
1555
+ // one decision for every conflict the lab can produce, so this fallback is
1556
+ // covered directly rather than through a live merge.
1544
1557
  const decisions = receipt.decisions.length > 0 ? receipt.decisions : [{ field: "-", retained: "-", discarded: "-" }];
1545
1558
  for (const decision of decisions) {
1546
1559
  lines.push(renderMarkdownRow([
@@ -2034,7 +2047,7 @@ function renderDocstringsMarkdown(result) {
2034
2047
  const defineExtension = (module) => module;
2035
2048
  export default defineExtension({
2036
2049
  name: "pm-ops",
2037
- version: "2026.8.29",
2050
+ version: "2026.9.2",
2038
2051
  activate(api) {
2039
2052
  if (typeof api.registerRenderer === "function") {
2040
2053
  // Derived from OPS_COMMAND_PATHS rather than restated. A literal copy is