@unbrained/pm-cli 2026.9.5 → 2026.9.7
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/CHANGELOG.md +39 -3
- package/dist/cli/main.js +10 -8
- package/dist/cli/register-operations.js +13 -8
- package/dist/cli-bundle/bundle-manifest.json +150 -158
- package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +9 -0
- package/dist/cli-bundle/chunks/{chunk-WWAEGW4V.js → chunk-BAX2BYUW.js} +66 -66
- package/dist/cli-bundle/chunks/{chunk-IBYREGSP.js → chunk-CKOL5U7J.js} +51 -51
- package/dist/cli-bundle/chunks/{chunk-ZRAQS3UA.js → chunk-JT6XF6GE.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-HUAQITVW.js → chunk-JX4DH4WC.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-QPZHQY2O.js → chunk-NPN5I67J.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-66VPKFP2.js → chunk-OF76PSDQ.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +232 -0
- package/dist/cli-bundle/chunks/{chunk-3HBDTJRK.js → chunk-RWWBOWJ5.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +40 -0
- package/dist/cli-bundle/chunks/{chunk-SF6SM6U5.js → chunk-WI2AYPA7.js} +2 -2
- package/dist/cli-bundle/chunks/{register-list-query-AYKGODWT.js → register-list-query-5JL52D33.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-YZYG53SQ.js → register-mutation-ZPG7KMVP.js} +2 -2
- package/dist/cli-bundle/chunks/register-operations-I5W3KMGB.js +2 -0
- package/dist/cli-bundle/chunks/{register-setup-UJCRVGBG.js → register-setup-H4SVAVX7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-QL66QNWG.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-WOCIPMW7.js → chunk-EZR3GHFE.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +56 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3ASU6WGI.js → chunk-LIJIA4TY.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-H5FOGAVD.js → chunk-M6PBRFSH.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-B2TTQGFG.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-YEF5PQQ2.js → chunk-PEFABF7E.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-KWWCBWFO.js → chunk-QTVCLMSZ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-4OSHLNUE.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 +20 -20
- 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 -42
- 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/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/output/mutation-projection.js +12 -5
- 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.js +6 -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/cli-contracts/flag-contracts.d.ts +2 -0
- package/dist/sdk/cli-contracts/flag-contracts.js +7 -5
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +4 -3
- 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 +18 -6
- package/dist/sdk/cli-contracts/tool-schema.d.ts +2 -2
- package/dist/sdk/cli-contracts/tool-schema.js +17 -5
- package/dist/sdk/context/usage-ledger.d.ts +41 -0
- package/dist/sdk/context/usage-ledger.js +186 -0
- package/dist/sdk/context-usage.d.ts +18 -9
- package/dist/sdk/context-usage.js +37 -112
- 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 +8 -8
- 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.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/history-repair.d.ts +7 -0
- package/dist/sdk/history-repair.js +48 -13
- 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/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/public-surface.json +1 -1
- package/dist/sdk/query/duplicate-candidates.d.ts +7 -0
- package/dist/sdk/query/duplicate-candidates.js +71 -0
- package/dist/sdk/query/get.js +7 -7
- package/dist/sdk/query.d.ts +2 -0
- package/dist/sdk/query.js +4 -2
- package/dist/sdk/read-output-session.js +5 -3
- package/dist/sdk/runtime.js +4 -2
- package/dist/sdk/similarity.d.ts +24 -3
- package/dist/sdk/similarity.js +50 -55
- package/docs/AGENT_GUIDE.md +23 -2
- 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/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 +18 -1
- package/docs/RELEASING.md +7 -2
- package/docs/SDK_LIFECYCLE.md +27 -0
- package/docs/SDK_RUNTIME_BOUNDARIES.md +11 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +1 -1
- package/marketplace.json +2 -2
- package/package.json +6 -3
- 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-codex/.codex-plugin/plugin.json +1 -1
- package/dist/cli-bundle/chunks/chunk-LUI4E32I.js +0 -231
- package/dist/cli-bundle/chunks/chunk-SVK5Y64D.js +0 -40
- package/dist/cli-bundle/chunks/chunk-V5GBD5KV.js +0 -9
- package/dist/cli-bundle/chunks/register-operations-KFSCC7QV.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-24IG6ONG.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-3ITLIBKS.js +0 -56
- package/dist/cli-bundle/focused-chunks/chunk-3NQD5YDG.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-65N6JHAN.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-GUBM3RGY.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-JX3RCVCX.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-MV752SUC.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-PKIPMCFS.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-RJMR2JIF.js +0 -9
- package/dist/cli-bundle/focused-chunks/chunk-WIUPWIZR.js +0 -177
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { PreparedSimilarityText } from "../similarity-scoring.js";
|
|
2
|
+
/** Declared safety bound applies to retained pairs, never silently to recall. */
|
|
3
|
+
export declare const MAX_DUPLICATE_CANDIDATE_PAIRS = 1000000;
|
|
4
|
+
/** Generate every potentially qualifying pair once, with an explicit exhaustive oracle. */
|
|
5
|
+
export declare function collectDuplicateCandidatePairs(items: readonly {
|
|
6
|
+
prepared: PreparedSimilarityText;
|
|
7
|
+
}[], maxPairs?: number, threshold?: number, exhaustive?: boolean): Set<string>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sdk/query/duplicate-candidates
|
|
3
|
+
*
|
|
4
|
+
* Lossless prefix filtering for Jaccard joins. A globally ordered prefix of
|
|
5
|
+
* length n-ceil(threshold*n)+1 must intersect the corresponding prefix of
|
|
6
|
+
* every qualifying set. Exact titles and issue codes remain independent
|
|
7
|
+
* signals, including titles with no word tokens.
|
|
8
|
+
*/
|
|
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]="d425cf45-5b62-5613-a5ce-78da6b0e201b")}catch(e){}}();
|
|
11
|
+
import { EXIT_CODE } from "../../core/shared/constants.js";
|
|
12
|
+
import { PmCliError } from "../../core/shared/errors.js";
|
|
13
|
+
/** Declared safety bound applies to retained pairs, never silently to recall. */
|
|
14
|
+
export const MAX_DUPLICATE_CANDIDATE_PAIRS = 1_000_000;
|
|
15
|
+
/** Generate every potentially qualifying pair once, with an explicit exhaustive oracle. */
|
|
16
|
+
export function collectDuplicateCandidatePairs(items, maxPairs = MAX_DUPLICATE_CANDIDATE_PAIRS, threshold = 0.8, exhaustive = false) {
|
|
17
|
+
const candidates = new Set();
|
|
18
|
+
/** Deduplicate a candidate pair and refuse before publishing an over-budget result. */
|
|
19
|
+
const add = (left, right) => {
|
|
20
|
+
candidates.add(`${left}:${right}`);
|
|
21
|
+
if (candidates.size > maxPairs) {
|
|
22
|
+
throw new PmCliError(`Duplicate sweep exceeded ${maxPairs} candidate pairs; narrow it with statuses or since.`, EXIT_CODE.CONFLICT, { code: "duplicate_sweep_cost_limit", required: "Narrow the duplicate query or increase its similarity threshold. No partial result was returned." });
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
if (exhaustive || threshold === 0) {
|
|
26
|
+
for (let right = 0; right < items.length; right += 1) {
|
|
27
|
+
for (let left = 0; left < right; left += 1)
|
|
28
|
+
add(left, right);
|
|
29
|
+
}
|
|
30
|
+
return candidates;
|
|
31
|
+
}
|
|
32
|
+
const frequencies = new Map();
|
|
33
|
+
for (const { prepared } of items) {
|
|
34
|
+
for (const token of prepared.tokens)
|
|
35
|
+
frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
36
|
+
}
|
|
37
|
+
const exact = new Map();
|
|
38
|
+
const codes = new Map();
|
|
39
|
+
const prefixes = new Map();
|
|
40
|
+
for (const [right, { prepared }] of items.entries()) {
|
|
41
|
+
const tokens = [...prepared.tokens].sort((left, other) => frequencies.get(left) - frequencies.get(other) || left.localeCompare(other));
|
|
42
|
+
// An epsilon expands rather than shrinks the prefix at floating boundaries.
|
|
43
|
+
const prefixLength = tokens.length - Math.ceil(threshold * tokens.length - 1e-12) + 1;
|
|
44
|
+
const signals = [
|
|
45
|
+
{ index: exact, keys: [prepared.normalized], lengthFilter: false },
|
|
46
|
+
{ index: codes, keys: threshold <= 0.99 ? prepared.issueCodes : [], lengthFilter: false },
|
|
47
|
+
{ index: prefixes, keys: tokens.length === 0 ? [""] : tokens.slice(0, prefixLength), lengthFilter: true },
|
|
48
|
+
];
|
|
49
|
+
for (const signal of signals) {
|
|
50
|
+
appendPostingCandidates(items, right, signal, threshold, add);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return candidates;
|
|
54
|
+
}
|
|
55
|
+
/** Join one exact/code/prefix posting list, applying the Jaccard length bound only to tokens. */
|
|
56
|
+
function appendPostingCandidates(items, right, signal, threshold, add) {
|
|
57
|
+
const rightLength = items[right].prepared.tokens.length;
|
|
58
|
+
for (const key of signal.keys) {
|
|
59
|
+
const posting = signal.index.get(key) ?? [];
|
|
60
|
+
for (const left of posting) {
|
|
61
|
+
const leftLength = items[left].prepared.tokens.length;
|
|
62
|
+
if (signal.lengthFilter && Math.min(leftLength, rightLength) / Math.max(leftLength, rightLength) + 1e-12 < threshold)
|
|
63
|
+
continue;
|
|
64
|
+
add(left, right);
|
|
65
|
+
}
|
|
66
|
+
posting.push(right);
|
|
67
|
+
signal.index.set(key, posting);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=duplicate-candidates.js.map
|
|
71
|
+
//# debugId=d425cf45-5b62-5613-a5ce-78da6b0e201b
|
package/dist/sdk/query/get.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Implements the SDK-owned item query shared by every surface.
|
|
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]="88732a1d-ae5a-5dfb-93d6-9659cb6f05c3")}catch(e){}}();
|
|
8
8
|
import { assertInitializedTracker } from "../environment/tracker-preflight.js";
|
|
9
9
|
import { getActiveExtensionRegistrations, toItemRecord, resolveItemTypeRegistry, resolveRuntimeFieldRegistry, resolveRuntimeStatusRegistry, EXIT_CODE, TYPE_TO_FOLDER, PmCliError, buildItemNotFoundError, listAllItemMetadataLight, locateItem, readLocatedItem, getHistoryPath, resolvePmRoot, readSettings, resolveAuthor, } from "../runtime-primitives.js";
|
|
10
10
|
import { readHistoryEntries } from "../history-read.js";
|
|
@@ -337,14 +337,14 @@ function attachGetLinked(result, context, fields, includeLinked) {
|
|
|
337
337
|
docs: includeLinkedDocs ? (context.metadata.docs ?? []) : [],
|
|
338
338
|
};
|
|
339
339
|
}
|
|
340
|
-
|
|
340
|
+
/** Enumerate hierarchy metadata only when the caller explicitly requested a child rollup. */
|
|
341
|
+
async function buildGetChildrenRollup(context, includeChildren) {
|
|
341
342
|
if (!includeChildren) {
|
|
342
343
|
return undefined;
|
|
343
344
|
}
|
|
344
345
|
const statusRegistry = resolveRuntimeStatusRegistry(context.settings.schema);
|
|
345
346
|
const corpus = await listAllItemMetadataLight(context.pmRoot, context.settings.item_format, context.typeToFolder, undefined, context.settings.schema);
|
|
346
|
-
|
|
347
|
-
return rollup.count > 0 || includeEmpty ? rollup : undefined;
|
|
347
|
+
return buildItemChildrenRollup(context.locatedId, corpus, statusRegistry);
|
|
348
348
|
}
|
|
349
349
|
function attachGetSchedule(result, context, fields, includeSchedule) {
|
|
350
350
|
if (!includeSchedule) {
|
|
@@ -395,7 +395,7 @@ export async function runGet(id, global, options = {}) {
|
|
|
395
395
|
const includeChildren = context.historical === undefined &&
|
|
396
396
|
(projection.fieldProjection
|
|
397
397
|
? fieldsIncludeRoot(projection.fields, "children")
|
|
398
|
-
: projection.depth
|
|
398
|
+
: projection.depth === "deep" &&
|
|
399
399
|
shouldAutoIncludeGetChildren(context.metadata.type));
|
|
400
400
|
const includeSchedule = projection.fieldProjection
|
|
401
401
|
? fieldsIncludeRoot(projection.fields, "schedule")
|
|
@@ -414,7 +414,7 @@ export async function runGet(id, global, options = {}) {
|
|
|
414
414
|
result.claim_state = claimState;
|
|
415
415
|
}
|
|
416
416
|
attachGetSchedule(result, context, projection.fields, includeSchedule);
|
|
417
|
-
const children = await buildGetChildrenRollup(context, includeChildren
|
|
417
|
+
const children = await buildGetChildrenRollup(context, includeChildren);
|
|
418
418
|
if (children !== undefined) {
|
|
419
419
|
result.children = children;
|
|
420
420
|
}
|
|
@@ -448,4 +448,4 @@ export const _testOnlyGetCommand = {
|
|
|
448
448
|
shouldAutoIncludeGetChildren,
|
|
449
449
|
};
|
|
450
450
|
//# sourceMappingURL=get.js.map
|
|
451
|
-
//# debugId=
|
|
451
|
+
//# debugId=88732a1d-ae5a-5dfb-93d6-9659cb6f05c3
|
package/dist/sdk/query.d.ts
CHANGED
|
@@ -28,3 +28,5 @@ export * from "./context-signal-store.js";
|
|
|
28
28
|
export * from "./context-usage.js";
|
|
29
29
|
export * from "./workspace-memory.js";
|
|
30
30
|
export * from "./duplicates.js";
|
|
31
|
+
export { analyzeDuplicateItems, type DuplicateItemInput, type DuplicateItemsAnalysis } from "./similarity.js";
|
|
32
|
+
export { measureItemMetadataReadWork, type ItemMetadataReadWork } from "../core/store/item-metadata-read-work.js";
|
package/dist/sdk/query.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Provides focused read, search, pagination, and context-ranking primitives.
|
|
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]="affe6060-5129-5914-9d4e-46c300d49080")}catch(e){}}();
|
|
8
8
|
export * from "./query/list.js";
|
|
9
9
|
export * from "./query/complete-list.js";
|
|
10
10
|
export * from "./query/search.js";
|
|
@@ -30,5 +30,7 @@ export * from "./context-signal-store.js";
|
|
|
30
30
|
export * from "./context-usage.js";
|
|
31
31
|
export * from "./workspace-memory.js";
|
|
32
32
|
export * from "./duplicates.js";
|
|
33
|
+
export { analyzeDuplicateItems } from "./similarity.js";
|
|
34
|
+
export { measureItemMetadataReadWork } from "../core/store/item-metadata-read-work.js";
|
|
33
35
|
//# sourceMappingURL=query.js.map
|
|
34
|
-
//# debugId=
|
|
36
|
+
//# debugId=affe6060-5129-5914-9d4e-46c300d49080
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* budgets and resolvable references to item facts served by earlier reads.
|
|
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]="bb73f77d-d372-5d68-8ef2-90b208129f21")}catch(e){}}();
|
|
9
9
|
import { EXIT_CODE } from "../core/shared/constants.js";
|
|
10
10
|
import { PmCliError } from "../core/shared/errors.js";
|
|
11
11
|
import { mapReadOutputRows, readOutputRowCollections, } from "./read-output-rows.js";
|
|
@@ -119,7 +119,9 @@ function summarizeReadOutputSessionRows(result) {
|
|
|
119
119
|
continue;
|
|
120
120
|
}
|
|
121
121
|
const itemId = itemIdFromRow(row);
|
|
122
|
-
|
|
122
|
+
// Activity can contain workspace pseudo-identities. Preserve those rows
|
|
123
|
+
// in the response, but only carry ids the next invocation can consume.
|
|
124
|
+
if (itemId !== undefined && ITEM_ID_PATTERN.test(itemId))
|
|
123
125
|
deliveredIds.add(itemId);
|
|
124
126
|
}
|
|
125
127
|
}
|
|
@@ -204,4 +206,4 @@ export function readOutputSessionRemainingTokens(state) {
|
|
|
204
206
|
return Math.max(0, state.token_budget - state.spent_tokens);
|
|
205
207
|
}
|
|
206
208
|
//# sourceMappingURL=read-output-session.js.map
|
|
207
|
-
//# debugId=
|
|
209
|
+
//# debugId=bb73f77d-d372-5d68-8ef2-90b208129f21
|
package/dist/sdk/runtime.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Defines public SDK APIs and package-author helpers for Runtime.
|
|
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]="422ff60b-97b1-5477-a7b8-ed4f983155e0")}catch(e){}}();
|
|
8
8
|
export { PM_GITIGNORE_END, PM_GITIGNORE_START, ensurePmGitignore, getPmGitignoreBlock, } from "./workspace.js";
|
|
9
9
|
export { SEARCH_EXTENSION_FLAG_DEFINITIONS } from "./extension-contracts.js";
|
|
10
10
|
export * from "./cli-contracts/agent-output-contracts.js";
|
|
@@ -1770,11 +1770,13 @@ const SDK_ACTION_HANDLERS = {
|
|
|
1770
1770
|
return runGet(id, ctx.global, applyContextIntentProjection("get", ctx.options, [id]));
|
|
1771
1771
|
},
|
|
1772
1772
|
search: runMcpSearchAction,
|
|
1773
|
+
/** Normalize MCP duplicate controls before delegating to the shared SDK action. */
|
|
1773
1774
|
duplicates: (ctx) => {
|
|
1774
1775
|
const status = typeof ctx.options.status === "string"
|
|
1775
1776
|
? [ctx.options.status]
|
|
1776
1777
|
: readStringArray(ctx.options.status);
|
|
1777
1778
|
return runDuplicates(ctx.global, {
|
|
1779
|
+
exhaustive: ctx.options.exhaustive === true,
|
|
1778
1780
|
...(status.length === 0 ? {} : { status }),
|
|
1779
1781
|
since: readString(ctx.options, "since"),
|
|
1780
1782
|
threshold: typeof ctx.options.threshold === "number"
|
|
@@ -1924,4 +1926,4 @@ async function loadWorkspaceExtensionRegistrations(pmRoot, settings, cwd) {
|
|
|
1924
1926
|
}
|
|
1925
1927
|
}
|
|
1926
1928
|
//# sourceMappingURL=runtime.js.map
|
|
1927
|
-
//# debugId=
|
|
1929
|
+
//# debugId=422ff60b-97b1-5477-a7b8-ed4f983155e0
|
package/dist/sdk/similarity.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { collectDuplicateCandidatePairs } from "./query/duplicate-candidates.js";
|
|
1
2
|
import type { ItemMetadata } from "../types/index.js";
|
|
2
3
|
import { type PreparedSimilarityText } from "./similarity-scoring.js";
|
|
3
4
|
export { jaccardSimilarity, normalizeSimilarityText, prepareSimilarityText, scoreItemSimilarity, scorePreparedItemSimilarity, tokenizeSimilarityText, type ItemSimilarityScore, type PreparedSimilarityText, } from "./similarity-scoring.js";
|
|
@@ -51,6 +52,8 @@ export interface SimilarItemsResult {
|
|
|
51
52
|
}
|
|
52
53
|
/** Query controls for one all-status duplicate-cluster sweep. */
|
|
53
54
|
export interface FindDuplicateClustersOptions extends FindSimilarItemsOptions {
|
|
55
|
+
/** Compare every pair as an exact oracle; retains the declared safety bound. */
|
|
56
|
+
exhaustive?: boolean;
|
|
54
57
|
/** Lifecycle statuses to include; omit to inspect every status. */
|
|
55
58
|
statuses?: readonly string[];
|
|
56
59
|
/** Inclusive ISO timestamp lower bound on item creation. */
|
|
@@ -97,6 +100,14 @@ export interface DuplicateClustersResult {
|
|
|
97
100
|
cost: {
|
|
98
101
|
/** Items retained after filters. */
|
|
99
102
|
item_count: number;
|
|
103
|
+
/** Algorithm used to generate candidates before canonical scoring. */
|
|
104
|
+
algorithm: "prefix_exact" | "exhaustive";
|
|
105
|
+
/** Pairs in the exhaustive population, including pairs pruned without scoring. */
|
|
106
|
+
possible_pairs: number;
|
|
107
|
+
/** Safety ceiling, exceeded only by an explicit refusal. */
|
|
108
|
+
pair_limit: number;
|
|
109
|
+
/** Every qualifying pair is retained by candidate generation. */
|
|
110
|
+
recall_guarantee: "exact";
|
|
100
111
|
/** Candidate pairs sharing at least one deterministic signal. */
|
|
101
112
|
candidate_pairs: number;
|
|
102
113
|
/** Candidate pairs scored. */
|
|
@@ -104,7 +115,7 @@ export interface DuplicateClustersResult {
|
|
|
104
115
|
};
|
|
105
116
|
}
|
|
106
117
|
interface PreparedDuplicateItem {
|
|
107
|
-
item: ItemMetadata
|
|
118
|
+
item: Pick<ItemMetadata, "id" | "title" | "status" | "type">;
|
|
108
119
|
prepared: PreparedSimilarityText;
|
|
109
120
|
}
|
|
110
121
|
/** Create/copy governance envelope attached when likely duplicates exist. */
|
|
@@ -131,7 +142,6 @@ export interface SimilarityRecovery {
|
|
|
131
142
|
}
|
|
132
143
|
/** Render compact warning tokens shared by create and copy results. */
|
|
133
144
|
export declare function similarityAdvisoryWarnings(advisory: SimilarityAdvisory | undefined): string[];
|
|
134
|
-
declare function collectDuplicateCandidatePairs(items: PreparedDuplicateItem[], maxPairEvaluations?: number): Set<string>;
|
|
135
145
|
/** Internal deterministic seams used to prove bounded batch behavior without building a million-pair fixture. */
|
|
136
146
|
export declare const _testOnlySimilarity: {
|
|
137
147
|
buildDuplicateClusters: typeof buildDuplicateClusters;
|
|
@@ -142,10 +152,21 @@ declare function createDuplicateUnionFind(size: number): {
|
|
|
142
152
|
parent: number[];
|
|
143
153
|
findRoot: (index: number) => number;
|
|
144
154
|
};
|
|
155
|
+
/** Group unioned endpoints and their scored evidence once, then deterministically rank and limit complete components. */
|
|
145
156
|
declare function buildDuplicateClusters(items: PreparedDuplicateItem[], matches: DuplicateClusterMatch[], union: ReturnType<typeof createDuplicateUnionFind>, limit: number): DuplicateCluster[];
|
|
146
157
|
/** Find likely existing duplicates through an index-first bounded read. */
|
|
147
158
|
export declare function findSimilarItems(candidate: SimilarItemCandidate, options?: FindSimilarItemsOptions): Promise<SimilarItemsResult>;
|
|
148
|
-
/**
|
|
159
|
+
/** Minimal metadata accepted from package-owned or remote project stores. */
|
|
160
|
+
export type DuplicateItemInput = Pick<ItemMetadata, "id" | "title" | "status" | "type">;
|
|
161
|
+
/** Exact in-memory duplicate analysis, independent of a filesystem workspace. */
|
|
162
|
+
export type DuplicateItemsAnalysis = Omit<DuplicateClustersResult, "source" | "filters">;
|
|
163
|
+
/**
|
|
164
|
+
* Cluster caller-owned metadata using the same scorer and safety bounds as the CLI.
|
|
165
|
+
* Candidate filtering is lossless; exhaustive mode provides an independent pair
|
|
166
|
+
* population for recall checks. Inputs are copied, sorted and never mutated.
|
|
167
|
+
*/
|
|
168
|
+
export declare function analyzeDuplicateItems(input: readonly DuplicateItemInput[], options?: Pick<FindDuplicateClustersOptions, "threshold" | "limit" | "exhaustive">): DuplicateItemsAnalysis;
|
|
169
|
+
/** Find connected duplicate clusters with one metadata read and shared package analysis. */
|
|
149
170
|
export declare function findDuplicateClusters(options?: FindDuplicateClustersOptions): Promise<DuplicateClustersResult>;
|
|
150
171
|
/** Apply off/advisory/strict mutation governance through the shared query. */
|
|
151
172
|
export declare function evaluateSimilarityGovernance(candidate: SimilarItemCandidate, options: FindSimilarItemsOptions & {
|
package/dist/sdk/similarity.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* governance and package-owned duplicate analysis.
|
|
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]="9baf5711-9796-57e8-99e5-8990fd481ef8")}catch(e){}}();
|
|
9
9
|
import { EXIT_CODE } from "../core/shared/constants.js";
|
|
10
10
|
import { PmCliError } from "../core/shared/errors.js";
|
|
11
11
|
import { resolveItemTypeRegistry } from "../core/item/type-registry.js";
|
|
@@ -16,6 +16,7 @@ import { resolvePmRoot } from "../core/store/paths.js";
|
|
|
16
16
|
import { readSettings } from "../core/store/settings.js";
|
|
17
17
|
import { readItemMetadataDerivedIndexState } from "../core/store/item-metadata-cache.js";
|
|
18
18
|
import { querySimilarItemMetadataIndex } from "../core/store/item-metadata-query-index.js";
|
|
19
|
+
import { collectDuplicateCandidatePairs, MAX_DUPLICATE_CANDIDATE_PAIRS } from "./query/duplicate-candidates.js";
|
|
19
20
|
import { prepareSimilarityText, scoreItemSimilarity, scorePreparedItemSimilarity, tokenizeSimilarityText, } from "./similarity-scoring.js";
|
|
20
21
|
export { jaccardSimilarity, normalizeSimilarityText, prepareSimilarityText, scoreItemSimilarity, scorePreparedItemSimilarity, tokenizeSimilarityText, } from "./similarity-scoring.js";
|
|
21
22
|
const DEFAULT_SIMILARITY_LIMIT = 3;
|
|
@@ -23,7 +24,6 @@ const MAX_SIMILARITY_LIMIT = 20;
|
|
|
23
24
|
const DEFAULT_SIMILARITY_THRESHOLD = 0.8;
|
|
24
25
|
const DEFAULT_DUPLICATE_CLUSTER_LIMIT = 100;
|
|
25
26
|
const MAX_DUPLICATE_CLUSTER_LIMIT = 1_000;
|
|
26
|
-
const MAX_BATCH_PAIR_EVALUATIONS = 1_000_000;
|
|
27
27
|
/** Render compact warning tokens shared by create and copy results. */
|
|
28
28
|
export function similarityAdvisoryWarnings(advisory) {
|
|
29
29
|
return advisory === undefined
|
|
@@ -80,7 +80,8 @@ function validateDuplicateClusterOptions(options) {
|
|
|
80
80
|
}
|
|
81
81
|
return { limit, since };
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
/** Resolve registered statuses and load the metadata population before applying status and creation-time filters. */
|
|
84
|
+
async function loadDuplicateItems(options, since) {
|
|
84
85
|
const pmRoot = resolvePmRoot(options.cwd ?? process.cwd(), options.pmRoot);
|
|
85
86
|
const settings = await readSettings(pmRoot);
|
|
86
87
|
const typeRegistry = resolveItemTypeRegistry(settings);
|
|
@@ -111,43 +112,10 @@ async function loadPreparedDuplicateItems(options, since) {
|
|
|
111
112
|
return {
|
|
112
113
|
items: (await listAllItemMetadataLight(pmRoot, settings.item_format, typeRegistry.type_to_folder, undefined, settings.schema))
|
|
113
114
|
.filter((item) => (!allowedStatuses || allowedStatuses.has(item.status)) &&
|
|
114
|
-
(!since || new Date(item.created_at).getTime() >= since.getTime()))
|
|
115
|
-
.sort((left, right) => left.id.localeCompare(right.id))
|
|
116
|
-
.map((item) => ({ item, prepared: prepareSimilarityText(item.title) })),
|
|
115
|
+
(!since || new Date(item.created_at).getTime() >= since.getTime())),
|
|
117
116
|
statuses: effectiveStatuses,
|
|
118
117
|
};
|
|
119
118
|
}
|
|
120
|
-
function collectDuplicateCandidatePairs(items, maxPairEvaluations = MAX_BATCH_PAIR_EVALUATIONS) {
|
|
121
|
-
const candidates = new Set();
|
|
122
|
-
const indexes = [
|
|
123
|
-
new Map(),
|
|
124
|
-
new Map(),
|
|
125
|
-
new Map(),
|
|
126
|
-
];
|
|
127
|
-
for (const [index, entry] of items.entries()) {
|
|
128
|
-
const signals = [
|
|
129
|
-
[entry.prepared.normalized],
|
|
130
|
-
entry.prepared.issueCodes,
|
|
131
|
-
entry.prepared.tokens,
|
|
132
|
-
];
|
|
133
|
-
for (const [signalKind, values] of signals.entries()) {
|
|
134
|
-
for (const value of values) {
|
|
135
|
-
const matches = indexes[signalKind].get(value) ?? [];
|
|
136
|
-
for (const other of matches)
|
|
137
|
-
candidates.add(`${other}:${index}`);
|
|
138
|
-
if (candidates.size > maxPairEvaluations) {
|
|
139
|
-
throw new PmCliError(`Duplicate sweep exceeded ${MAX_BATCH_PAIR_EVALUATIONS} candidate pairs; narrow it with statuses or since.`, EXIT_CODE.CONFLICT, {
|
|
140
|
-
code: "duplicate_sweep_cost_limit",
|
|
141
|
-
required: "Narrow the batch duplicate query until its deterministic candidate set fits the disclosed safety bound.",
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
matches.push(index);
|
|
145
|
-
indexes[signalKind].set(value, matches);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return candidates;
|
|
150
|
-
}
|
|
151
119
|
/** Internal deterministic seams used to prove bounded batch behavior without building a million-pair fixture. */
|
|
152
120
|
export const _testOnlySimilarity = {
|
|
153
121
|
buildDuplicateClusters,
|
|
@@ -190,6 +158,7 @@ function scoreDuplicateCandidates(items, candidates, threshold, union) {
|
|
|
190
158
|
}
|
|
191
159
|
return matches;
|
|
192
160
|
}
|
|
161
|
+
/** Group unioned endpoints and their scored evidence once, then deterministically rank and limit complete components. */
|
|
193
162
|
function buildDuplicateClusters(items, matches, union, limit) {
|
|
194
163
|
const components = new Map();
|
|
195
164
|
for (const index of items.keys()) {
|
|
@@ -198,12 +167,19 @@ function buildDuplicateClusters(items, matches, union, limit) {
|
|
|
198
167
|
component.push(index);
|
|
199
168
|
components.set(root, component);
|
|
200
169
|
}
|
|
170
|
+
const rootsById = new Map(items.map(({ item }, index) => [item.id, union.findRoot(index)]));
|
|
171
|
+
const matchesByRoot = new Map();
|
|
172
|
+
for (const match of matches) {
|
|
173
|
+
// Scored matches have already joined both endpoints in the union above.
|
|
174
|
+
const root = rootsById.get(match.left_id);
|
|
175
|
+
const rows = matchesByRoot.get(root) ?? [];
|
|
176
|
+
rows.push(match);
|
|
177
|
+
matchesByRoot.set(root, rows);
|
|
178
|
+
}
|
|
201
179
|
return [...components.values()]
|
|
202
180
|
.filter((component) => component.length > 1)
|
|
203
181
|
.map((component) => {
|
|
204
|
-
const
|
|
205
|
-
const clusterMatches = matches
|
|
206
|
-
.filter((match) => ids.has(match.left_id) && ids.has(match.right_id))
|
|
182
|
+
const clusterMatches = (matchesByRoot.get(union.findRoot(component[0])) ?? [])
|
|
207
183
|
.sort((left, right) => right.score - left.score ||
|
|
208
184
|
left.left_id.localeCompare(right.left_id) ||
|
|
209
185
|
left.right_id.localeCompare(right.right_id));
|
|
@@ -272,15 +248,24 @@ export async function findSimilarItems(candidate, options = {}) {
|
|
|
272
248
|
source: indexed ? "persistent_index" : "metadata_fallback",
|
|
273
249
|
};
|
|
274
250
|
}
|
|
275
|
-
/**
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
const {
|
|
282
|
-
const {
|
|
283
|
-
const
|
|
251
|
+
/**
|
|
252
|
+
* Cluster caller-owned metadata using the same scorer and safety bounds as the CLI.
|
|
253
|
+
* Candidate filtering is lossless; exhaustive mode provides an independent pair
|
|
254
|
+
* population for recall checks. Inputs are copied, sorted and never mutated.
|
|
255
|
+
*/
|
|
256
|
+
export function analyzeDuplicateItems(input, options = {}) {
|
|
257
|
+
const { threshold } = validateSimilarityOptions({ ...options, limit: undefined });
|
|
258
|
+
const { limit } = validateDuplicateClusterOptions(options);
|
|
259
|
+
const ids = new Set();
|
|
260
|
+
for (const item of input) {
|
|
261
|
+
if (!item.id.trim() || ids.has(item.id)) {
|
|
262
|
+
throw new PmCliError("Duplicate analysis requires unique non-empty item ids.", EXIT_CODE.USAGE);
|
|
263
|
+
}
|
|
264
|
+
ids.add(item.id);
|
|
265
|
+
}
|
|
266
|
+
const items = [...input].sort((left, right) => left.id.localeCompare(right.id))
|
|
267
|
+
.map((item) => ({ item, prepared: prepareSimilarityText(item.title) }));
|
|
268
|
+
const candidates = collectDuplicateCandidatePairs(items, MAX_DUPLICATE_CANDIDATE_PAIRS, threshold, options.exhaustive === true);
|
|
284
269
|
const union = createDuplicateUnionFind(items.length);
|
|
285
270
|
const matches = scoreDuplicateCandidates(items, candidates, threshold, union);
|
|
286
271
|
const clusters = buildDuplicateClusters(items, matches, union, limit);
|
|
@@ -288,18 +273,28 @@ export async function findDuplicateClusters(options = {}) {
|
|
|
288
273
|
clusters,
|
|
289
274
|
count: clusters.length,
|
|
290
275
|
threshold,
|
|
291
|
-
source: "metadata_scan",
|
|
292
|
-
filters: {
|
|
293
|
-
statuses: statuses ?? null,
|
|
294
|
-
since: options.since ?? null,
|
|
295
|
-
},
|
|
296
276
|
cost: {
|
|
297
277
|
item_count: items.length,
|
|
278
|
+
algorithm: options.exhaustive === true || threshold === 0 ? "exhaustive" : "prefix_exact",
|
|
279
|
+
possible_pairs: items.length * (items.length - 1) / 2,
|
|
280
|
+
pair_limit: MAX_DUPLICATE_CANDIDATE_PAIRS,
|
|
281
|
+
recall_guarantee: "exact",
|
|
298
282
|
candidate_pairs: candidates.size,
|
|
299
283
|
scored_pairs: candidates.size,
|
|
300
284
|
},
|
|
301
285
|
};
|
|
302
286
|
}
|
|
287
|
+
/** Find connected duplicate clusters with one metadata read and shared package analysis. */
|
|
288
|
+
export async function findDuplicateClusters(options = {}) {
|
|
289
|
+
validateSimilarityOptions({ ...options, limit: undefined });
|
|
290
|
+
const { since } = validateDuplicateClusterOptions(options);
|
|
291
|
+
const { items, statuses } = await loadDuplicateItems(options, since);
|
|
292
|
+
return {
|
|
293
|
+
...analyzeDuplicateItems(items, options),
|
|
294
|
+
source: "metadata_scan",
|
|
295
|
+
filters: { statuses: statuses ?? null, since: options.since ?? null },
|
|
296
|
+
};
|
|
297
|
+
}
|
|
303
298
|
/** Apply off/advisory/strict mutation governance through the shared query. */
|
|
304
299
|
export async function evaluateSimilarityGovernance(candidate, options) {
|
|
305
300
|
if (options.mode === "off")
|
|
@@ -329,4 +324,4 @@ export async function evaluateSimilarityGovernance(candidate, options) {
|
|
|
329
324
|
return { mode: options.mode, bypassed, result, recovery };
|
|
330
325
|
}
|
|
331
326
|
//# sourceMappingURL=similarity.js.map
|
|
332
|
-
//# debugId=
|
|
327
|
+
//# debugId=9baf5711-9796-57e8-99e5-8990fd481ef8
|
package/docs/AGENT_GUIDE.md
CHANGED
|
@@ -105,10 +105,22 @@ pm learnings <item-id> --add "Durable lesson for future work."
|
|
|
105
105
|
pm test <item-id> --run --progress
|
|
106
106
|
node scripts/run-tests.mjs coverage
|
|
107
107
|
pm comments <item-id> "Evidence: linked test and coverage passed."
|
|
108
|
-
pm close <item-id> "Acceptance criteria met; verification passed." --validate-close warn
|
|
108
|
+
pm close <item-id> "Acceptance criteria met; verification passed." --resolution "Delivered behavior" --expected "Required outcome" --actual "Verified outcome" --validate-close warn
|
|
109
109
|
pm release <item-id>
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
If a warning close omitted evidence, use its `recovery.suggested_retry_args`
|
|
113
|
+
to append an evidence update while preserving the original close event. See
|
|
114
|
+
[Close Evidence Recovery](CLOSE_EVIDENCE_RECOVERY.md).
|
|
115
|
+
|
|
116
|
+
For real subprocess tests, set `cwd`, `PM_PATH`, and `PM_GLOBAL_PATH` to the
|
|
117
|
+
fixture's temporary workspace and trackers. This also isolates bootstrap and
|
|
118
|
+
implicit discovery reads. Disable external probes with
|
|
119
|
+
`PM_AGENT_PROBES=0` when testing deterministic process output.
|
|
120
|
+
When testing conflicting ambient context, preserve that deliberate input in the
|
|
121
|
+
fixture. CLI and MCP dispatch scope SDK identity signals to the resolved tracker;
|
|
122
|
+
tests compare complete history bytes while the unrelated tracker's claims change.
|
|
123
|
+
|
|
112
124
|
## Token-Minimal Retrieval
|
|
113
125
|
|
|
114
126
|
| Need | Command |
|
|
@@ -181,6 +193,12 @@ Use the canonical [guide topic map](README.md#guide-topic-map) when local in-CLI
|
|
|
181
193
|
|
|
182
194
|
## Reviewed Delivery Closeout
|
|
183
195
|
|
|
196
|
+
Before closing repository defects, set `escape_class` and structured
|
|
197
|
+
`gate_evidence` through `pm update` as described in
|
|
198
|
+
[Defect Evidence](DEFECT_RECURRENCE.md#defect-evidence-on-pm-items).
|
|
199
|
+
Run `pnpm quality:defect-evidence` after the terminal transitions: a static
|
|
200
|
+
gate run before closure cannot validate evidence required only for closed items.
|
|
201
|
+
|
|
184
202
|
Treat PM evidence, item closure, and the generated changelog as part of the
|
|
185
203
|
reviewed change. Add all evidence known before merge to the active branch,
|
|
186
204
|
close and release the item there, regenerate `CHANGELOG.md`, and include those
|
|
@@ -219,7 +237,10 @@ positively and explain false or stale findings on their actual review surface.
|
|
|
219
237
|
After each push, request the reviewers again and wait for terminal results;
|
|
220
238
|
provider quota or skipped reviews are limitations, not approvals. Before merge,
|
|
221
239
|
run `inventory` once more to catch new or edited feedback, and require all
|
|
222
|
-
findings to have an explicit disposition.
|
|
240
|
+
findings to have an explicit disposition. Compare each artifact's `revision`
|
|
241
|
+
and pass it as `--revision` when acknowledging feedback, so edited bodies get
|
|
242
|
+
fresh decisions and retrying a handled revision adds no duplicate reply.
|
|
243
|
+
See [Review Loop](PR_REVIEW_LOOP.md) for the revision and reply contracts.
|
|
223
244
|
|
|
224
245
|
## Documentation Rules for Agents
|
|
225
246
|
|