@productbrain/cli 0.1.0-beta.107 → 0.1.0-beta.109
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/__tests__/canonicalRefs.vocab.test.d.ts +2 -0
- package/dist/__tests__/canonicalRefs.vocab.test.d.ts.map +1 -0
- package/dist/__tests__/canonicalRefs.vocab.test.js +251 -0
- package/dist/__tests__/canonicalRefs.vocab.test.js.map +1 -0
- package/dist/__tests__/constants.test.js +6 -1
- package/dist/__tests__/constants.test.js.map +1 -1
- package/dist/__tests__/envelope-contract.test.js +29 -3
- package/dist/__tests__/envelope-contract.test.js.map +1 -1
- package/dist/__tests__/errors.test.js +1 -0
- package/dist/__tests__/errors.test.js.map +1 -1
- package/dist/__tests__/handshake-preview.test.js +501 -1
- package/dist/__tests__/handshake-preview.test.js.map +1 -1
- package/dist/__tests__/handshake.e2e.test.d.ts +2 -0
- package/dist/__tests__/handshake.e2e.test.d.ts.map +1 -0
- package/dist/__tests__/handshake.e2e.test.js +257 -0
- package/dist/__tests__/handshake.e2e.test.js.map +1 -0
- package/dist/__tests__/handshake.test.js +251 -1
- package/dist/__tests__/handshake.test.js.map +1 -1
- package/dist/__tests__/manifest.test.js +118 -1
- package/dist/__tests__/manifest.test.js.map +1 -1
- package/dist/__tests__/orient.test.js +65 -24
- package/dist/__tests__/orient.test.js.map +1 -1
- package/dist/__tests__/perimeter.test.d.ts +2 -0
- package/dist/__tests__/perimeter.test.d.ts.map +1 -0
- package/dist/__tests__/perimeter.test.js +143 -0
- package/dist/__tests__/perimeter.test.js.map +1 -0
- package/dist/__tests__/promote.test.js +2 -2
- package/dist/__tests__/promote.test.js.map +1 -1
- package/dist/__tests__/setup-ingest.test.js +16 -0
- package/dist/__tests__/setup-ingest.test.js.map +1 -1
- package/dist/__tests__/skill-vocabulary.test.d.ts +21 -0
- package/dist/__tests__/skill-vocabulary.test.d.ts.map +1 -0
- package/dist/__tests__/skill-vocabulary.test.js +187 -0
- package/dist/__tests__/skill-vocabulary.test.js.map +1 -0
- package/dist/__tests__/update-check.test.d.ts +2 -0
- package/dist/__tests__/update-check.test.d.ts.map +1 -0
- package/dist/__tests__/update-check.test.js +56 -0
- package/dist/__tests__/update-check.test.js.map +1 -0
- package/dist/__tests__/upgrade-runner.test.d.ts +2 -0
- package/dist/__tests__/upgrade-runner.test.d.ts.map +1 -0
- package/dist/__tests__/upgrade-runner.test.js +42 -0
- package/dist/__tests__/upgrade-runner.test.js.map +1 -0
- package/dist/__tests__/vocabulary-leak.test.d.ts +25 -0
- package/dist/__tests__/vocabulary-leak.test.d.ts.map +1 -0
- package/dist/__tests__/vocabulary-leak.test.js +379 -0
- package/dist/__tests__/vocabulary-leak.test.js.map +1 -0
- package/dist/commands/__tests__/connect-handoff.test.d.ts +11 -0
- package/dist/commands/__tests__/connect-handoff.test.d.ts.map +1 -0
- package/dist/commands/__tests__/connect-handoff.test.js +111 -0
- package/dist/commands/__tests__/connect-handoff.test.js.map +1 -0
- package/dist/commands/__tests__/setup-state.test.d.ts +2 -0
- package/dist/commands/__tests__/setup-state.test.d.ts.map +1 -0
- package/dist/commands/__tests__/setup-state.test.js +194 -0
- package/dist/commands/__tests__/setup-state.test.js.map +1 -0
- package/dist/commands/admin/seed.d.ts +32 -2
- package/dist/commands/admin/seed.d.ts.map +1 -1
- package/dist/commands/admin/seed.js +279 -33
- package/dist/commands/admin/seed.js.map +1 -1
- package/dist/commands/admin/seed.test.js +7 -0
- package/dist/commands/admin/seed.test.js.map +1 -1
- package/dist/commands/admin/seedRegistryEntries.generated.d.ts +14 -0
- package/dist/commands/admin/seedRegistryEntries.generated.d.ts.map +1 -0
- package/dist/commands/admin/seedRegistryEntries.generated.js +117 -0
- package/dist/commands/admin/seedRegistryEntries.generated.js.map +1 -0
- package/dist/commands/admin/seedRegistryEntries.test.d.ts +11 -0
- package/dist/commands/admin/seedRegistryEntries.test.d.ts.map +1 -0
- package/dist/commands/admin/seedRegistryEntries.test.js +67 -0
- package/dist/commands/admin/seedRegistryEntries.test.js.map +1 -0
- package/dist/commands/authority-domains.d.ts +7 -1
- package/dist/commands/authority-domains.d.ts.map +1 -1
- package/dist/commands/authority-domains.js +17 -3
- package/dist/commands/authority-domains.js.map +1 -1
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +3 -2
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/codex-prep.js +6 -6
- package/dist/commands/codex-prep.js.map +1 -1
- package/dist/commands/connect-handoff.d.ts +51 -0
- package/dist/commands/connect-handoff.d.ts.map +1 -0
- package/dist/commands/connect-handoff.js +70 -0
- package/dist/commands/connect-handoff.js.map +1 -0
- package/dist/commands/connect-integration.test.js +13 -12
- package/dist/commands/connect-integration.test.js.map +1 -1
- package/dist/commands/connect-screens.d.ts +2 -2
- package/dist/commands/connect-screens.js +2 -2
- package/dist/commands/connect.d.ts +3 -6
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +10 -36
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +67 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/doctor.test.js +131 -0
- package/dist/commands/doctor.test.js.map +1 -1
- package/dist/commands/handshake.d.ts +25 -0
- package/dist/commands/handshake.d.ts.map +1 -1
- package/dist/commands/handshake.js +795 -18
- package/dist/commands/handshake.js.map +1 -1
- package/dist/commands/method.d.ts.map +1 -1
- package/dist/commands/method.js +3 -0
- package/dist/commands/method.js.map +1 -1
- package/dist/commands/orient.d.ts +4 -2
- package/dist/commands/orient.d.ts.map +1 -1
- package/dist/commands/orient.js +16 -2
- package/dist/commands/orient.js.map +1 -1
- package/dist/commands/setup-detect-surfaces.d.ts +38 -0
- package/dist/commands/setup-detect-surfaces.d.ts.map +1 -0
- package/dist/commands/setup-detect-surfaces.js +67 -0
- package/dist/commands/setup-detect-surfaces.js.map +1 -0
- package/dist/commands/setup-ingest.d.ts.map +1 -1
- package/dist/commands/setup-ingest.js +4 -2
- package/dist/commands/setup-ingest.js.map +1 -1
- package/dist/commands/setup-state.d.ts +42 -0
- package/dist/commands/setup-state.d.ts.map +1 -0
- package/dist/commands/setup-state.js +93 -0
- package/dist/commands/setup-state.js.map +1 -0
- package/dist/commands/setup.d.ts +17 -9
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +52 -131
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/upgrade.d.ts +5 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +89 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/formatters/handshake.d.ts +12 -0
- package/dist/formatters/handshake.d.ts.map +1 -1
- package/dist/formatters/handshake.js +32 -0
- package/dist/formatters/handshake.js.map +1 -1
- package/dist/formatters/orient.d.ts +10 -4
- package/dist/formatters/orient.d.ts.map +1 -1
- package/dist/formatters/orient.js +32 -16
- package/dist/formatters/orient.js.map +1 -1
- package/dist/generators/context-md.js +6 -6
- package/dist/generators/context-md.js.map +1 -1
- package/dist/generators/manifest.d.ts +76 -0
- package/dist/generators/manifest.d.ts.map +1 -1
- package/dist/generators/manifest.js +125 -14
- package/dist/generators/manifest.js.map +1 -1
- package/dist/generators/portable-knowledge.d.ts +2 -2
- package/dist/generators/portable-knowledge.d.ts.map +1 -1
- package/dist/generators/surface-profiles.d.ts +1 -2
- package/dist/generators/surface-profiles.d.ts.map +1 -1
- package/dist/generators/surface-profiles.js.map +1 -1
- package/dist/index.js +43 -18
- package/dist/index.js.map +1 -1
- package/dist/lib/activation.js +2 -2
- package/dist/lib/activation.js.map +1 -1
- package/dist/lib/activation.test.js +3 -3
- package/dist/lib/activation.test.js.map +1 -1
- package/dist/lib/canonicalRefs.d.ts +72 -0
- package/dist/lib/canonicalRefs.d.ts.map +1 -1
- package/dist/lib/canonicalRefs.js +67 -0
- package/dist/lib/canonicalRefs.js.map +1 -1
- package/dist/lib/client.d.ts.map +1 -1
- package/dist/lib/client.js +13 -3
- package/dist/lib/client.js.map +1 -1
- package/dist/lib/constants.d.ts +2 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +2 -0
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/errors.d.ts +1 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +1 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/onboarding-shared.js +1 -1
- package/dist/lib/onboarding-shared.js.map +1 -1
- package/dist/lib/update-check.d.ts +20 -0
- package/dist/lib/update-check.d.ts.map +1 -1
- package/dist/lib/update-check.js +122 -21
- package/dist/lib/update-check.js.map +1 -1
- package/dist/lib/upgrade-runner.d.ts +21 -0
- package/dist/lib/upgrade-runner.d.ts.map +1 -0
- package/dist/lib/upgrade-runner.js +109 -0
- package/dist/lib/upgrade-runner.js.map +1 -0
- package/dist/lib/workspaceVocabCache.d.ts +60 -0
- package/dist/lib/workspaceVocabCache.d.ts.map +1 -0
- package/dist/lib/workspaceVocabCache.js +98 -0
- package/dist/lib/workspaceVocabCache.js.map +1 -0
- package/dist/setup/__tests__/coach-traces.test.d.ts +2 -0
- package/dist/setup/__tests__/coach-traces.test.d.ts.map +1 -0
- package/dist/setup/__tests__/coach-traces.test.js +189 -0
- package/dist/setup/__tests__/coach-traces.test.js.map +1 -0
- package/dist/setup/__tests__/setup-commands.test.d.ts +2 -0
- package/dist/setup/__tests__/setup-commands.test.d.ts.map +1 -0
- package/dist/setup/__tests__/setup-commands.test.js +177 -0
- package/dist/setup/__tests__/setup-commands.test.js.map +1 -0
- package/dist/setup/__tests__/state-machine.test.d.ts +2 -0
- package/dist/setup/__tests__/state-machine.test.d.ts.map +1 -0
- package/dist/setup/__tests__/state-machine.test.js +341 -0
- package/dist/setup/__tests__/state-machine.test.js.map +1 -0
- package/dist/setup/detect-surfaces.d.ts +21 -0
- package/dist/setup/detect-surfaces.d.ts.map +1 -0
- package/dist/setup/detect-surfaces.js +39 -0
- package/dist/setup/detect-surfaces.js.map +1 -0
- package/dist/setup/manifest-writer.d.ts +17 -0
- package/dist/setup/manifest-writer.d.ts.map +1 -0
- package/dist/setup/manifest-writer.js +153 -0
- package/dist/setup/manifest-writer.js.map +1 -0
- package/dist/setup/perimeter.d.ts +62 -0
- package/dist/setup/perimeter.d.ts.map +1 -0
- package/dist/setup/perimeter.js +113 -0
- package/dist/setup/perimeter.js.map +1 -0
- package/dist/setup/state-machine.d.ts +67 -0
- package/dist/setup/state-machine.d.ts.map +1 -0
- package/dist/setup/state-machine.js +124 -0
- package/dist/setup/state-machine.js.map +1 -0
- package/dist/surfaces/__tests__/adapter.test.d.ts +2 -0
- package/dist/surfaces/__tests__/adapter.test.d.ts.map +1 -0
- package/dist/surfaces/__tests__/adapter.test.js +90 -0
- package/dist/surfaces/__tests__/adapter.test.js.map +1 -0
- package/dist/surfaces/__tests__/pb-setup-passthrough.test.d.ts +2 -0
- package/dist/surfaces/__tests__/pb-setup-passthrough.test.d.ts.map +1 -0
- package/dist/surfaces/__tests__/pb-setup-passthrough.test.js +132 -0
- package/dist/surfaces/__tests__/pb-setup-passthrough.test.js.map +1 -0
- package/dist/surfaces/__tests__/telemetry.test.d.ts +2 -0
- package/dist/surfaces/__tests__/telemetry.test.d.ts.map +1 -0
- package/dist/surfaces/__tests__/telemetry.test.js +55 -0
- package/dist/surfaces/__tests__/telemetry.test.js.map +1 -0
- package/dist/surfaces/adapter.d.ts +70 -0
- package/dist/surfaces/adapter.d.ts.map +1 -0
- package/dist/surfaces/adapter.js +2 -0
- package/dist/surfaces/adapter.js.map +1 -0
- package/dist/surfaces/adapters/claude.d.ts +3 -0
- package/dist/surfaces/adapters/claude.d.ts.map +1 -0
- package/dist/surfaces/adapters/claude.js +67 -0
- package/dist/surfaces/adapters/claude.js.map +1 -0
- package/dist/surfaces/adapters/codex.d.ts +3 -0
- package/dist/surfaces/adapters/codex.d.ts.map +1 -0
- package/dist/surfaces/adapters/codex.js +61 -0
- package/dist/surfaces/adapters/codex.js.map +1 -0
- package/dist/surfaces/adapters/copilot.d.ts +3 -0
- package/dist/surfaces/adapters/copilot.d.ts.map +1 -0
- package/dist/surfaces/adapters/copilot.js +59 -0
- package/dist/surfaces/adapters/copilot.js.map +1 -0
- package/dist/surfaces/adapters/cursor.d.ts +3 -0
- package/dist/surfaces/adapters/cursor.d.ts.map +1 -0
- package/dist/surfaces/adapters/cursor.js +78 -0
- package/dist/surfaces/adapters/cursor.js.map +1 -0
- package/dist/surfaces/registry.d.ts +58 -2
- package/dist/surfaces/registry.d.ts.map +1 -1
- package/dist/surfaces/registry.js +82 -7
- package/dist/surfaces/registry.js.map +1 -1
- package/dist/surfaces/telemetry.d.ts +17 -0
- package/dist/surfaces/telemetry.d.ts.map +1 -0
- package/dist/surfaces/telemetry.js +31 -0
- package/dist/surfaces/telemetry.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vocabulary leak test — WP-436 S2 (PAT-229, DEC-1021).
|
|
3
|
+
*
|
|
4
|
+
* Asserts that ZERO literal `bet`/`BET` appears in stdout of orient/get/capture/search
|
|
5
|
+
* CLI formatters when the workspace vocabulary is overridden to non-bet labels.
|
|
6
|
+
*
|
|
7
|
+
* Design (from phasing):
|
|
8
|
+
* 3 fixture workspaces × 4 formatter surfaces = 12 scenarios minimum.
|
|
9
|
+
* Each workspace sets `work_package` labels to something non-bet.
|
|
10
|
+
* If any "bet"/"BET" appears in formatted output (outside stored entry IDs), it's a leak.
|
|
11
|
+
*
|
|
12
|
+
* COMMAND-LEVEL layer (Gap 2 fix): 3 fixture workspaces × runOrient = 3 additional
|
|
13
|
+
* scenarios that prove the CLI command SUPPLIES vocabCtx to formatOrient/formatOrientBrief,
|
|
14
|
+
* not just that the formatters work when given ctx directly.
|
|
15
|
+
*
|
|
16
|
+
* Whitelist: stored entry IDs matching /\b[A-Z]+-\d+\b/ (e.g. WP-436, BET-XXX) are
|
|
17
|
+
* excluded from the leak check — they're stable database references, not user-facing labels.
|
|
18
|
+
*
|
|
19
|
+
* Exclusion: `pb audit` is excluded per R7 (WP-313 owns the audit surface).
|
|
20
|
+
*
|
|
21
|
+
* Perf: fixture workspaces are seeded purely in-memory (no Convex, no HTTP).
|
|
22
|
+
* BET-241: no fixture has >100 entries.
|
|
23
|
+
*/
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=vocabulary-leak.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vocabulary-leak.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/vocabulary-leak.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vocabulary leak test — WP-436 S2 (PAT-229, DEC-1021).
|
|
3
|
+
*
|
|
4
|
+
* Asserts that ZERO literal `bet`/`BET` appears in stdout of orient/get/capture/search
|
|
5
|
+
* CLI formatters when the workspace vocabulary is overridden to non-bet labels.
|
|
6
|
+
*
|
|
7
|
+
* Design (from phasing):
|
|
8
|
+
* 3 fixture workspaces × 4 formatter surfaces = 12 scenarios minimum.
|
|
9
|
+
* Each workspace sets `work_package` labels to something non-bet.
|
|
10
|
+
* If any "bet"/"BET" appears in formatted output (outside stored entry IDs), it's a leak.
|
|
11
|
+
*
|
|
12
|
+
* COMMAND-LEVEL layer (Gap 2 fix): 3 fixture workspaces × runOrient = 3 additional
|
|
13
|
+
* scenarios that prove the CLI command SUPPLIES vocabCtx to formatOrient/formatOrientBrief,
|
|
14
|
+
* not just that the formatters work when given ctx directly.
|
|
15
|
+
*
|
|
16
|
+
* Whitelist: stored entry IDs matching /\b[A-Z]+-\d+\b/ (e.g. WP-436, BET-XXX) are
|
|
17
|
+
* excluded from the leak check — they're stable database references, not user-facing labels.
|
|
18
|
+
*
|
|
19
|
+
* Exclusion: `pb audit` is excluded per R7 (WP-313 owns the audit surface).
|
|
20
|
+
*
|
|
21
|
+
* Perf: fixture workspaces are seeded purely in-memory (no Convex, no HTTP).
|
|
22
|
+
* BET-241: no fixture has >100 entries.
|
|
23
|
+
*/
|
|
24
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
25
|
+
import { formatOrient, formatOrientBrief } from '../formatters/orient.js';
|
|
26
|
+
import { formatSearchResults } from '../formatters/search.js';
|
|
27
|
+
import { formatCaptureReceipt } from '../formatters/capture.js';
|
|
28
|
+
import { clearVocabCache } from '../lib/workspaceVocabCache.js';
|
|
29
|
+
// ─── Fixture workspace vocabulary contexts ────────────────────────────────────
|
|
30
|
+
/**
|
|
31
|
+
* Fixture A — Generic Item workspace.
|
|
32
|
+
* work_package.singular = "Item", plural = "Items".
|
|
33
|
+
* Any "bet"/"BET" in output (outside stored IDs) is a leak.
|
|
34
|
+
*/
|
|
35
|
+
const GENERIC_ITEM_CTX = {
|
|
36
|
+
collectionLabels: {
|
|
37
|
+
work_package: {
|
|
38
|
+
singular: 'Item',
|
|
39
|
+
plural: 'Items',
|
|
40
|
+
verb_start: 'create',
|
|
41
|
+
verb_commit: 'commit',
|
|
42
|
+
verb_complete: 'archive',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Fixture B — Linear Project workspace.
|
|
48
|
+
* work_package.singular = "Project", plural = "Projects".
|
|
49
|
+
*/
|
|
50
|
+
const LINEAR_PROJECT_CTX = {
|
|
51
|
+
collectionLabels: {
|
|
52
|
+
work_package: {
|
|
53
|
+
singular: 'Project',
|
|
54
|
+
plural: 'Projects',
|
|
55
|
+
verb_start: 'start',
|
|
56
|
+
verb_commit: 'commit',
|
|
57
|
+
verb_complete: 'complete',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Fixture C — Initiative workspace.
|
|
63
|
+
* work_package.singular = "Initiative", plural = "Initiatives".
|
|
64
|
+
*/
|
|
65
|
+
const INITIATIVE_CTX = {
|
|
66
|
+
collectionLabels: {
|
|
67
|
+
work_package: {
|
|
68
|
+
singular: 'Initiative',
|
|
69
|
+
plural: 'Initiatives',
|
|
70
|
+
verb_start: 'launch',
|
|
71
|
+
verb_commit: 'commit',
|
|
72
|
+
verb_complete: 'close',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Fixture D — Defaults-only workspace (Layer 2 regression guard).
|
|
78
|
+
* NO collectionLabels (no workspace override). Only collectionDefaults
|
|
79
|
+
* populated — mirrors what a real workspace looks like AFTER the
|
|
80
|
+
* seedCollectionLabels migration but BEFORE any methodology preset.
|
|
81
|
+
*
|
|
82
|
+
* INS-1446: this fixture catches the class of regression where consumers
|
|
83
|
+
* drop collectionDefaults from the vocab response and Layer 2 goes dead.
|
|
84
|
+
*/
|
|
85
|
+
const DEFAULTS_ONLY_CTX = {
|
|
86
|
+
collectionDefaults: {
|
|
87
|
+
work_package: {
|
|
88
|
+
singular: 'Bet',
|
|
89
|
+
plural: 'Bets',
|
|
90
|
+
verb_start: 'shape',
|
|
91
|
+
verb_commit: 'commit',
|
|
92
|
+
verb_complete: 'ship',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* skipLeakCheck: true — fixture labels themselves contain "bet" (correct resolved
|
|
98
|
+
* output), so assertNoBetLeak would false-positive. These fixtures still exercise
|
|
99
|
+
* the positive label resolution check; leak tests are covered by the separate
|
|
100
|
+
* "defaults-only" command-level tests below.
|
|
101
|
+
*/
|
|
102
|
+
const ALL_FIXTURES = [
|
|
103
|
+
{ name: 'Generic Item', ctx: GENERIC_ITEM_CTX },
|
|
104
|
+
{ name: 'Linear Project', ctx: LINEAR_PROJECT_CTX },
|
|
105
|
+
{ name: 'Initiative', ctx: INITIATIVE_CTX },
|
|
106
|
+
{ name: 'Defaults-only (Bet)', ctx: DEFAULTS_ONLY_CTX, skipLeakCheck: true },
|
|
107
|
+
];
|
|
108
|
+
// ─── Shared test fixtures ─────────────────────────────────────────────────────
|
|
109
|
+
/** Typical orient view with some active work packages. Perf: <100 entries (BET-241). */
|
|
110
|
+
const ORIENT_VIEW = {
|
|
111
|
+
stage: 'grounded',
|
|
112
|
+
strategicContext: {
|
|
113
|
+
vision: 'Build the best product knowledge system',
|
|
114
|
+
purpose: 'Make knowledge work for everyone',
|
|
115
|
+
activeBetCount: 2,
|
|
116
|
+
currentWorkPackage: 'WP-100',
|
|
117
|
+
activeTensionCount: 1,
|
|
118
|
+
productAreas: ['Growth'],
|
|
119
|
+
productAreaCount: 1,
|
|
120
|
+
},
|
|
121
|
+
activeWorkPackages: [
|
|
122
|
+
{ entryId: 'WP-100', name: 'Ship vocabulary foundation', collectionSlug: 'work-packages', status: 'active' },
|
|
123
|
+
{ entryId: 'WP-101', name: 'Build orient view', collectionSlug: 'work-packages', status: 'active' },
|
|
124
|
+
],
|
|
125
|
+
governance: {
|
|
126
|
+
principles: [],
|
|
127
|
+
standards: [],
|
|
128
|
+
businessRules: [],
|
|
129
|
+
},
|
|
130
|
+
tensions: [],
|
|
131
|
+
architectureNotes: [],
|
|
132
|
+
proposals: [],
|
|
133
|
+
absenceSignals: [],
|
|
134
|
+
plannedWork: {
|
|
135
|
+
uncommittedDrafts: [],
|
|
136
|
+
inProgressEntries: [],
|
|
137
|
+
openTensions: [],
|
|
138
|
+
},
|
|
139
|
+
priorSessions: [],
|
|
140
|
+
recoveryBlock: null,
|
|
141
|
+
};
|
|
142
|
+
// ─── Leak detection utility ───────────────────────────────────────────────────
|
|
143
|
+
/**
|
|
144
|
+
* Strip stored entry IDs from output before checking for leaks.
|
|
145
|
+
* Whitelist: /\b[A-Z]+-\d+\b/ (e.g. WP-100, BET-42, DEC-1021).
|
|
146
|
+
* These are stable DB references, not vocabulary labels.
|
|
147
|
+
*/
|
|
148
|
+
function stripStoredIds(output) {
|
|
149
|
+
return output.replace(/\b[A-Z]+-\d+\b/g, '<<ENTRYID>>');
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Assert no literal bet/BET in output (outside stored entry IDs).
|
|
153
|
+
* We check both lowercase `bet` and uppercase `BET` as word-boundary matches.
|
|
154
|
+
*/
|
|
155
|
+
function assertNoBetLeak(output, fixture, surface) {
|
|
156
|
+
const stripped = stripStoredIds(output);
|
|
157
|
+
const hasBetLeak = /\bbet\b/i.test(stripped);
|
|
158
|
+
if (hasBetLeak) {
|
|
159
|
+
// Find the first match for a helpful error message
|
|
160
|
+
const match = stripped.match(/\bbet\b/i);
|
|
161
|
+
const idx = match?.index ?? 0;
|
|
162
|
+
const context = stripped.slice(Math.max(0, idx - 30), idx + 50);
|
|
163
|
+
throw new Error(`[vocabulary-leak] Literal "bet" found in "${fixture}" workspace, "${surface}" surface.\n` +
|
|
164
|
+
`Context: ...${context}...\n` +
|
|
165
|
+
`Full output (stripped IDs):\n${stripped}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ─── Captured search result fixture ──────────────────────────────────────────
|
|
169
|
+
/** Minimal search result that matches the formatSearchResults expected shape. */
|
|
170
|
+
const SEARCH_RESULTS = [
|
|
171
|
+
{ entryId: 'WP-100', name: 'Ship vocabulary foundation', collectionSlug: 'work-packages', status: 'active' },
|
|
172
|
+
{ entryId: 'DEC-42', name: 'Resolver architecture decision', collectionSlug: 'decisions', status: 'published' },
|
|
173
|
+
];
|
|
174
|
+
// ─── Capture receipt fixture ──────────────────────────────────────────────────
|
|
175
|
+
/** Minimal capture receipt that exercises the formatter. */
|
|
176
|
+
const CAPTURE_RECEIPT = {
|
|
177
|
+
entryId: 'WP-200',
|
|
178
|
+
status: 'draft',
|
|
179
|
+
collection: 'work-packages',
|
|
180
|
+
name: 'New vocabulary test entry',
|
|
181
|
+
classification: {
|
|
182
|
+
collection: 'work-packages',
|
|
183
|
+
confidence: 0.9,
|
|
184
|
+
tier: 'high',
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
// ─── Test suites ──────────────────────────────────────────────────────────────
|
|
188
|
+
describe('vocabulary-leak: orient formatters', () => {
|
|
189
|
+
for (const { name, ctx, skipLeakCheck } of ALL_FIXTURES) {
|
|
190
|
+
it(`${name}: formatOrient contains no literal "bet" outside stored IDs`, () => {
|
|
191
|
+
const output = formatOrient(ORIENT_VIEW, ctx);
|
|
192
|
+
if (!skipLeakCheck)
|
|
193
|
+
assertNoBetLeak(output, name, 'pb orient (full)');
|
|
194
|
+
// Also verify the resolved label appears (positive check)
|
|
195
|
+
const expectedLabel = ctx.collectionLabels?.work_package?.plural ?? ctx.collectionDefaults?.work_package?.plural ?? 'work_package';
|
|
196
|
+
expect(output).toContain(expectedLabel);
|
|
197
|
+
});
|
|
198
|
+
it(`${name}: formatOrientBrief contains no literal "bet" outside stored IDs`, () => {
|
|
199
|
+
const output = formatOrientBrief(ORIENT_VIEW, ctx);
|
|
200
|
+
if (!skipLeakCheck)
|
|
201
|
+
assertNoBetLeak(output, name, 'pb orient --brief');
|
|
202
|
+
// Also verify the resolved label appears in the count line
|
|
203
|
+
const expectedLabel = ctx.collectionLabels?.work_package?.plural ?? ctx.collectionDefaults?.work_package?.plural ?? 'work_package';
|
|
204
|
+
expect(output).toContain(expectedLabel);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
describe('vocabulary-leak: search formatter', () => {
|
|
209
|
+
for (const { name, skipLeakCheck } of ALL_FIXTURES) {
|
|
210
|
+
const titleSuffix = skipLeakCheck
|
|
211
|
+
? '[skipLeakCheck — label is "Bet"; surface does not render vocab labels anyway]'
|
|
212
|
+
: 'contains no literal "bet" outside stored IDs';
|
|
213
|
+
it(`${name}: formatSearchResults ${titleSuffix}`, () => {
|
|
214
|
+
// formatSearchResults renders entry names and slugs — none should contain "bet"
|
|
215
|
+
// since our fixtures use work-packages (not bets) as the slug.
|
|
216
|
+
const output = formatSearchResults(SEARCH_RESULTS);
|
|
217
|
+
if (!skipLeakCheck)
|
|
218
|
+
assertNoBetLeak(output, name, 'pb search');
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
describe('vocabulary-leak: capture formatter', () => {
|
|
223
|
+
for (const { name, skipLeakCheck } of ALL_FIXTURES) {
|
|
224
|
+
const titleSuffix = skipLeakCheck
|
|
225
|
+
? '[skipLeakCheck — label is "Bet"; surface does not render vocab labels anyway]'
|
|
226
|
+
: 'contains no literal "bet" outside stored IDs';
|
|
227
|
+
it(`${name}: formatCaptureReceipt ${titleSuffix}`, () => {
|
|
228
|
+
const output = formatCaptureReceipt(CAPTURE_RECEIPT);
|
|
229
|
+
if (!skipLeakCheck)
|
|
230
|
+
assertNoBetLeak(output, name, 'pb capture');
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
// NOTE: `pb audit` is excluded per R7 (WP-313 owns the audit surface rename).
|
|
235
|
+
// ─── Command-level leak tests (Gap 2) ────────────────────────────────────────
|
|
236
|
+
//
|
|
237
|
+
// These tests invoke `runOrient` from the actual CLI command module to prove
|
|
238
|
+
// the command SUPPLIES vocabCtx to formatOrient/formatOrientBrief — not just
|
|
239
|
+
// that the formatters work when given ctx directly.
|
|
240
|
+
//
|
|
241
|
+
// Mocking strategy (mirrors orient.test.ts):
|
|
242
|
+
// - kernelCall is mocked at module level
|
|
243
|
+
// - getConfigOrGuide returns { apiKey: 'pb_sk_test', siteUrl: 'https://test.convex.site' }
|
|
244
|
+
// - runCliCommand is mocked to call fn() + formatPretty() and write to stdout
|
|
245
|
+
// - chain.getVocabulary returns the fixture vocabCtx
|
|
246
|
+
// - chain.getOrientView returns ORIENT_VIEW
|
|
247
|
+
//
|
|
248
|
+
// The vocab cache is cleared before each test to ensure fresh fetches.
|
|
249
|
+
const kernelCallMock = vi.fn();
|
|
250
|
+
vi.mock('../lib/client.js', () => ({
|
|
251
|
+
kernelCall: (...args) => kernelCallMock(...args),
|
|
252
|
+
}));
|
|
253
|
+
vi.mock('../lib/config.js', () => ({
|
|
254
|
+
getConfig: vi.fn(() => ({ apiKey: 'pb_sk_test', siteUrl: 'https://test.convex.site' })),
|
|
255
|
+
getConfigOrGuide: vi.fn(() => Promise.resolve({ apiKey: 'pb_sk_test', siteUrl: 'https://test.convex.site' })),
|
|
256
|
+
}));
|
|
257
|
+
vi.mock('../lib/runner.js', () => ({
|
|
258
|
+
runCliCommand: vi.fn(async ({ fn, formatPretty }) => {
|
|
259
|
+
const data = await fn();
|
|
260
|
+
process.stdout.write(formatPretty(data) + '\n');
|
|
261
|
+
}),
|
|
262
|
+
isJsonMode: vi.fn(() => false),
|
|
263
|
+
}));
|
|
264
|
+
vi.mock('../lib/strip.js', async (importOriginal) => {
|
|
265
|
+
const actual = await importOriginal();
|
|
266
|
+
return {
|
|
267
|
+
...actual,
|
|
268
|
+
stripConvexInternals: (v) => v,
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
// Install a no-op global fetch so fire-and-forget byte reports don't fail.
|
|
272
|
+
function installFetchMock() {
|
|
273
|
+
globalThis.fetch = vi.fn().mockResolvedValue(new Response(null, { status: 200 }));
|
|
274
|
+
}
|
|
275
|
+
describe('vocabulary-leak: runOrient command (end-to-end vocab wiring)', () => {
|
|
276
|
+
beforeEach(() => {
|
|
277
|
+
vi.clearAllMocks();
|
|
278
|
+
// Clear vocab cache so each test fetches fresh via chain.getVocabulary mock.
|
|
279
|
+
clearVocabCache();
|
|
280
|
+
installFetchMock();
|
|
281
|
+
});
|
|
282
|
+
for (const { name, ctx, skipLeakCheck } of ALL_FIXTURES) {
|
|
283
|
+
it(`${name}: runOrient stdout contains no literal "bet" outside stored IDs`, async () => {
|
|
284
|
+
// Wire kernelCallMock: differentiate chain.getVocabulary from chain.getOrientView.
|
|
285
|
+
kernelCallMock.mockImplementation((fn) => {
|
|
286
|
+
if (fn === 'chain.getVocabulary') {
|
|
287
|
+
return Promise.resolve({
|
|
288
|
+
collectionLabels: ctx.collectionLabels,
|
|
289
|
+
collectionDefaults: ctx.collectionDefaults,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// Default: return the orient view for chain.getOrientView
|
|
293
|
+
return Promise.resolve(ORIENT_VIEW);
|
|
294
|
+
});
|
|
295
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
296
|
+
const { runOrient } = await import('../commands/orient.js');
|
|
297
|
+
await runOrient();
|
|
298
|
+
const output = writeSpy.mock.calls.map((call) => String(call[0])).join('');
|
|
299
|
+
writeSpy.mockRestore();
|
|
300
|
+
if (!skipLeakCheck)
|
|
301
|
+
assertNoBetLeak(output, name, 'pb orient (command)');
|
|
302
|
+
// Positive check: resolved label should appear in output.
|
|
303
|
+
const expectedLabel = ctx.collectionLabels?.work_package?.plural ?? ctx.collectionDefaults?.work_package?.plural ?? 'work_package';
|
|
304
|
+
expect(output).toContain(expectedLabel);
|
|
305
|
+
});
|
|
306
|
+
it(`${name}: runOrient --brief stdout contains no literal "bet" outside stored IDs`, async () => {
|
|
307
|
+
kernelCallMock.mockImplementation((fn) => {
|
|
308
|
+
if (fn === 'chain.getVocabulary') {
|
|
309
|
+
return Promise.resolve({
|
|
310
|
+
collectionLabels: ctx.collectionLabels,
|
|
311
|
+
collectionDefaults: ctx.collectionDefaults,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
return Promise.resolve(ORIENT_VIEW);
|
|
315
|
+
});
|
|
316
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
317
|
+
const { runOrient } = await import('../commands/orient.js');
|
|
318
|
+
await runOrient({ brief: true });
|
|
319
|
+
const output = writeSpy.mock.calls.map((call) => String(call[0])).join('');
|
|
320
|
+
writeSpy.mockRestore();
|
|
321
|
+
if (!skipLeakCheck)
|
|
322
|
+
assertNoBetLeak(output, name, 'pb orient --brief (command)');
|
|
323
|
+
// Positive check: resolved label should appear in the count line.
|
|
324
|
+
const expectedLabel = ctx.collectionLabels?.work_package?.plural ?? ctx.collectionDefaults?.work_package?.plural ?? 'work_package';
|
|
325
|
+
expect(output).toContain(expectedLabel);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
it('fallback: runOrient uses work_package literal when both collectionLabels AND collectionDefaults are null', async () => {
|
|
329
|
+
kernelCallMock.mockImplementation((fn) => {
|
|
330
|
+
if (fn === 'chain.getVocabulary') {
|
|
331
|
+
return Promise.resolve({ collectionLabels: null, collectionDefaults: null });
|
|
332
|
+
}
|
|
333
|
+
return Promise.resolve(ORIENT_VIEW);
|
|
334
|
+
});
|
|
335
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
336
|
+
const { runOrient } = await import('../commands/orient.js');
|
|
337
|
+
await runOrient({ brief: true });
|
|
338
|
+
const output = writeSpy.mock.calls.map((call) => String(call[0])).join('');
|
|
339
|
+
writeSpy.mockRestore();
|
|
340
|
+
// Layer 4 fallback: resolver emits the canonicalKey literal 'work_package'
|
|
341
|
+
expect(output).toContain('work_package');
|
|
342
|
+
});
|
|
343
|
+
it('defaults-only: runOrient resolves from collectionDefaults when collectionLabels is null (INS-1446 regression guard)', async () => {
|
|
344
|
+
kernelCallMock.mockImplementation((fn) => {
|
|
345
|
+
if (fn === 'chain.getVocabulary') {
|
|
346
|
+
return Promise.resolve({
|
|
347
|
+
collectionLabels: null,
|
|
348
|
+
collectionDefaults: { work_package: { singular: 'Bet', plural: 'Bets' } },
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
return Promise.resolve(ORIENT_VIEW);
|
|
352
|
+
});
|
|
353
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
354
|
+
const { runOrient } = await import('../commands/orient.js');
|
|
355
|
+
await runOrient({ brief: true });
|
|
356
|
+
const output = writeSpy.mock.calls.map((call) => String(call[0])).join('');
|
|
357
|
+
writeSpy.mockRestore();
|
|
358
|
+
// Layer 2 defaults: resolver uses collectionDefaults, NOT the literal
|
|
359
|
+
expect(output).toContain('Bets');
|
|
360
|
+
expect(output).not.toContain('work_package');
|
|
361
|
+
});
|
|
362
|
+
it('fail-open: runOrient still renders when chain.getVocabulary throws', async () => {
|
|
363
|
+
kernelCallMock.mockImplementation((fn) => {
|
|
364
|
+
if (fn === 'chain.getVocabulary') {
|
|
365
|
+
return Promise.reject(new Error('simulated 404'));
|
|
366
|
+
}
|
|
367
|
+
return Promise.resolve(ORIENT_VIEW);
|
|
368
|
+
});
|
|
369
|
+
const writeSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true);
|
|
370
|
+
const { runOrient } = await import('../commands/orient.js');
|
|
371
|
+
await expect(runOrient({ brief: true })).resolves.toBeUndefined();
|
|
372
|
+
const output = writeSpy.mock.calls.map((call) => String(call[0])).join('');
|
|
373
|
+
writeSpy.mockRestore();
|
|
374
|
+
// Output should still render (fallback to canonicalKey literal)
|
|
375
|
+
expect(output.length).toBeGreaterThan(0);
|
|
376
|
+
expect(output).toContain('work_package');
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
//# sourceMappingURL=vocabulary-leak.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vocabulary-leak.test.js","sourceRoot":"","sources":["../../src/__tests__/vocabulary-leak.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WP-433 S4 — FEAT-1291: Tests for connect-handoff module.
|
|
3
|
+
*
|
|
4
|
+
* Contract:
|
|
5
|
+
* 1. Heuristic table: 5 cases, each asserts exact agent + message strings.
|
|
6
|
+
* 2. ≤200-char invariant: all 5 messages must be ≤200 characters.
|
|
7
|
+
* 3. No bare-repo undefined: bare-repo result is never undefined/empty.
|
|
8
|
+
* 4. detectHandoffMarkers integration: tmpdir with .claude/ → hasClaude=true.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=connect-handoff.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-handoff.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/connect-handoff.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WP-433 S4 — FEAT-1291: Tests for connect-handoff module.
|
|
3
|
+
*
|
|
4
|
+
* Contract:
|
|
5
|
+
* 1. Heuristic table: 5 cases, each asserts exact agent + message strings.
|
|
6
|
+
* 2. ≤200-char invariant: all 5 messages must be ≤200 characters.
|
|
7
|
+
* 3. No bare-repo undefined: bare-repo result is never undefined/empty.
|
|
8
|
+
* 4. detectHandoffMarkers integration: tmpdir with .claude/ → hasClaude=true.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdirSync, rmSync, writeFileSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { tmpdir } from 'os';
|
|
13
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
14
|
+
import { resolveHandoff, detectHandoffMarkers, HANDOFF_MESSAGE_CLAUDE_OR_CURSOR, HANDOFF_MESSAGE_CLAUDE, HANDOFF_MESSAGE_CURSOR, HANDOFF_MESSAGE_CODEX, HANDOFF_MESSAGE_BARE_REPO, } from '../connect-handoff.js';
|
|
15
|
+
// ── 1. Heuristic table (5 cases) ─────────────────────────────────────────────
|
|
16
|
+
describe('resolveHandoff — heuristic table', () => {
|
|
17
|
+
it('.claude=Y, .cursor=Y → claude-or-cursor', () => {
|
|
18
|
+
const result = resolveHandoff({ hasClaude: true, hasCursor: true, hasCodexFile: false });
|
|
19
|
+
expect(result.agent).toBe('claude-or-cursor');
|
|
20
|
+
expect(result.message).toBe('Open Claude Code or Cursor in this project and type in chat: Start Product Brain');
|
|
21
|
+
});
|
|
22
|
+
it('.claude=Y, .cursor=N → claude', () => {
|
|
23
|
+
const result = resolveHandoff({ hasClaude: true, hasCursor: false, hasCodexFile: false });
|
|
24
|
+
expect(result.agent).toBe('claude');
|
|
25
|
+
expect(result.message).toBe('Open Claude Code in this project and type in chat: Start Product Brain');
|
|
26
|
+
});
|
|
27
|
+
it('.claude=N, .cursor=Y → cursor', () => {
|
|
28
|
+
const result = resolveHandoff({ hasClaude: false, hasCursor: true, hasCodexFile: false });
|
|
29
|
+
expect(result.agent).toBe('cursor');
|
|
30
|
+
expect(result.message).toBe('Open Cursor in this project and type in chat: Start Product Brain');
|
|
31
|
+
});
|
|
32
|
+
it('.claude=N, .cursor=N, AGENTS.md=Y → codex', () => {
|
|
33
|
+
const result = resolveHandoff({ hasClaude: false, hasCursor: false, hasCodexFile: true });
|
|
34
|
+
expect(result.agent).toBe('codex');
|
|
35
|
+
expect(result.message).toBe('Open Codex CLI in this project and type in chat: Start Product Brain');
|
|
36
|
+
});
|
|
37
|
+
it('.claude=N, .cursor=N, AGENTS.md=N → bare-repo', () => {
|
|
38
|
+
const result = resolveHandoff({ hasClaude: false, hasCursor: false, hasCodexFile: false });
|
|
39
|
+
expect(result.agent).toBe('bare-repo');
|
|
40
|
+
expect(result.message).toBe('No AI tool detected in this project. Visit productbrain.io/docs to set one up.');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
// ── 2. Exhaustiveness: every boolean combination returns a defined HandoffResult ──
|
|
44
|
+
describe('resolveHandoff — every boolean combination returns a defined HandoffResult', () => {
|
|
45
|
+
it('covers all 2^3 = 8 marker combinations', () => {
|
|
46
|
+
const bools = [false, true];
|
|
47
|
+
for (const hasClaude of bools) {
|
|
48
|
+
for (const hasCursor of bools) {
|
|
49
|
+
for (const hasCodexFile of bools) {
|
|
50
|
+
const result = resolveHandoff({ hasClaude, hasCursor, hasCodexFile });
|
|
51
|
+
expect(result.agent).toBeDefined();
|
|
52
|
+
expect(result.message.length).toBeGreaterThan(0);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
// ── 4. ≤200-char invariant ────────────────────────────────────────────────────
|
|
59
|
+
describe('handoff message ≤200-char invariant', () => {
|
|
60
|
+
const allMessages = [
|
|
61
|
+
HANDOFF_MESSAGE_CLAUDE_OR_CURSOR,
|
|
62
|
+
HANDOFF_MESSAGE_CLAUDE,
|
|
63
|
+
HANDOFF_MESSAGE_CURSOR,
|
|
64
|
+
HANDOFF_MESSAGE_CODEX,
|
|
65
|
+
HANDOFF_MESSAGE_BARE_REPO,
|
|
66
|
+
];
|
|
67
|
+
it.each(allMessages)('message is ≤200 chars: "%s"', (msg) => {
|
|
68
|
+
expect(msg.length).toBeLessThanOrEqual(200);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
// ── 5. No bare-repo undefined ─────────────────────────────────────────────────
|
|
72
|
+
describe('bare-repo result is never undefined or empty', () => {
|
|
73
|
+
it('resolveHandoff returns bare-repo with non-empty message when no markers', () => {
|
|
74
|
+
const result = resolveHandoff({ hasClaude: false, hasCursor: false, hasCodexFile: false });
|
|
75
|
+
expect(result.agent).toBe('bare-repo');
|
|
76
|
+
expect(result.message).toBeDefined();
|
|
77
|
+
expect(result.message.length).toBeGreaterThan(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
// ── 6. detectHandoffMarkers integration ──────────────────────────────────────
|
|
81
|
+
describe('detectHandoffMarkers integration', () => {
|
|
82
|
+
let tmp;
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
tmp = join(tmpdir(), `pb-handoff-test-${Date.now()}`);
|
|
85
|
+
mkdirSync(tmp, { recursive: true });
|
|
86
|
+
});
|
|
87
|
+
afterEach(() => {
|
|
88
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
89
|
+
});
|
|
90
|
+
it('returns hasClaude=true when .claude/ dir exists', () => {
|
|
91
|
+
mkdirSync(join(tmp, '.claude'));
|
|
92
|
+
const markers = detectHandoffMarkers(tmp);
|
|
93
|
+
expect(markers).toEqual({ hasClaude: true, hasCursor: false, hasCodexFile: false });
|
|
94
|
+
});
|
|
95
|
+
it('returns hasCursor=true when .cursor/ dir exists', () => {
|
|
96
|
+
mkdirSync(join(tmp, '.cursor'));
|
|
97
|
+
const markers = detectHandoffMarkers(tmp);
|
|
98
|
+
expect(markers).toEqual({ hasClaude: false, hasCursor: true, hasCodexFile: false });
|
|
99
|
+
});
|
|
100
|
+
it('returns hasCodexFile=true when AGENTS.md exists', () => {
|
|
101
|
+
// Write a minimal file so existsSync resolves it as a file (not dir)
|
|
102
|
+
writeFileSync(join(tmp, 'AGENTS.md'), '');
|
|
103
|
+
const markers = detectHandoffMarkers(tmp);
|
|
104
|
+
expect(markers).toEqual({ hasClaude: false, hasCursor: false, hasCodexFile: true });
|
|
105
|
+
});
|
|
106
|
+
it('returns all false when directory is empty', () => {
|
|
107
|
+
const markers = detectHandoffMarkers(tmp);
|
|
108
|
+
expect(markers).toEqual({ hasClaude: false, hasCursor: false, hasCodexFile: false });
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=connect-handoff.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect-handoff.test.js","sourceRoot":"","sources":["../../../src/commands/__tests__/connect-handoff.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-state.test.d.ts","sourceRoot":"","sources":["../../../src/commands/__tests__/setup-state.test.ts"],"names":[],"mappings":""}
|