@wazir-dev/cli 1.3.0 → 1.4.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/CHANGELOG.md +17 -2
- package/docs/research/2026-03-20-agents/a18fb002157904af5.txt +187 -0
- package/docs/research/2026-03-20-agents/a1d0ac79ac2f11e6f.txt +2 -0
- package/docs/research/2026-03-20-agents/a324079de037abd7c.txt +198 -0
- package/docs/research/2026-03-20-agents/a357586bccfafb0e5.txt +256 -0
- package/docs/research/2026-03-20-agents/a4365394e4d753105.txt +137 -0
- package/docs/research/2026-03-20-agents/a492af28bc52d3613.txt +136 -0
- package/docs/research/2026-03-20-agents/a4984db0b6a8eee07.txt +124 -0
- package/docs/research/2026-03-20-agents/a5b30e59d34bbb062.txt +214 -0
- package/docs/research/2026-03-20-agents/a5cf7829dab911586.txt +165 -0
- package/docs/research/2026-03-20-agents/a607157c30dd97c9e.txt +96 -0
- package/docs/research/2026-03-20-agents/a60b68b1e19d1e16b.txt +115 -0
- package/docs/research/2026-03-20-agents/a722af01c5594aba0.txt +166 -0
- package/docs/research/2026-03-20-agents/a787bdc516faa5829.txt +181 -0
- package/docs/research/2026-03-20-agents/a7c46d1bba1056ed2.txt +132 -0
- package/docs/research/2026-03-20-agents/a7e5abbab2b281a0d.txt +100 -0
- package/docs/research/2026-03-20-agents/a8dbadc66cd0d7d5a.txt +95 -0
- package/docs/research/2026-03-20-agents/a904d9f45d6b86a6d.txt +75 -0
- package/docs/research/2026-03-20-agents/a927659a942ee7f60.txt +102 -0
- package/docs/research/2026-03-20-agents/a962cb569191f7583.txt +125 -0
- package/docs/research/2026-03-20-agents/aab6decea538aac41.txt +148 -0
- package/docs/research/2026-03-20-agents/abd58b853dd938a1b.txt +295 -0
- package/docs/research/2026-03-20-agents/ac009da573eff7f65.txt +100 -0
- package/docs/research/2026-03-20-agents/ac1bc783364405e5f.txt +190 -0
- package/docs/research/2026-03-20-agents/aca5e2b57fde152a0.txt +132 -0
- package/docs/research/2026-03-20-agents/ad849b8c0a7e95b8b.txt +176 -0
- package/docs/research/2026-03-20-agents/adc2b12a4da32c962.txt +258 -0
- package/docs/research/2026-03-20-agents/af97caaaa9a80e4cb.txt +146 -0
- package/docs/research/2026-03-20-agents/afc5faceee368b3ca.txt +111 -0
- package/docs/research/2026-03-20-agents/afdb282d866e3c1e4.txt +164 -0
- package/docs/research/2026-03-20-agents/afe9d1f61c02b1e8d.txt +299 -0
- package/docs/research/2026-03-20-agents/b4hmkwril.txt +1856 -0
- package/docs/research/2026-03-20-agents/b80ptk89g.txt +1856 -0
- package/docs/research/2026-03-20-agents/bf54s1jss.txt +1150 -0
- package/docs/research/2026-03-20-agents/bhd6kq2kx.txt +1856 -0
- package/docs/research/2026-03-20-agents/bmb2fodyr.txt +988 -0
- package/docs/research/2026-03-20-agents/bmmsrij8i.txt +826 -0
- package/docs/research/2026-03-20-agents/bn4t2ywpu.txt +2175 -0
- package/docs/research/2026-03-20-agents/bu22t9f1z.txt +0 -0
- package/docs/research/2026-03-20-agents/bwvl98v2p.txt +738 -0
- package/docs/research/2026-03-20-agents/psych-a3697a7fd06eb64fd.txt +135 -0
- package/docs/research/2026-03-20-agents/psych-a37776fabc870feae.txt +123 -0
- package/docs/research/2026-03-20-agents/psych-a5b1fe05c0589efaf.txt +2 -0
- package/docs/research/2026-03-20-agents/psych-a95c15b1f29424435.txt +76 -0
- package/docs/research/2026-03-20-agents/psych-a9c26f4d9172dde7c.txt +2 -0
- package/docs/research/2026-03-20-agents/psych-aa19c69f0ca2c5ad3.txt +2 -0
- package/docs/research/2026-03-20-agents/psych-aa4e4cb70e1be5ecb.txt +95 -0
- package/docs/research/2026-03-20-agents/psych-ab5b302f26a554663.txt +102 -0
- package/docs/research/2026-03-20-deep-research-complete.md +101 -0
- package/docs/research/2026-03-20-deep-research-status.md +38 -0
- package/docs/research/2026-03-20-enforcement-research.md +107 -0
- package/expertise/composition-map.yaml +27 -8
- package/expertise/digests/reviewer/ai-coding-digest.md +83 -0
- package/expertise/digests/reviewer/architectural-thinking-digest.md +63 -0
- package/expertise/digests/reviewer/architecture-antipatterns-digest.md +49 -0
- package/expertise/digests/reviewer/code-smells-digest.md +53 -0
- package/expertise/digests/reviewer/coupling-cohesion-digest.md +54 -0
- package/expertise/digests/reviewer/ddd-digest.md +60 -0
- package/expertise/digests/reviewer/dependency-risk-digest.md +40 -0
- package/expertise/digests/reviewer/error-handling-digest.md +55 -0
- package/expertise/digests/reviewer/review-methodology-digest.md +49 -0
- package/exports/hosts/claude/.claude/commands/learn.md +61 -8
- package/exports/hosts/claude/.claude/settings.json +7 -6
- package/exports/hosts/claude/export.manifest.json +6 -3
- package/exports/hosts/claude/host-package.json +3 -0
- package/exports/hosts/codex/export.manifest.json +6 -3
- package/exports/hosts/codex/host-package.json +3 -0
- package/exports/hosts/cursor/.cursor/hooks.json +6 -6
- package/exports/hosts/cursor/export.manifest.json +6 -3
- package/exports/hosts/cursor/host-package.json +3 -0
- package/exports/hosts/gemini/export.manifest.json +6 -3
- package/exports/hosts/gemini/host-package.json +3 -0
- package/hooks/definitions/pretooluse_dispatcher.yaml +26 -0
- package/hooks/definitions/pretooluse_pipeline_guard.yaml +22 -0
- package/hooks/definitions/stop_pipeline_gate.yaml +22 -0
- package/hooks/hooks.json +7 -6
- package/hooks/pretooluse-dispatcher +84 -0
- package/hooks/pretooluse-pipeline-guard +9 -0
- package/hooks/stop-pipeline-gate +9 -0
- package/package.json +2 -2
- package/schemas/decision.schema.json +15 -0
- package/schemas/hook.schema.json +4 -1
- package/skills/TEMPLATE-3-ZONE.md +160 -0
- package/skills/brainstorming/SKILL.md +127 -23
- package/skills/clarifier/SKILL.md +175 -18
- package/skills/claude-cli/SKILL.md +91 -12
- package/skills/codex-cli/SKILL.md +91 -12
- package/skills/debugging/SKILL.md +133 -38
- package/skills/design/SKILL.md +173 -37
- package/skills/dispatching-parallel-agents/SKILL.md +129 -31
- package/skills/executing-plans/SKILL.md +113 -25
- package/skills/executor/SKILL.md +185 -21
- package/skills/finishing-a-development-branch/SKILL.md +107 -18
- package/skills/gemini-cli/SKILL.md +91 -12
- package/skills/humanize/SKILL.md +92 -13
- package/skills/init-pipeline/SKILL.md +90 -17
- package/skills/prepare-next/SKILL.md +93 -24
- package/skills/receiving-code-review/SKILL.md +90 -16
- package/skills/requesting-code-review/SKILL.md +100 -24
- package/skills/requesting-code-review/code-reviewer.md +29 -17
- package/skills/reviewer/SKILL.md +190 -50
- package/skills/run-audit/SKILL.md +92 -15
- package/skills/scan-project/SKILL.md +93 -14
- package/skills/self-audit/SKILL.md +113 -39
- package/skills/skill-research/SKILL.md +94 -7
- package/skills/subagent-driven-development/SKILL.md +129 -30
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +30 -2
- package/skills/subagent-driven-development/implementer-prompt.md +40 -27
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +25 -12
- package/skills/tdd/SKILL.md +125 -20
- package/skills/using-git-worktrees/SKILL.md +118 -28
- package/skills/using-skills/SKILL.md +116 -29
- package/skills/verification/SKILL.md +127 -22
- package/skills/wazir/SKILL.md +517 -153
- package/skills/writing-plans/SKILL.md +134 -28
- package/skills/writing-skills/SKILL.md +91 -13
- package/skills/writing-skills/anthropic-best-practices.md +104 -64
- package/skills/writing-skills/persuasion-principles.md +100 -34
- package/tooling/src/capture/command.js +29 -1
- package/tooling/src/capture/decision.js +40 -0
- package/tooling/src/capture/store.js +1 -0
- package/tooling/src/config/depth-table.js +60 -0
- package/tooling/src/export/compiler.js +7 -8
- package/tooling/src/guards/guardrail-functions.js +131 -0
- package/tooling/src/guards/phase-prerequisite-guard.js +39 -3
- package/tooling/src/hooks/pretooluse-dispatcher.js +300 -0
- package/tooling/src/hooks/pretooluse-pipeline-guard.js +141 -0
- package/tooling/src/hooks/stop-pipeline-gate.js +92 -0
- package/tooling/src/learn/pipeline.js +177 -0
- package/tooling/src/state/db.js +251 -2
- package/tooling/src/state/pipeline-state.js +262 -0
- package/wazir.manifest.yaml +3 -0
- package/workflows/learn.md +61 -8
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export const PHASE_ORDER = ['init', 'clarify', 'execute', 'verify', 'review', 'complete'];
|
|
6
|
+
|
|
7
|
+
const STATE_FILE = 'pipeline-state.json';
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Atomic file write — temp + rename to prevent corruption
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
function atomicWriteJson(filePath, data) {
|
|
14
|
+
const dir = path.dirname(filePath);
|
|
15
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
16
|
+
const tmpPath = `${filePath}.${process.pid}.tmp`;
|
|
17
|
+
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
18
|
+
fs.renameSync(tmpPath, filePath);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function statePath(stateRoot) {
|
|
22
|
+
return path.join(stateRoot, STATE_FILE);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Read / Create
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Read the current pipeline state. Returns null if no state file exists.
|
|
31
|
+
*/
|
|
32
|
+
export function readPipelineState(stateRoot) {
|
|
33
|
+
const fp = statePath(stateRoot);
|
|
34
|
+
if (!fs.existsSync(fp)) return null;
|
|
35
|
+
try {
|
|
36
|
+
return JSON.parse(fs.readFileSync(fp, 'utf8'));
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a fresh pipeline state for a new run.
|
|
44
|
+
*/
|
|
45
|
+
export function createPipelineState(runId, stateRoot) {
|
|
46
|
+
const state = {
|
|
47
|
+
run_id: runId,
|
|
48
|
+
current_phase: 'init',
|
|
49
|
+
phase_history: [],
|
|
50
|
+
allowed_transitions: ['clarify'],
|
|
51
|
+
stop_hook_active: false,
|
|
52
|
+
artifacts: {},
|
|
53
|
+
guardrail_results: {},
|
|
54
|
+
session_id: crypto.randomUUID(),
|
|
55
|
+
updated_at: new Date().toISOString(),
|
|
56
|
+
};
|
|
57
|
+
atomicWriteJson(statePath(stateRoot), state);
|
|
58
|
+
return state;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Transitions
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check whether a transition from currentPhase to nextPhase is valid.
|
|
67
|
+
* Only forward, sequential transitions are allowed.
|
|
68
|
+
*/
|
|
69
|
+
export function isTransitionAllowed(currentPhase, nextPhase) {
|
|
70
|
+
const currentIdx = PHASE_ORDER.indexOf(currentPhase);
|
|
71
|
+
const nextIdx = PHASE_ORDER.indexOf(nextPhase);
|
|
72
|
+
if (currentIdx === -1 || nextIdx === -1) return false;
|
|
73
|
+
return nextIdx === currentIdx + 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Transition to the next phase. Validates the transition is legal.
|
|
78
|
+
* Throws on invalid transition or missing state.
|
|
79
|
+
*/
|
|
80
|
+
export function transitionPhase(stateRoot, nextPhase) {
|
|
81
|
+
const state = readPipelineState(stateRoot);
|
|
82
|
+
if (!state) {
|
|
83
|
+
throw new Error('No pipeline state found. Call createPipelineState first.');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!isTransitionAllowed(state.current_phase, nextPhase)) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Invalid transition: ${state.current_phase} → ${nextPhase}. ` +
|
|
89
|
+
`Allowed: ${state.current_phase} → ${PHASE_ORDER[PHASE_ORDER.indexOf(state.current_phase) + 1] ?? 'none'}`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const now = new Date().toISOString();
|
|
94
|
+
|
|
95
|
+
// Record the outgoing phase in history
|
|
96
|
+
state.phase_history.push({
|
|
97
|
+
phase: state.current_phase,
|
|
98
|
+
entered_at: state.phase_entered_at ?? state.updated_at,
|
|
99
|
+
exited_at: now,
|
|
100
|
+
status: 'completed',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Move to new phase
|
|
104
|
+
state.current_phase = nextPhase;
|
|
105
|
+
state.phase_entered_at = now;
|
|
106
|
+
|
|
107
|
+
// Compute next allowed transition
|
|
108
|
+
const nextIdx = PHASE_ORDER.indexOf(nextPhase);
|
|
109
|
+
state.allowed_transitions = nextIdx < PHASE_ORDER.length - 1
|
|
110
|
+
? [PHASE_ORDER[nextIdx + 1]]
|
|
111
|
+
: [];
|
|
112
|
+
|
|
113
|
+
state.updated_at = now;
|
|
114
|
+
atomicWriteJson(statePath(stateRoot), state);
|
|
115
|
+
return state;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Phase completion (artifact recording)
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Mark the current phase as having produced artifacts.
|
|
124
|
+
* artifacts: { name: { path: string } }
|
|
125
|
+
*/
|
|
126
|
+
export function completePhase(stateRoot, phase, artifacts = {}) {
|
|
127
|
+
const state = readPipelineState(stateRoot);
|
|
128
|
+
if (!state) {
|
|
129
|
+
throw new Error('No pipeline state found.');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const now = new Date().toISOString();
|
|
133
|
+
|
|
134
|
+
for (const [name, meta] of Object.entries(artifacts)) {
|
|
135
|
+
const digest = meta.path ? computeArtifactDigest(meta.path) : null;
|
|
136
|
+
state.artifacts[name] = {
|
|
137
|
+
path: meta.path,
|
|
138
|
+
digest,
|
|
139
|
+
created_at: now,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
state.guardrail_results[phase] = { passed: true, checked_at: now };
|
|
144
|
+
state.updated_at = now;
|
|
145
|
+
atomicWriteJson(statePath(stateRoot), state);
|
|
146
|
+
return state;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Stop hook flag
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Set or clear the stop_hook_active flag to prevent infinite loops.
|
|
155
|
+
*/
|
|
156
|
+
export function setStopHookActive(stateRoot, active) {
|
|
157
|
+
const state = readPipelineState(stateRoot);
|
|
158
|
+
if (!state) {
|
|
159
|
+
throw new Error('No pipeline state found.');
|
|
160
|
+
}
|
|
161
|
+
state.stop_hook_active = !!active;
|
|
162
|
+
state.updated_at = new Date().toISOString();
|
|
163
|
+
atomicWriteJson(statePath(stateRoot), state);
|
|
164
|
+
return state;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// Artifact dependency graph
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Canonical artifact dependency graph for the pipeline.
|
|
173
|
+
* Each artifact lists the artifacts it requires as inputs.
|
|
174
|
+
*/
|
|
175
|
+
export const ARTIFACT_DEPENDENCY_GRAPH = {
|
|
176
|
+
'clarification.md': { requires: [] },
|
|
177
|
+
'spec-hardened.md': { requires: ['clarification.md'] },
|
|
178
|
+
'design.md': { requires: ['spec-hardened.md'] },
|
|
179
|
+
'execution-plan.md': { requires: ['design.md'] },
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Store artifact dependencies in pipeline state.
|
|
184
|
+
*/
|
|
185
|
+
export function setArtifactDependencies(stateRoot, depGraph) {
|
|
186
|
+
const state = readPipelineState(stateRoot);
|
|
187
|
+
if (!state) throw new Error('No pipeline state found.');
|
|
188
|
+
state.artifact_dependencies = depGraph;
|
|
189
|
+
state.updated_at = new Date().toISOString();
|
|
190
|
+
atomicWriteJson(statePath(stateRoot), state);
|
|
191
|
+
return state;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Compute all artifacts downstream of a changed artifact.
|
|
196
|
+
* Walks the dependency graph to find everything that transitively requires
|
|
197
|
+
* the changed artifact.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} changedArtifact — the artifact that was modified
|
|
200
|
+
* @param {object} depGraph — the dependency graph
|
|
201
|
+
* @returns {string[]} downstream artifact names
|
|
202
|
+
*/
|
|
203
|
+
export function computeDownstreamArtifacts(changedArtifact, depGraph) {
|
|
204
|
+
const downstream = [];
|
|
205
|
+
const visited = new Set();
|
|
206
|
+
|
|
207
|
+
function walk(target) {
|
|
208
|
+
for (const [name, meta] of Object.entries(depGraph)) {
|
|
209
|
+
if (visited.has(name)) continue;
|
|
210
|
+
if (meta.requires.includes(target)) {
|
|
211
|
+
visited.add(name);
|
|
212
|
+
downstream.push(name);
|
|
213
|
+
walk(name);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
walk(changedArtifact);
|
|
219
|
+
return downstream;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Classify the mutation level of a changed artifact.
|
|
224
|
+
*
|
|
225
|
+
* - L0 (cosmetic): unknown artifact, no graph impact
|
|
226
|
+
* - L1 (local): leaf artifact with no downstream dependents
|
|
227
|
+
* - L2 (structural): mid-graph artifact with some downstream dependents
|
|
228
|
+
* - L3 (fundamental): root artifact — everything downstream is affected
|
|
229
|
+
*
|
|
230
|
+
* @param {string} changedArtifact
|
|
231
|
+
* @param {object} depGraph
|
|
232
|
+
* @returns {'L0'|'L1'|'L2'|'L3'}
|
|
233
|
+
*/
|
|
234
|
+
export function classifyMutation(changedArtifact, depGraph) {
|
|
235
|
+
if (!(changedArtifact in depGraph)) return 'L0';
|
|
236
|
+
|
|
237
|
+
const downstream = computeDownstreamArtifacts(changedArtifact, depGraph);
|
|
238
|
+
const entry = depGraph[changedArtifact];
|
|
239
|
+
|
|
240
|
+
// Root artifact (no requirements) with downstream dependents
|
|
241
|
+
if (entry.requires.length === 0 && downstream.length > 0) return 'L3';
|
|
242
|
+
|
|
243
|
+
// Mid-graph: has downstream dependents
|
|
244
|
+
if (downstream.length > 0) return 'L2';
|
|
245
|
+
|
|
246
|
+
// Leaf: no downstream dependents
|
|
247
|
+
return 'L1';
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// Artifact digest
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Compute sha256 digest of a file. Returns null if file doesn't exist.
|
|
256
|
+
*/
|
|
257
|
+
export function computeArtifactDigest(filePath) {
|
|
258
|
+
if (!fs.existsSync(filePath)) return null;
|
|
259
|
+
const content = fs.readFileSync(filePath);
|
|
260
|
+
const hash = crypto.createHash('sha256').update(content).digest('hex');
|
|
261
|
+
return `sha256:${hash}`;
|
|
262
|
+
}
|
package/wazir.manifest.yaml
CHANGED
package/workflows/learn.md
CHANGED
|
@@ -2,37 +2,90 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
Extract durable scoped learnings
|
|
5
|
+
Extract durable scoped learnings from the completed run using the 4-stage promotion pipeline.
|
|
6
6
|
|
|
7
7
|
## Phase entry
|
|
8
8
|
|
|
9
9
|
On entering this phase, run:
|
|
10
|
-
`wazir capture event --run <run-id> --event phase_enter --phase
|
|
10
|
+
`wazir capture event --run <run-id> --event phase_enter --phase learn --status in_progress`
|
|
11
11
|
|
|
12
12
|
## Inputs
|
|
13
13
|
|
|
14
14
|
- run artifacts
|
|
15
|
-
- review findings
|
|
15
|
+
- review findings (all passes, all tiers)
|
|
16
16
|
- verification proof
|
|
17
17
|
|
|
18
18
|
## Primary Role
|
|
19
19
|
|
|
20
20
|
- `learner`
|
|
21
21
|
|
|
22
|
+
## Pipeline Stages
|
|
23
|
+
|
|
24
|
+
The learning pipeline follows a 4-stage promotion model (Tally → Candidate → Promote → Active):
|
|
25
|
+
|
|
26
|
+
### Stage 1: TALLY (Automatic)
|
|
27
|
+
|
|
28
|
+
Every finding from the run is:
|
|
29
|
+
1. Canonicalized (file paths, line numbers, identifiers stripped)
|
|
30
|
+
2. Hashed for dedup
|
|
31
|
+
3. Clustered by semantic similarity in `finding_clusters` table
|
|
32
|
+
4. Category-tagged (from the reviewer's finding category)
|
|
33
|
+
|
|
34
|
+
Also:
|
|
35
|
+
- Read `.wazir/runs/<id>/decisions.ndjson` for recurring patterns
|
|
36
|
+
- Append summary to `memory/findings/cumulative-findings.md`
|
|
37
|
+
|
|
38
|
+
Implementation: `tooling/src/learn/pipeline.js` → `tallyFinding()`
|
|
39
|
+
|
|
40
|
+
### Stage 2: CANDIDATE (Automatic)
|
|
41
|
+
|
|
42
|
+
Clusters meeting the promotion threshold are flagged:
|
|
43
|
+
- **Occurrence threshold:** 3+ findings with the same canonical pattern
|
|
44
|
+
- **Run threshold:** Pattern must appear across 2+ distinct runs
|
|
45
|
+
- **Drift cap:** No promotion if active antipatterns count >= 30
|
|
46
|
+
|
|
47
|
+
Implementation: `tooling/src/learn/pipeline.js` → `identifyCandidates()` + `promoteToCandidates()`
|
|
48
|
+
|
|
49
|
+
### Stage 3: PROMOTE (Human Gate)
|
|
50
|
+
|
|
51
|
+
Candidates are proposed for user review:
|
|
52
|
+
- Written to `memory/learnings/proposed/<run-id>-<NNN>.md`
|
|
53
|
+
- User reviews and accepts/rejects via `/wazir audit learnings`
|
|
54
|
+
- Accepted candidates move to `status: accepted` in `antipattern_candidates` table
|
|
55
|
+
|
|
56
|
+
### Stage 4: ACTIVE (Automatic)
|
|
57
|
+
|
|
58
|
+
Accepted antipatterns are loaded into reviewer context for future runs:
|
|
59
|
+
- Injected as project-level learnings alongside expertise modules
|
|
60
|
+
- Hit-rate tracked: if an antipattern triggers in <5% of runs over 90 days, it's demoted
|
|
61
|
+
- Max 30 active project-level antipatterns (drift prevention)
|
|
62
|
+
|
|
63
|
+
## Drift Prevention
|
|
64
|
+
|
|
65
|
+
- **Cap:** 30 active project antipatterns maximum
|
|
66
|
+
- **TTL:** 90-day expiry on unreviewed candidates
|
|
67
|
+
- **Demotion:** Antipatterns with <5% hit rate over 90 days are auto-demoted
|
|
68
|
+
- **Consolidation:** When count exceeds 25, similar antipatterns are merged
|
|
69
|
+
|
|
22
70
|
## Outputs
|
|
23
71
|
|
|
24
|
-
-
|
|
25
|
-
-
|
|
72
|
+
- Tallied findings in `finding_clusters` table
|
|
73
|
+
- Promoted candidates in `antipattern_candidates` table
|
|
74
|
+
- Proposed learning artifacts in `memory/learnings/proposed/`
|
|
75
|
+
- Cumulative findings appended to `memory/findings/cumulative-findings.md`
|
|
26
76
|
|
|
27
77
|
## Approval Gate
|
|
28
78
|
|
|
29
|
-
-
|
|
79
|
+
- Accepted learnings require explicit user review and scope tags
|
|
80
|
+
- Learnings are NEVER auto-applied to future runs without user acceptance
|
|
30
81
|
|
|
31
82
|
## Phase exit
|
|
32
83
|
|
|
33
84
|
On completing this phase, run:
|
|
34
|
-
`wazir capture event --run <run-id> --event phase_exit --phase
|
|
85
|
+
`wazir capture event --run <run-id> --event phase_exit --phase learn --status completed`
|
|
35
86
|
|
|
36
87
|
## Failure Conditions
|
|
37
88
|
|
|
38
|
-
- auto-applied learning drift
|
|
89
|
+
- auto-applied learning drift (bypassing human gate)
|
|
90
|
+
- candidate count exceeds cap without consolidation
|
|
91
|
+
- stale candidates not expired
|