@xulthekl/team-flow 0.22.4 → 0.24.0
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/always/phase-guard.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/marketplace.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/plugin/marketplace.json +2 -2
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +103 -0
- package/GEMINI.md +1 -1
- package/HANDOFF.md +123 -98
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/docs/README_en.md +1 -1
- package/docs/solutions/INDEX.md +5 -0
- package/docs/solutions/cross-phase/2026-07-28-no-summary.md +17 -0
- package/gemini-extension.json +1 -1
- package/hooks/pre-tool-use-guard +9 -9
- package/hooks/session-start +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/guard/checks/arch-design.mjs +79 -0
- package/scripts/guard/checks/compound-captured.mjs +70 -0
- package/scripts/guard/guard.mjs +6 -2
- package/scripts/lib/arch-merge.mjs +459 -0
- package/scripts/lib/cmd-state.mjs +5 -0
- package/scripts/lib/hash.mjs +18 -0
- package/scripts/lib/solutions-promote.mjs +1 -1
- package/scripts/lib/state-loader.mjs +16 -0
- package/scripts/team-flow.mjs +3 -0
- package/skills/architecture-design/SKILL.md +42 -3
- package/skills/architecture-design/templates/api.md +71 -0
- package/skills/architecture-design/templates/architecture.md +82 -0
- package/skills/architecture-design/templates/change-brief.md +29 -0
- package/skills/architecture-design/templates/database.md +69 -0
- package/skills/architecture-design/templates/index.md +33 -0
- package/skills/architecture-design/templates/physical-model.md +93 -0
- package/skills/bug-investigator/SKILL.md +1 -1
- package/skills/build-executor/SKILL.md +23 -19
- package/skills/build-executor/implementer-prompt.md +1 -1
- package/skills/build-executor/references/execution-modes.md +6 -6
- package/skills/build-executor/task-reviewer-prompt.md +1 -1
- package/skills/ce-brainstorm/SKILL.md +6 -0
- package/skills/code-reviewer/SKILL.md +6 -2
- package/skills/code-reviewer/code-reviewer-prompt.md +1 -1
- package/skills/contract-builder/SKILL.md +6 -6
- package/skills/need-explorer/SKILL.md +2 -2
- package/skills/release-archivist/SKILL.md +25 -12
- package/skills/release-archivist/references/closing-procedures.md +8 -8
- package/skills/spec-merger/SKILL.md +2 -2
- package/skills/spec-writer/SKILL.md +11 -9
- package/skills/workflow-bootstrap/SKILL.md +39 -5
- package/skills/workflow-bootstrap/references/b1-reconnaissance.md +22 -1
- package/skills/workflow-bootstrap/scripts/recon-probe.sh +122 -1
- package/skills/workflow-orchestrator/references/s2-prd-prototype-loop.md +1 -1
- package/skills/workflow-orchestrator/references/s4-split-validate.md +1 -1
- package/skills/workflow-orchestrator/references/s5-monitoring.md +1 -1
- package/skills/workflow-start/SKILL.md +44 -16
- package/skills/workflow-start/references/routing-rules.md +17 -17
- package/templates/api.md +177 -0
- package/templates/architecture.md +122 -0
- package/templates/change-brief.md +24 -0
- package/templates/database.md +114 -0
- package/tests/lib/guard-compound-captured.test.mjs +92 -0
- package/tests/lib/guard-specs-merged.test.mjs +2 -0
- package/tests/lib/guard-tests-passing.test.mjs +2 -0
- package/tests/lib/guard.test.mjs +2 -0
- package/tests/lib/solutions-capture.test.mjs +108 -0
- package/tests/lib/solutions-index-gen.test.mjs +147 -0
- package/tests/lib/solutions-inject.test.mjs +115 -0
- package/tests/lib/solutions-promote.test.mjs +200 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// tests/lib/solutions-promote.test.mjs
|
|
2
|
+
// Tests for scripts/lib/solutions-promote.mjs — change→product promotion
|
|
3
|
+
|
|
4
|
+
import { describe, it, before, after, beforeEach } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { mkdtempSync, writeFileSync, existsSync, rmSync, mkdirSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { dirname } from 'node:path';
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const ROOT = join(__dirname, '..', '..');
|
|
14
|
+
|
|
15
|
+
// NODE_ENV=test is set below before import — promote's CLI guard skips when test.
|
|
16
|
+
process.env.NODE_ENV = 'test';
|
|
17
|
+
const { run: promoteRun } = await import(join(ROOT, 'scripts', 'lib', 'solutions-promote.mjs'));
|
|
18
|
+
|
|
19
|
+
function makeDir(prefix = '') {
|
|
20
|
+
return mkdtempSync(join(tmpdir(), `tf-sol-promote-${prefix}-`));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function cleanup(dir) {
|
|
24
|
+
if (existsSync(dir)) rmSync(dir, { recursive: true, force: true });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function writeLearnings(dir, content) {
|
|
28
|
+
writeFileSync(join(dir, 'learnings.md'), content, 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const INDEX_HEADER = `# Solutions Index
|
|
32
|
+
| date | phase | domain | type | severity | summary | file |
|
|
33
|
+
|------|-------|--------|------|----------|---------|------|
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
function writeGlobalIndex(solDir, rows = []) {
|
|
37
|
+
mkdirSync(solDir, { recursive: true });
|
|
38
|
+
writeFileSync(join(solDir, 'INDEX.md'), INDEX_HEADER + rows.join('\n') + '\n', 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('solutions-promote', () => {
|
|
42
|
+
let changeDir, solDir;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
changeDir = makeDir('change');
|
|
46
|
+
solDir = makeDir('solutions');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
after(() => {
|
|
50
|
+
cleanup(changeDir);
|
|
51
|
+
cleanup(solDir);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('returns zero when no learnings.md exists', () => {
|
|
55
|
+
const result = promoteRun({ changeDir, dir: solDir });
|
|
56
|
+
assert.equal(result.promoted, 0);
|
|
57
|
+
assert.equal(result.updated, 0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('promotes entry with severity=high and type=pitfall', () => {
|
|
61
|
+
writeLearnings(changeDir, `## Auth token refresh failure
|
|
62
|
+
---
|
|
63
|
+
phase: build
|
|
64
|
+
domain: auth
|
|
65
|
+
type: pitfall
|
|
66
|
+
severity: high
|
|
67
|
+
date: 2026-07-15
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
When OAuth token refresh fails, the system silently swallows the error.
|
|
71
|
+
`);
|
|
72
|
+
const result = promoteRun({ changeDir, dir: solDir });
|
|
73
|
+
assert.equal(result.promoted, 1);
|
|
74
|
+
assert.ok(existsSync(join(solDir, 'build')), 'build phase dir should exist');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('promotes entry with severity=medium and type=pattern', () => {
|
|
78
|
+
writeLearnings(changeDir, `## Retry with backoff pattern
|
|
79
|
+
---
|
|
80
|
+
phase: build
|
|
81
|
+
domain: networking
|
|
82
|
+
type: pattern
|
|
83
|
+
severity: medium
|
|
84
|
+
date: 2026-07-15
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
Use exponential backoff for API retry logic.
|
|
88
|
+
`);
|
|
89
|
+
const result = promoteRun({ changeDir, dir: solDir });
|
|
90
|
+
assert.equal(result.promoted, 1);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('skips entry with severity=low', () => {
|
|
94
|
+
writeLearnings(changeDir, `## Minor formatting issue
|
|
95
|
+
---
|
|
96
|
+
phase: build
|
|
97
|
+
domain: style
|
|
98
|
+
type: pitfall
|
|
99
|
+
severity: low
|
|
100
|
+
date: 2026-07-15
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
Some low severity issue.
|
|
104
|
+
`);
|
|
105
|
+
const result = promoteRun({ changeDir, dir: solDir });
|
|
106
|
+
assert.equal(result.promoted, 0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('skips entry with type=insight (not pitfall/pattern)', () => {
|
|
110
|
+
writeLearnings(changeDir, `## Interesting observation
|
|
111
|
+
---
|
|
112
|
+
phase: build
|
|
113
|
+
domain: auth
|
|
114
|
+
type: insight
|
|
115
|
+
severity: high
|
|
116
|
+
date: 2026-07-15
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
An insight that should not be promoted.
|
|
120
|
+
`);
|
|
121
|
+
const result = promoteRun({ changeDir, dir: solDir });
|
|
122
|
+
assert.equal(result.promoted, 0);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('detects duplicate domain+type and marks as confirmed', () => {
|
|
126
|
+
writeGlobalIndex(solDir, [
|
|
127
|
+
'| 2026-07-01 | build | auth | pitfall | medium | Existing auth pitfall | build/existing.md |',
|
|
128
|
+
]);
|
|
129
|
+
writeLearnings(changeDir, `## Another auth pitfall
|
|
130
|
+
---
|
|
131
|
+
phase: build
|
|
132
|
+
domain: auth
|
|
133
|
+
type: pitfall
|
|
134
|
+
severity: high
|
|
135
|
+
date: 2026-07-15
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
Duplicate domain+type with existing index entry.
|
|
139
|
+
`);
|
|
140
|
+
const result = promoteRun({ changeDir, dir: solDir });
|
|
141
|
+
assert.equal(result.updated, 1, 'should detect duplicate and mark as confirmed');
|
|
142
|
+
assert.equal(result.promoted, 0, 'should not create new file for duplicate');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('promotes multiple entries from single learnings.md', () => {
|
|
146
|
+
writeLearnings(changeDir, `## Issue one
|
|
147
|
+
---
|
|
148
|
+
phase: spec
|
|
149
|
+
domain: api
|
|
150
|
+
type: pitfall
|
|
151
|
+
severity: high
|
|
152
|
+
date: 2026-07-15
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
First pitfall.
|
|
156
|
+
|
|
157
|
+
## Issue two
|
|
158
|
+
---
|
|
159
|
+
phase: spec
|
|
160
|
+
domain: db
|
|
161
|
+
type: pattern
|
|
162
|
+
severity: medium
|
|
163
|
+
date: 2026-07-15
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
Second pattern.
|
|
167
|
+
|
|
168
|
+
## Issue three (low sev, skipped)
|
|
169
|
+
---
|
|
170
|
+
phase: spec
|
|
171
|
+
domain: style
|
|
172
|
+
type: pitfall
|
|
173
|
+
severity: low
|
|
174
|
+
date: 2026-07-15
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
Skipped due to low severity.
|
|
178
|
+
`);
|
|
179
|
+
const result = promoteRun({ changeDir, dir: solDir });
|
|
180
|
+
assert.equal(result.promoted, 2, 'should promote 2 entries (high+medium, skip low)');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('appends promoted entry to global INDEX.md', () => {
|
|
184
|
+
writeGlobalIndex(solDir);
|
|
185
|
+
writeLearnings(changeDir, `## DB migration ordering
|
|
186
|
+
---
|
|
187
|
+
phase: build
|
|
188
|
+
domain: db
|
|
189
|
+
type: pitfall
|
|
190
|
+
severity: high
|
|
191
|
+
date: 2026-07-15
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
Migration files must be ordered by timestamp.
|
|
195
|
+
`);
|
|
196
|
+
promoteRun({ changeDir, dir: solDir });
|
|
197
|
+
const indexContent = readFileSync(join(solDir, 'INDEX.md'), 'utf-8');
|
|
198
|
+
assert.match(indexContent, /DB migration ordering/);
|
|
199
|
+
});
|
|
200
|
+
});
|