@productbrain/cli 0.1.0-beta.6724 → 0.1.0-beta.6730
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/surfaces/__tests__/claude-skill-pointer-drift.test.d.ts +2 -0
- package/dist/surfaces/__tests__/claude-skill-pointer-drift.test.d.ts.map +1 -0
- package/dist/surfaces/__tests__/claude-skill-pointer-drift.test.js +180 -0
- package/dist/surfaces/__tests__/claude-skill-pointer-drift.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-skill-pointer-drift.test.d.ts","sourceRoot":"","sources":["../../../src/surfaces/__tests__/claude-skill-pointer-drift.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude skill pointer drift test — WP-761 E4a (TASK-251).
|
|
3
|
+
*
|
|
4
|
+
* PR #1039 review round 2 found three canonical skill bodies wrong or
|
|
5
|
+
* dangerous to invoke (git-cleanup, create-blog, prototype). Round 3 found
|
|
6
|
+
* three more (flag-cleanup, community-post, retro) plus a fourth
|
|
7
|
+
* (triage) already fixed content-wise but never load-bearing for this WP,
|
|
8
|
+
* plus a real gap in this test's own body-match assertion. The mechanism
|
|
9
|
+
* that kept drawing these findings — "expose every canonical skill as a
|
|
10
|
+
* Claude pointer, unaudited" — is cut here, not patched again: **a delivery
|
|
11
|
+
* route ships ONLY the skills WP-761 itself makes load-bearing.**
|
|
12
|
+
*
|
|
13
|
+
* INCLUDED is the whole population. Two skills today — `shaping` (WP-761
|
|
14
|
+
* TASK-245 / E1 edits this file directly) and `defect-driven-review`
|
|
15
|
+
* (WP-761 TASK-242 / E2 edits this file directly) — because a later slice
|
|
16
|
+
* of this same work package writes to them. E0 adds `after-merge` and
|
|
17
|
+
* `handover` once it lands. No other canonical skill gets a pointer from
|
|
18
|
+
* this test's population, regardless of how good its content is — being
|
|
19
|
+
* exposed here is not "this skill happens to be fine," it's "this WP
|
|
20
|
+
* depends on it," and nothing else has been audited well enough yet to
|
|
21
|
+
* make that claim.
|
|
22
|
+
*
|
|
23
|
+
* EXCLUDED_SKILLS is kept as the historical record of skills that were
|
|
24
|
+
* exposed and then found broken (round 2's three) — not consulted by the
|
|
25
|
+
* population logic below (INCLUDED already leaves them out), but still
|
|
26
|
+
* asserted to have no pointer, so a future edit can't silently re-expose
|
|
27
|
+
* a known-broken skill by, say, copy-pasting an old INCLUDED entry.
|
|
28
|
+
*
|
|
29
|
+
* Each pointer's frontmatter `name` and `description`, and its body's
|
|
30
|
+
* target line, are a deliberate denormalized copy of the canonical's
|
|
31
|
+
* (constitution §12):
|
|
32
|
+
*
|
|
33
|
+
* - Source of truth: .productbrain/skills/<name>.md's `name:`/`description:`
|
|
34
|
+
* and its being the file the pointer's body names.
|
|
35
|
+
* - Query need: the loader must see a name and description in the pointer
|
|
36
|
+
* to auto-match a request, and it cannot read a file it has not loaded.
|
|
37
|
+
* - Sync contract: this test. It parses both frontmatter blocks and
|
|
38
|
+
* asserts the name and description strings are equal, and that the
|
|
39
|
+
* pointer body (frontmatter stripped) is EXACTLY the expected one-line
|
|
40
|
+
* target — not merely contains it, so nothing can be prepended,
|
|
41
|
+
* appended, or hidden in frontmatter — in both directions, naming the
|
|
42
|
+
* skill on drift.
|
|
43
|
+
*
|
|
44
|
+
* Never imports the production frontmatter parser (WP-431 S4 precedent, see
|
|
45
|
+
* pb-setup-passthrough.test.ts) — an independent reader means this test
|
|
46
|
+
* doesn't share a blind spot with whatever a future generator produces.
|
|
47
|
+
*/
|
|
48
|
+
import { describe, expect, it } from 'vitest';
|
|
49
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
50
|
+
import { dirname, join } from 'path';
|
|
51
|
+
import { fileURLToPath } from 'url';
|
|
52
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
53
|
+
// repo root: packages/cli/src/surfaces/__tests__ -> ../../../../..
|
|
54
|
+
const REPO_ROOT = join(__dirname, '..', '..', '..', '..', '..');
|
|
55
|
+
const CANONICAL_DIR = join(REPO_ROOT, '.productbrain', 'skills');
|
|
56
|
+
const POINTER_ROOT = join(REPO_ROOT, '.claude', 'skills');
|
|
57
|
+
/**
|
|
58
|
+
* The whole pointer population: name -> which WP-761 slice makes it
|
|
59
|
+
* load-bearing. Add an entry only when a slice of THIS work package edits
|
|
60
|
+
* or depends on the file directly — never for "this skill looks fine."
|
|
61
|
+
*/
|
|
62
|
+
const INCLUDED_SKILLS = {
|
|
63
|
+
shaping: 'WP-761 TASK-245 (E1) edits this file directly — adds a canonical subtraction step and RULE-288/RULE-419 citations.',
|
|
64
|
+
'defect-driven-review': 'WP-761 TASK-242 (E2) edits this file directly — adds Reviewer 2 item 9 and the Artifact clause.',
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Historical record only (PR #1039 review round 2) — these were exposed,
|
|
68
|
+
* found wrong or dangerous to invoke, and removed. Not consulted by
|
|
69
|
+
* INCLUDED_SKILLS above (which already excludes them by omission); asserted
|
|
70
|
+
* below so a future edit can't silently re-expose one.
|
|
71
|
+
*/
|
|
72
|
+
const EXCLUDED_SKILLS = {
|
|
73
|
+
'git-cleanup': 'Step 7 pushes `git push origin main` directly — the forbidden direct-to-main path (worktree contract, .claude/rules/git-workflow.md). https://github.com/ProductBrain/Product-OS/pull/1039#discussion_r4070378871',
|
|
74
|
+
'create-blog': 'Step 3 hardcodes an absolute Mac path ("/Users/randyhereman/Coding/Product-OS/docs/blog/readme.md") for the mandatory voice-guide gate — fails on any other checkout. https://github.com/ProductBrain/Product-OS/pull/1039#discussion_r4070378884',
|
|
75
|
+
prototype: 'The workflow requires prototype/starter-kit(-admin)/index.html, prototype/manifest.json, and `npm run sync:prototype-manifest` — none exist in this repo today (confirmed live: no prototype/ directory at all). https://github.com/ProductBrain/Product-OS/pull/1039#discussion_r4070378892',
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Reads one top-level frontmatter field's value, folding a `>-`/`|-` block
|
|
79
|
+
* scalar's continuation lines the way YAML folds them (newlines become single
|
|
80
|
+
* spaces), or returning a plain scalar's single line as-is.
|
|
81
|
+
*/
|
|
82
|
+
function readFrontmatterField(raw, field) {
|
|
83
|
+
const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
84
|
+
if (!fmMatch)
|
|
85
|
+
return null;
|
|
86
|
+
const lines = fmMatch[1].split('\n');
|
|
87
|
+
const startIdx = lines.findIndex((l) => new RegExp(`^${field}:`).test(l));
|
|
88
|
+
if (startIdx === -1)
|
|
89
|
+
return null;
|
|
90
|
+
let endIdx = lines.length;
|
|
91
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
92
|
+
if (/^[A-Za-z_][A-Za-z0-9_-]*:/.test(lines[i])) {
|
|
93
|
+
endIdx = i;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const block = lines.slice(startIdx, endIdx);
|
|
98
|
+
const firstLineValue = block[0].replace(new RegExp(`^${field}:\\s*`), '');
|
|
99
|
+
const continuation = block
|
|
100
|
+
.slice(1)
|
|
101
|
+
.map((l) => l.replace(/^\s+/, ''))
|
|
102
|
+
.filter((l) => l.length > 0);
|
|
103
|
+
if (firstLineValue && !firstLineValue.startsWith('>') && !firstLineValue.startsWith('|')) {
|
|
104
|
+
return firstLineValue.trim();
|
|
105
|
+
}
|
|
106
|
+
return continuation.join(' ').trim();
|
|
107
|
+
}
|
|
108
|
+
/** Strips the frontmatter block, returning only the body, trimmed. */
|
|
109
|
+
function stripFrontmatter(raw) {
|
|
110
|
+
const fmMatch = raw.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
111
|
+
return (fmMatch ? raw.slice(fmMatch[0].length) : raw).trim();
|
|
112
|
+
}
|
|
113
|
+
describe('Claude skill pointer drift (WP-761 E4a)', () => {
|
|
114
|
+
it('INCLUDED_SKILLS <-> pointer: every listed skill has a pointer with matching name, description, and exact body', () => {
|
|
115
|
+
const names = Object.keys(INCLUDED_SKILLS).sort();
|
|
116
|
+
const mismatches = [];
|
|
117
|
+
const failedNames = new Set();
|
|
118
|
+
for (const name of names) {
|
|
119
|
+
const canonicalPath = join(CANONICAL_DIR, `${name}.md`);
|
|
120
|
+
const pointerPath = join(POINTER_ROOT, name, 'SKILL.md');
|
|
121
|
+
if (!existsSync(canonicalPath)) {
|
|
122
|
+
mismatches.push(`${name}: INCLUDED but no canonical file at .productbrain/skills/${name}.md`);
|
|
123
|
+
failedNames.add(name);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (!existsSync(pointerPath)) {
|
|
127
|
+
mismatches.push(`${name}: INCLUDED but no pointer at .claude/skills/${name}/SKILL.md`);
|
|
128
|
+
failedNames.add(name);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const canonicalRaw = readFileSync(canonicalPath, 'utf8');
|
|
132
|
+
const pointerRaw = readFileSync(pointerPath, 'utf8');
|
|
133
|
+
const canonicalName = readFrontmatterField(canonicalRaw, 'name');
|
|
134
|
+
const pointerName = readFrontmatterField(pointerRaw, 'name');
|
|
135
|
+
if (pointerName !== name) {
|
|
136
|
+
mismatches.push(`${name}: pointer name field is "${pointerName}", expected "${name}"`);
|
|
137
|
+
failedNames.add(name);
|
|
138
|
+
}
|
|
139
|
+
if (canonicalName !== name) {
|
|
140
|
+
mismatches.push(`${name}: canonical name field is "${canonicalName}", expected "${name}"`);
|
|
141
|
+
failedNames.add(name);
|
|
142
|
+
}
|
|
143
|
+
const canonicalDesc = readFrontmatterField(canonicalRaw, 'description');
|
|
144
|
+
const pointerDesc = readFrontmatterField(pointerRaw, 'description');
|
|
145
|
+
if (canonicalDesc !== pointerDesc) {
|
|
146
|
+
mismatches.push(`${name}: description drift — canonical="${canonicalDesc}" pointer="${pointerDesc}"`);
|
|
147
|
+
failedNames.add(name);
|
|
148
|
+
}
|
|
149
|
+
// Exact body match (frontmatter stripped) — not `.includes()`. A
|
|
150
|
+
// pointer that prepends/appends stale or unsafe instructions, or
|
|
151
|
+
// hides the target line inside frontmatter, must go red here.
|
|
152
|
+
const expectedBody = `Read and follow .productbrain/skills/${name}.md`;
|
|
153
|
+
const actualBody = stripFrontmatter(pointerRaw);
|
|
154
|
+
if (actualBody !== expectedBody) {
|
|
155
|
+
mismatches.push(`${name}: pointer body is "${actualBody}", expected exactly "${expectedBody}"`);
|
|
156
|
+
failedNames.add(name);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const matched = names.length - failedNames.size;
|
|
160
|
+
expect(mismatches, `${matched}/${names.length} INCLUDED skills have a fully matching pointer`).toEqual([]);
|
|
161
|
+
expect(names.length).toBeGreaterThan(0);
|
|
162
|
+
});
|
|
163
|
+
it('pointer -> INCLUDED_SKILLS: every pointer directory is a declared INCLUDED entry (no undeclared exposure)', () => {
|
|
164
|
+
const includedNames = new Set(Object.keys(INCLUDED_SKILLS));
|
|
165
|
+
const pointerNames = existsSync(POINTER_ROOT)
|
|
166
|
+
? readdirSync(POINTER_ROOT, { withFileTypes: true })
|
|
167
|
+
.filter((e) => e.isDirectory())
|
|
168
|
+
.map((e) => e.name)
|
|
169
|
+
.sort()
|
|
170
|
+
: [];
|
|
171
|
+
const undeclared = pointerNames.filter((name) => !includedNames.has(name));
|
|
172
|
+
expect(undeclared, `${pointerNames.length - undeclared.length}/${pointerNames.length} pointers are declared in INCLUDED_SKILLS`).toEqual([]);
|
|
173
|
+
expect(pointerNames.length, `pointer count (${pointerNames.length}) vs INCLUDED count (${includedNames.size})`).toBe(includedNames.size);
|
|
174
|
+
});
|
|
175
|
+
it('EXCLUDED_SKILLS stay excluded: no pointer directory exists for any historical-record entry', () => {
|
|
176
|
+
const stillExposed = Object.keys(EXCLUDED_SKILLS).filter((name) => existsSync(join(POINTER_ROOT, name, 'SKILL.md')));
|
|
177
|
+
expect(stillExposed, `${Object.keys(EXCLUDED_SKILLS).length - stillExposed.length}/${Object.keys(EXCLUDED_SKILLS).length} excluded skills correctly have no pointer`).toEqual([]);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
//# sourceMappingURL=claude-skill-pointer-drift.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-skill-pointer-drift.test.js","sourceRoot":"","sources":["../../../src/surfaces/__tests__/claude-skill-pointer-drift.test.ts"],"names":[],"mappings":"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"}
|