wendkeep 0.72.1 → 0.74.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 +61 -0
- package/README.en.md +37 -16
- package/README.md +37 -16
- package/docs/en/commands/changes-and-verification.md +10 -5
- package/docs/en/commands/maintenance-and-diagnostics.md +17 -9
- package/docs/en/commands/memory.md +16 -1
- package/docs/en/commands/observer.md +8 -1
- package/docs/en/commands/operating-profiles.md +28 -3
- package/docs/pt-BR/commands/changes-and-verification.md +10 -5
- package/docs/pt-BR/commands/maintenance-and-diagnostics.md +12 -5
- package/docs/pt-BR/commands/memory.md +16 -1
- package/docs/pt-BR/commands/observer.md +7 -1
- package/docs/pt-BR/commands/operating-profiles.md +28 -3
- package/hooks/brain-core.mjs +2 -0
- package/hooks/brain-inject.mjs +6 -6
- package/hooks/brain-recall.mjs +5 -1
- package/hooks/change-context.mjs +11 -0
- package/hooks/change-core.mjs +53 -21
- package/hooks/change-warn.mjs +2 -0
- package/hooks/evidence-context.mjs +41 -0
- package/hooks/evidence-recall.mjs +1 -0
- package/hooks/harness-doctor.mjs +13 -5
- package/hooks/memory-scope.mjs +1 -0
- package/hooks/vault-health.mjs +2 -2
- package/package.json +2 -2
- package/packages/cli/src/index.mjs +13 -3
- package/packages/integrations/src/host-hooks.mjs +1 -0
- package/packages/vault/src/evidence-recall.mjs +343 -0
- package/packages/vault/src/index.mjs +2 -0
- package/packages/vault/src/memory-handoff.mjs +58 -3
- package/packages/vault/src/memory-schema.mjs +12 -2
- package/packages/vault/src/memory-scope.mjs +119 -0
- package/packages/vault/src/memory-store.mjs +86 -24
- package/schema/observer/004-evidence-recall.sql +25 -0
- package/src/change.mjs +10 -4
- package/src/delivery.mjs +303 -0
- package/src/doctor.mjs +47 -10
- package/src/memory.mjs +95 -2
- package/src/observer-sql-store.mjs +141 -5
- package/src/release-provenance.mjs +47 -0
- package/src/skills-seed.mjs +25 -9
- package/src/sync-defs.mjs +5 -2
- package/src/sync.mjs +2 -2
- package/src/taxonomy.mjs +4 -0
- package/src/work-kind.mjs +62 -0
|
@@ -4,9 +4,12 @@ import { createRequire } from 'node:module';
|
|
|
4
4
|
import { join, basename, dirname } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { decodeTranscript, encodeTranscript } from './observer-transcript-store.mjs';
|
|
7
|
+
import {
|
|
8
|
+
chunkMarkdownDocument, recallEvidence, recallTerms,
|
|
9
|
+
} from '../packages/vault/src/evidence-recall.mjs';
|
|
7
10
|
|
|
8
11
|
export const OBSERVER_SQL_FILE = 'observer.sqlite';
|
|
9
|
-
export const OBSERVER_SQL_SCHEMA_VERSION =
|
|
12
|
+
export const OBSERVER_SQL_SCHEMA_VERSION = 4;
|
|
10
13
|
export const OBSERVER_EVENT_SCHEMA_VERSION = 1;
|
|
11
14
|
|
|
12
15
|
const SCHEMA_DIR = fileURLToPath(new URL('../schema/observer/', import.meta.url));
|
|
@@ -117,12 +120,106 @@ export function migrateObserverDatabase(db) {
|
|
|
117
120
|
throw error;
|
|
118
121
|
}
|
|
119
122
|
}
|
|
123
|
+
rebuildSqlEvidenceIndex(db, { missingOnly: true });
|
|
120
124
|
return {
|
|
121
125
|
version: Number(db.prepare('SELECT COALESCE(MAX(version), 0) AS version FROM schema_migrations').get().version || 0),
|
|
122
126
|
applied: db.prepare('SELECT version, name, applied_at FROM schema_migrations ORDER BY version').all(),
|
|
123
127
|
};
|
|
124
128
|
}
|
|
125
129
|
|
|
130
|
+
export function observerFts5Support(db) {
|
|
131
|
+
try {
|
|
132
|
+
db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS evidence_chunks_fts USING fts5(
|
|
133
|
+
chunk_id UNINDEXED,
|
|
134
|
+
project_id UNINDEXED,
|
|
135
|
+
logical_path,
|
|
136
|
+
title,
|
|
137
|
+
heading,
|
|
138
|
+
content,
|
|
139
|
+
tokenize = 'unicode61 remove_diacritics 2'
|
|
140
|
+
)`);
|
|
141
|
+
return { supported: true, engine: 'fts5' };
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return { supported: false, engine: 'lexical-fallback', reason: text(error?.message || error) };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function synchronizeSqlFts(db) {
|
|
148
|
+
const support = observerFts5Support(db);
|
|
149
|
+
if (!support.supported) return support;
|
|
150
|
+
const chunks = Number(db.prepare('SELECT COUNT(*) AS count FROM document_chunks').get().count || 0);
|
|
151
|
+
const indexed = Number(db.prepare('SELECT COUNT(*) AS count FROM evidence_chunks_fts').get().count || 0);
|
|
152
|
+
if (chunks === indexed) return support;
|
|
153
|
+
db.exec(`DELETE FROM evidence_chunks_fts;
|
|
154
|
+
INSERT INTO evidence_chunks_fts(chunk_id, project_id, logical_path, title, heading, content)
|
|
155
|
+
SELECT chunk_id, project_id, logical_path, title, heading, content
|
|
156
|
+
FROM document_chunks;`);
|
|
157
|
+
return { ...support, rebuilt: true, chunks };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function writeSqlDocumentChunks(db, {
|
|
161
|
+
projectId, logicalPath, content, metadata = {}, entityType = 'document', capturedAt = '',
|
|
162
|
+
} = {}) {
|
|
163
|
+
const chunks = chunkMarkdownDocument({
|
|
164
|
+
projectId,
|
|
165
|
+
logicalPath,
|
|
166
|
+
content,
|
|
167
|
+
metadata: { ...metadata, observed_at: metadata.observed_at || capturedAt },
|
|
168
|
+
entityType,
|
|
169
|
+
});
|
|
170
|
+
db.prepare('DELETE FROM document_chunks WHERE project_id = ? AND logical_path = ?').run(projectId, logicalPath);
|
|
171
|
+
const fts = observerFts5Support(db);
|
|
172
|
+
if (fts.supported) {
|
|
173
|
+
db.prepare('DELETE FROM evidence_chunks_fts WHERE project_id = ? AND logical_path = ?').run(projectId, logicalPath);
|
|
174
|
+
}
|
|
175
|
+
const insert = db.prepare(`INSERT INTO document_chunks(
|
|
176
|
+
chunk_id, project_id, logical_path, title, heading, entity_type, change_slug,
|
|
177
|
+
session_id, work_session_id, authority, observed_at, validity, content_hash, ordinal, content
|
|
178
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
179
|
+
const insertFts = fts.supported
|
|
180
|
+
? db.prepare('INSERT INTO evidence_chunks_fts(chunk_id, project_id, logical_path, title, heading, content) VALUES (?, ?, ?, ?, ?, ?)')
|
|
181
|
+
: null;
|
|
182
|
+
for (const chunk of chunks) {
|
|
183
|
+
insert.run(
|
|
184
|
+
chunk.chunk_id, chunk.project_id, chunk.logical_path, chunk.title, chunk.heading,
|
|
185
|
+
chunk.entity_type, chunk.change_slug, chunk.session_id, chunk.work_session_id,
|
|
186
|
+
chunk.authority, chunk.observed_at, chunk.validity, chunk.content_hash,
|
|
187
|
+
chunk.ordinal, chunk.content,
|
|
188
|
+
);
|
|
189
|
+
insertFts?.run(chunk.chunk_id, chunk.project_id, chunk.logical_path, chunk.title, chunk.heading, chunk.content);
|
|
190
|
+
}
|
|
191
|
+
return { chunks: chunks.length, fts };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function rebuildSqlEvidenceIndex(db, { missingOnly = false } = {}) {
|
|
195
|
+
const table = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'document_chunks'").get();
|
|
196
|
+
if (!table) return { documents: 0, chunks: 0, fts: { supported: false, engine: 'unavailable' } };
|
|
197
|
+
const documents = db.prepare(`SELECT d.project_id, d.logical_path, d.content, d.metadata_json, d.entity_type, d.captured_at
|
|
198
|
+
FROM documents d
|
|
199
|
+
WHERE d.deleted_at IS NULL
|
|
200
|
+
AND (? = 0 OR NOT EXISTS (
|
|
201
|
+
SELECT 1 FROM document_chunks c
|
|
202
|
+
WHERE c.project_id = d.project_id AND c.logical_path = d.logical_path
|
|
203
|
+
))
|
|
204
|
+
ORDER BY d.project_id, d.logical_path`).all(missingOnly ? 1 : 0);
|
|
205
|
+
let chunks = 0;
|
|
206
|
+
let fts = observerFts5Support(db);
|
|
207
|
+
for (const document of documents) {
|
|
208
|
+
const result = writeSqlDocumentChunks(db, {
|
|
209
|
+
projectId: document.project_id,
|
|
210
|
+
logicalPath: document.logical_path,
|
|
211
|
+
content: document.content,
|
|
212
|
+
metadata: parseJson(document.metadata_json),
|
|
213
|
+
entityType: document.entity_type,
|
|
214
|
+
capturedAt: document.captured_at,
|
|
215
|
+
});
|
|
216
|
+
chunks += result.chunks;
|
|
217
|
+
fts = result.fts;
|
|
218
|
+
}
|
|
219
|
+
fts = synchronizeSqlFts(db);
|
|
220
|
+
return { documents: documents.length, chunks, fts };
|
|
221
|
+
}
|
|
222
|
+
|
|
126
223
|
export function ensureObserverDatabase(dataDir) {
|
|
127
224
|
const db = openObserverDatabase(dataDir);
|
|
128
225
|
try {
|
|
@@ -266,6 +363,14 @@ function applyDocument(db, event) {
|
|
|
266
363
|
VALUES (?, ?, ?, ?, 'upsert', ?, ?, ?, ?, ?, ?)
|
|
267
364
|
`).run(event.event_id, event.project_id, text(p.entity_type || p.entityType, 'memory'), logicalPath, revision, contentHash,
|
|
268
365
|
text(p.source_session_id || p.sourceSessionId), text(p.source_turn_id || p.sourceTurnId), event.occurred_at, json(p));
|
|
366
|
+
writeSqlDocumentChunks(db, {
|
|
367
|
+
projectId: event.project_id,
|
|
368
|
+
logicalPath,
|
|
369
|
+
content,
|
|
370
|
+
metadata: p.metadata,
|
|
371
|
+
entityType: text(p.entity_type || p.entityType, 'memory'),
|
|
372
|
+
capturedAt: text(p.captured_at || event.occurred_at),
|
|
373
|
+
});
|
|
269
374
|
return { stale: false };
|
|
270
375
|
}
|
|
271
376
|
|
|
@@ -279,6 +384,10 @@ function applyDocumentDelete(db, event) {
|
|
|
279
384
|
VALUES (?, ?, ?, ?, 'delete', ?, '', ?, ?, ?, ?)
|
|
280
385
|
`).run(event.event_id, event.project_id, text(event.payload.entity_type || event.payload.entityType, 'memory'), logicalPath,
|
|
281
386
|
integer(event.payload.revision, 1), text(event.payload.source_session_id || event.payload.sourceSessionId), text(event.payload.source_turn_id || event.payload.sourceTurnId), event.occurred_at, json(event.payload));
|
|
387
|
+
db.prepare('DELETE FROM document_chunks WHERE project_id = ? AND logical_path = ?').run(event.project_id, logicalPath);
|
|
388
|
+
if (observerFts5Support(db).supported) {
|
|
389
|
+
db.prepare('DELETE FROM evidence_chunks_fts WHERE project_id = ? AND logical_path = ?').run(event.project_id, logicalPath);
|
|
390
|
+
}
|
|
282
391
|
return { stale: false };
|
|
283
392
|
}
|
|
284
393
|
|
|
@@ -542,11 +651,38 @@ export function readSqlTree(db, projectId, prefix = '') {
|
|
|
542
651
|
return { schema_version: OBSERVER_SQL_SCHEMA_VERSION, project_id: projectId, prefix, documents: rows };
|
|
543
652
|
}
|
|
544
653
|
|
|
545
|
-
export function searchSqlDocuments(db, projectId, query = '') {
|
|
654
|
+
export function searchSqlDocuments(db, projectId, query = '', { forceLexical = false } = {}) {
|
|
546
655
|
requireProject(db, projectId);
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
656
|
+
const terms = recallTerms(query);
|
|
657
|
+
if (!terms.length) return [];
|
|
658
|
+
const fts = forceLexical
|
|
659
|
+
? { supported: false, engine: 'lexical-fallback' }
|
|
660
|
+
: observerFts5Support(db);
|
|
661
|
+
const columns = `c.chunk_id, c.project_id, c.logical_path, c.title, c.heading,
|
|
662
|
+
c.entity_type, c.change_slug, c.session_id, c.work_session_id, c.authority,
|
|
663
|
+
c.observed_at, c.validity, c.content_hash AS chunk_content_hash, c.ordinal, c.content,
|
|
664
|
+
d.content_hash, d.revision, d.captured_at, d.source_session_id`;
|
|
665
|
+
let rows;
|
|
666
|
+
if (fts.supported) {
|
|
667
|
+
const expression = [...new Set(terms)]
|
|
668
|
+
.map((term) => `"${term.replaceAll('"', '""')}"`)
|
|
669
|
+
.join(' OR ');
|
|
670
|
+
rows = db.prepare(`SELECT ${columns} FROM evidence_chunks_fts f
|
|
671
|
+
JOIN document_chunks c ON c.chunk_id = f.chunk_id
|
|
672
|
+
JOIN documents d ON d.project_id = c.project_id AND d.logical_path = c.logical_path
|
|
673
|
+
WHERE evidence_chunks_fts MATCH ? AND c.project_id = ? AND d.deleted_at IS NULL
|
|
674
|
+
ORDER BY bm25(evidence_chunks_fts, 0, 0, 1.5, 3.0, 2.5, 1.0), c.observed_at DESC
|
|
675
|
+
LIMIT 200`).all(expression, projectId);
|
|
676
|
+
} else {
|
|
677
|
+
// FTS5 may be unavailable in a valid Observer runtime. Rank the complete project corpus
|
|
678
|
+
// instead of truncating by recency before matching, which would make old exact evidence
|
|
679
|
+
// permanently unreachable.
|
|
680
|
+
rows = db.prepare(`SELECT ${columns} FROM document_chunks c
|
|
681
|
+
JOIN documents d ON d.project_id = c.project_id AND d.logical_path = c.logical_path
|
|
682
|
+
WHERE c.project_id = ? AND d.deleted_at IS NULL
|
|
683
|
+
ORDER BY c.observed_at DESC, c.logical_path, c.ordinal`).all(projectId);
|
|
684
|
+
}
|
|
685
|
+
return recallEvidence(rows, query, { topK: 5 });
|
|
550
686
|
}
|
|
551
687
|
|
|
552
688
|
export function readSqlSync(db, projectId) {
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import { cpSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { basename, join } from 'node:path';
|
|
5
|
+
|
|
1
6
|
const DEPENDENCY_FIELDS = Object.freeze([
|
|
2
7
|
'dependencies',
|
|
3
8
|
'devDependencies',
|
|
@@ -5,6 +10,48 @@ const DEPENDENCY_FIELDS = Object.freeze([
|
|
|
5
10
|
'peerDependencies',
|
|
6
11
|
]);
|
|
7
12
|
|
|
13
|
+
export function parsePackIntegrity(raw) {
|
|
14
|
+
const text = String(raw || '');
|
|
15
|
+
const start = text.indexOf('[');
|
|
16
|
+
const end = text.lastIndexOf(']');
|
|
17
|
+
if (start < 0 || end < start) return '';
|
|
18
|
+
try {
|
|
19
|
+
return String(JSON.parse(text.slice(start, end + 1))[0]?.integrity || '');
|
|
20
|
+
} catch {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function packIntegrityInIsolatedCopy(root, { execute = execFileSync } = {}) {
|
|
26
|
+
const tempRoot = mkdtempSync(join(tmpdir(), 'wendkeep-release-pack-'));
|
|
27
|
+
const packageRoot = join(tempRoot, 'package');
|
|
28
|
+
const ignored = new Set(['.git', 'node_modules']);
|
|
29
|
+
try {
|
|
30
|
+
const binding = JSON.parse(readFileSync(join(root, '.wendkeep.json'), 'utf8'));
|
|
31
|
+
const vault = String(binding.vault || '');
|
|
32
|
+
if (vault && !vault.includes('/') && !vault.includes('\\')) ignored.add(vault);
|
|
33
|
+
} catch { /* unbound package: nothing else to exclude */ }
|
|
34
|
+
try {
|
|
35
|
+
cpSync(root, packageRoot, {
|
|
36
|
+
recursive: true,
|
|
37
|
+
filter(source) {
|
|
38
|
+
if (source === root) return true;
|
|
39
|
+
const relativeName = basename(source);
|
|
40
|
+
return !ignored.has(relativeName);
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const command = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
44
|
+
const raw = execute(command, ['pack', '--dry-run', '--json'], {
|
|
45
|
+
cwd: packageRoot,
|
|
46
|
+
encoding: 'utf8',
|
|
47
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
48
|
+
});
|
|
49
|
+
return parsePackIntegrity(raw);
|
|
50
|
+
} finally {
|
|
51
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
8
55
|
export function packageHasSelfDependency(pkg = {}) {
|
|
9
56
|
const name = String(pkg.name || '');
|
|
10
57
|
if (!name) return false;
|
package/src/skills-seed.mjs
CHANGED
|
@@ -27,10 +27,15 @@ pode persistir \`profile use OFF\` explicitamente.
|
|
|
27
27
|
|
|
28
28
|
- **FLOW:** ajuste local, reversível e de escopo fechado, sem contrato/spec, segurança,
|
|
29
29
|
dependência, CI/release ou policy.
|
|
30
|
-
- **GUIDE:**
|
|
30
|
+
- **GUIDE:** implementação compacta com change, sem spec/design/ADR automáticos quando contract_impact é none.
|
|
31
31
|
- **GOVERN:** escolha conservadora em caso de dúvida ou risco e para superfícies sensíveis.
|
|
32
32
|
- **ASSURE:** GOVERN quando confirmação explícita e handoff fazem parte do contrato.
|
|
33
33
|
|
|
34
|
+
Classifique também o work kind: inspection, maintenance, implementation, delivery ou recovery.
|
|
35
|
+
Risco operacional e impacto de contrato são independentes. Merge, push, tag e publish de código
|
|
36
|
+
já aprovado usam delivery + ASSURE sem criar outra change; registre com delivery start e conclua
|
|
37
|
+
com delivery finish para gerar receipt.
|
|
38
|
+
|
|
34
39
|
O harness da LLM faz essa classificação semântica; o Wend Runtime valida e aplica a lease.
|
|
35
40
|
Se não houver uma sessão causal identificada ou o comando falhar, não fabrique estado: use o
|
|
36
41
|
perfil efetivo já injetado e trate \`GOVERN\` como fallback conservador quando ele for o padrão.
|
|
@@ -39,6 +44,7 @@ perfil efetivo já injetado e trate \`GOVERN\` como fallback conservador quando
|
|
|
39
44
|
Antes de editar, leia o **perfil efetivo** injetado pelo WendKeep e siga somente sua rota:
|
|
40
45
|
- \`OFF\`: não imponha processo Wend; a governança pertence ao **harness nativo da LLM**.
|
|
41
46
|
- \`FLOW\`: inicie o microcontrato com \`wendkeep flow start\` antes de editar os paths permitidos.
|
|
47
|
+
- \`delivery\`: inicie \`wendkeep delivery start\` antes das operações autorizadas; não crie change.
|
|
42
48
|
- \`GUIDE\`, \`GOVERN\` ou \`ASSURE\`: não edite código antes de Propose / \`wendkeep change new\`.
|
|
43
49
|
Este gate nunca transforma \`OFF\` ou \`FLOW\` silenciosamente em \`GOVERN\`.
|
|
44
50
|
</HARD-GATE>
|
|
@@ -47,14 +53,16 @@ Este gate nunca transforma \`OFF\` ou \`FLOW\` silenciosamente em \`GOVERN\`.
|
|
|
47
53
|
|
|
48
54
|
- **OFF — LLM nativa:** Wend Runtime desligado; esta skill devolve a execução ao harness nativo.
|
|
49
55
|
- **FLOW — E → V:** \`flow start\` → implementar com wk-tdd → \`flow finish\`; sem change/ADR/verdict.
|
|
50
|
-
- **GUIDE — P → E → V:** change
|
|
56
|
+
- **GUIDE — P → E → V:** change new --guide, sem design/spec/ADR automáticos quando não há impacto de contrato.
|
|
51
57
|
- **GOVERN — P → R → E → V:** loop a2 atual, com design/revisão; é o padrão conservador.
|
|
52
58
|
- **ASSURE — P → R → E → V → C:** GOVERN acrescido de confirmação e handoff explícitos.
|
|
53
59
|
|
|
54
60
|
## Passos para GUIDE, GOVERN e ASSURE
|
|
55
61
|
|
|
56
62
|
1. **Explore** — entenda o problema antes de propor. Leia o código/contexto relevante.
|
|
57
|
-
2. **Propose** — \`wendkeep change new <slug
|
|
63
|
+
2. **Propose** — GUIDE usa \`wendkeep change new <slug> --guide\`; GOVERN/ASSURE usam
|
|
64
|
+
\`wendkeep change new <slug>\`. O GUIDE compacto exige objetivo, critérios de aceite, áreas
|
|
65
|
+
afetadas, testes e resultado. GOVERN/ASSURE criam \`08-Mudanças/<slug>/\` com:
|
|
58
66
|
- \`proposta.md\` — *por quê* e *o que muda* (o WHAT).
|
|
59
67
|
- \`design.md\` — a abordagem técnica.
|
|
60
68
|
- \`tarefas.md\` — a lista de tarefas \`- [ ] N.N descrição\`.
|
|
@@ -83,7 +91,7 @@ Este gate nunca transforma \`OFF\` ou \`FLOW\` silenciosamente em \`GOVERN\`.
|
|
|
83
91
|
\`[req:]\`) recebe verdict automático — pula este passe.
|
|
84
92
|
6. **Archive** — \`wendkeep change archive <slug>\`. O *gate* exige sensores verdes **E**
|
|
85
93
|
\`verdict.json\` cobrindo os \`[req:]\`. Passando, promove os deltas pro \`07-Specs\`,
|
|
86
|
-
move a change pro \`_arquivo
|
|
94
|
+
move a change pro \`_arquivo\`; GUIDE com contract_impact none não gera ADR automático.
|
|
87
95
|
|
|
88
96
|
## Regras
|
|
89
97
|
|
|
@@ -296,10 +304,15 @@ persist it explicitly through \`profile use OFF\`.
|
|
|
296
304
|
|
|
297
305
|
- **FLOW:** a local, reversible, bounded adjustment with no contract/spec, security, dependency,
|
|
298
306
|
CI/release, or policy impact.
|
|
299
|
-
- **GUIDE:**
|
|
307
|
+
- **GUIDE:** compact implementation with a change, but no automatic spec/design/ADR when contract impact is none.
|
|
300
308
|
- **GOVERN:** the conservative choice when uncertain or risky, and for sensitive surfaces.
|
|
301
309
|
- **ASSURE:** GOVERN when explicit confirmation and handoff are part of the contract.
|
|
302
310
|
|
|
311
|
+
Also classify work kind as inspection, maintenance, implementation, delivery, or recovery.
|
|
312
|
+
Operational risk and contract impact are independent. Merge, push, tag, and publish for
|
|
313
|
+
already-approved code use delivery + ASSURE without another change; use delivery start and
|
|
314
|
+
delivery finish to produce a receipt.
|
|
315
|
+
|
|
303
316
|
The LLM harness owns semantic classification; Wend Runtime validates and applies the lease. If
|
|
304
317
|
there is no causally identified session or the command fails, do not fabricate state: use the
|
|
305
318
|
already injected effective profile, with \`GOVERN\` as the conservative configured fallback.
|
|
@@ -308,6 +321,7 @@ already injected effective profile, with \`GOVERN\` as the conservative configur
|
|
|
308
321
|
Before editing, read the injected **effective profile** and follow only its route:
|
|
309
322
|
- \`OFF\`: impose no Wend process; governance belongs to the **native LLM harness**.
|
|
310
323
|
- \`FLOW\`: start the microcontract with \`wendkeep flow start\` before editing allowed paths.
|
|
324
|
+
- \`delivery\`: run \`wendkeep delivery start\` before authorized operations; do not create a change.
|
|
311
325
|
- \`GUIDE\`, \`GOVERN\`, or \`ASSURE\`: do not edit code before Propose / \`wendkeep change new\`.
|
|
312
326
|
This gate never silently turns \`OFF\` or \`FLOW\` into \`GOVERN\`.
|
|
313
327
|
</HARD-GATE>
|
|
@@ -316,15 +330,16 @@ This gate never silently turns \`OFF\` or \`FLOW\` into \`GOVERN\`.
|
|
|
316
330
|
|
|
317
331
|
- **OFF — native LLM:** Wend Runtime is disabled; this skill returns execution to the native harness.
|
|
318
332
|
- **FLOW — E → V:** \`flow start\` → implement with wk-tdd → \`flow finish\`; no change/ADR/verdict.
|
|
319
|
-
- **GUIDE — P → E → V:**
|
|
333
|
+
- **GUIDE — P → E → V:** change new --guide, with no automatic design/spec/ADR when contract impact is none.
|
|
320
334
|
- **GOVERN — P → R → E → V:** the current a2 loop with design/review; the conservative default.
|
|
321
335
|
- **ASSURE — P → R → E → V → C:** GOVERN plus explicit confirmation and handoff.
|
|
322
336
|
|
|
323
337
|
## Steps for GUIDE, GOVERN, and ASSURE
|
|
324
338
|
|
|
325
339
|
1. **Explore** — understand the problem before proposing.
|
|
326
|
-
2. **Propose** — \`wendkeep change new <slug
|
|
327
|
-
|
|
340
|
+
2. **Propose** — GUIDE uses \`wendkeep change new <slug> --guide\`; GOVERN/ASSURE use
|
|
341
|
+
\`wendkeep change new <slug>\`. Compact GUIDE requires objective, acceptance criteria,
|
|
342
|
+
affected areas, tests, and result. The change becomes *current* through global
|
|
328
343
|
\`.brain/CURRENT_CHANGE.md\`. Multiple changes may stay open; hooks and \`change list/status\`
|
|
329
344
|
show every pending task, while commands without \`--change\` use only the current change.
|
|
330
345
|
Before implementation, resolve \`spec_impact\`: \`required\` needs the capability listed in
|
|
@@ -341,7 +356,8 @@ This gate never silently turns \`OFF\` or \`FLOW\` into \`GOVERN\`.
|
|
|
341
356
|
5. **Verify deep** — the **wk-verify** skill (fresh, author≠verifier) writes \`verdict.json\`.
|
|
342
357
|
A trivial change (no \`[req:]\`) gets an auto verdict.
|
|
343
358
|
6. **Archive** — \`wendkeep change archive <slug>\`. The gate needs green sensors AND a
|
|
344
|
-
verdict AND no open tasks. It promotes the delta into \`07-Specs
|
|
359
|
+
verdict AND no open tasks. It promotes the delta into \`07-Specs\`; compact GUIDE does not
|
|
360
|
+
mint an automatic ADR.
|
|
345
361
|
|
|
346
362
|
## Rules
|
|
347
363
|
- Multiple changes may stay open. \`CURRENT_CHANGE.md\` marks one current change without hiding
|
package/src/sync-defs.mjs
CHANGED
|
@@ -66,7 +66,7 @@ The persistent profile is selected explicitly; missing or invalid configuration
|
|
|
66
66
|
|
|
67
67
|
Before every implementation, the native LLM harness **MUST run the routing gate**:
|
|
68
68
|
1. Inspect \`wendkeep profile status\`.
|
|
69
|
-
2. Classify
|
|
69
|
+
2. Classify work kind, contract impact, and operation risk independently; then choose the profile.
|
|
70
70
|
3. Register the choice with
|
|
71
71
|
\`wendkeep profile route <FLOW|GUIDE|GOVERN|ASSURE> --session <id> --reason <text>\`.
|
|
72
72
|
4. Re-check \`wendkeep profile status\` and follow the effective profile before editing.
|
|
@@ -82,12 +82,15 @@ fallback when configuration is missing or invalid. The lease expires when the re
|
|
|
82
82
|
Route work by the effective profile:
|
|
83
83
|
- **OFF** — Wend Runtime is disabled and governance belongs to the native LLM harness; Keep Core stays active.
|
|
84
84
|
- **FLOW** — Execute → Validate through \`wendkeep flow start/finish\`, without creating a change.
|
|
85
|
-
- **GUIDE** — Plan → Execute → Validate through
|
|
85
|
+
- **GUIDE** — Plan → Execute → Validate through \`change new --guide\`; no automatic spec/design/ADR for contract_impact none.
|
|
86
86
|
- **GOVERN** — the default a2 loop: \`wendkeep change new <slug>\` → review → implement tasks test-first
|
|
87
87
|
(tag proof \`[sensor:id]\` and requirement \`[req:ID]\`) → \`wendkeep verify\` →
|
|
88
88
|
\`wendkeep verify --deep\` + independent read-only verdict → \`wendkeep change archive\`.
|
|
89
89
|
- **ASSURE** — GOVERN plus explicit confirmation and handoff.
|
|
90
90
|
|
|
91
|
+
Delivery of already-approved behavior uses \`wendkeep delivery start/status/finish/abandon\` with
|
|
92
|
+
ASSURE authorization and an append-only receipt. It does not create a new change, spec, or ADR.
|
|
93
|
+
|
|
91
94
|
Inspect with \`wendkeep profile status\` / \`wendkeep change status\` /
|
|
92
95
|
\`spec effective --change <slug>\` / \`sensors list\`. Author specs only in
|
|
93
96
|
\`08-Mudanças/<slug>/specs/\`; \`07-Specs\` is generated and must not be edited directly.
|
package/src/sync.mjs
CHANGED
|
@@ -81,11 +81,11 @@ export async function runSync(argv) {
|
|
|
81
81
|
// código é propagado sem ser tratado como falha da cadeia.
|
|
82
82
|
step(3, 'doctor');
|
|
83
83
|
const { runDoctor } = await import('./doctor.mjs');
|
|
84
|
-
const doctorCode = runDoctor(['--vault', vaultBase, '--project', projectPath]);
|
|
84
|
+
const doctorCode = runDoctor(['--vault', vaultBase, '--project', projectPath, '--scope', 'core']);
|
|
85
85
|
|
|
86
86
|
// Nunca afirmar "tudo em dia": o doctor sai 0 mesmo tendo listado órfãos, seções
|
|
87
87
|
// desatualizadas ou modelos sem preço — essas checagens não são fatais. Uma linha final
|
|
88
88
|
// otimista contradiria o relatório logo acima dela.
|
|
89
|
-
process.stdout.write(`\nwendkeep sync:
|
|
89
|
+
process.stdout.write(`\nwendkeep sync: ${doctorCode ? 'falhou — Keep Core comprometido' : 'concluído — Keep Core saudável'}\n`);
|
|
90
90
|
return doctorCode;
|
|
91
91
|
}
|
package/src/taxonomy.mjs
CHANGED
|
@@ -65,6 +65,8 @@ export const HOOK_FILES = [
|
|
|
65
65
|
'subagent-usage.mjs',
|
|
66
66
|
'pricing.json',
|
|
67
67
|
'brain-core.mjs',
|
|
68
|
+
'evidence-recall.mjs',
|
|
69
|
+
'memory-scope.mjs',
|
|
68
70
|
'memory-schema.mjs',
|
|
69
71
|
'memory-store.mjs',
|
|
70
72
|
'memory-handoff.mjs',
|
|
@@ -83,6 +85,7 @@ export const HOOK_FILES = [
|
|
|
83
85
|
'harness-doctor.mjs',
|
|
84
86
|
'lessons-core.mjs',
|
|
85
87
|
'brain-inject.mjs',
|
|
88
|
+
'evidence-context.mjs',
|
|
86
89
|
'brain-recall.mjs',
|
|
87
90
|
'brain-reindex.mjs',
|
|
88
91
|
'session-backfill.mjs',
|
|
@@ -114,6 +117,7 @@ export const RUNNABLE_HOOKS = [
|
|
|
114
117
|
'subagent-stop',
|
|
115
118
|
'task-log',
|
|
116
119
|
'brain-inject',
|
|
120
|
+
'evidence-context',
|
|
117
121
|
'brain-recall',
|
|
118
122
|
'brain-reindex',
|
|
119
123
|
'vault-health',
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export const WORK_KINDS = Object.freeze([
|
|
2
|
+
'inspection',
|
|
3
|
+
'maintenance',
|
|
4
|
+
'implementation',
|
|
5
|
+
'delivery',
|
|
6
|
+
'recovery',
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
export const CONTRACT_IMPACTS = Object.freeze(['none', 'internal', 'public']);
|
|
10
|
+
|
|
11
|
+
function normalize(value, values, code, label) {
|
|
12
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
13
|
+
if (values.includes(normalized)) return normalized;
|
|
14
|
+
const error = new Error(`${label} inválido: ${String(value || '(vazio)')}. Use ${values.join(', ')}.`);
|
|
15
|
+
error.code = code;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalizeWorkKind(value) {
|
|
20
|
+
return normalize(value, WORK_KINDS, 'WENDKEEP_WORK_KIND_INVALID', 'work kind');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function normalizeContractImpact(value) {
|
|
24
|
+
return normalize(value, CONTRACT_IMPACTS, 'WENDKEEP_CONTRACT_IMPACT_INVALID', 'contract impact');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createWorkRoute({
|
|
28
|
+
workKind,
|
|
29
|
+
profile = '',
|
|
30
|
+
contractImpact = 'none',
|
|
31
|
+
operationRisk = [],
|
|
32
|
+
sourceChange = '',
|
|
33
|
+
sourceCommit = '',
|
|
34
|
+
} = {}) {
|
|
35
|
+
const kind = normalizeWorkKind(workKind);
|
|
36
|
+
const impact = normalizeContractImpact(contractImpact);
|
|
37
|
+
const risks = [...new Set((Array.isArray(operationRisk) ? operationRisk : [operationRisk])
|
|
38
|
+
.map((item) => String(item || '').trim()).filter(Boolean))];
|
|
39
|
+
if (kind === 'delivery' && impact !== 'none') {
|
|
40
|
+
const error = new Error('delivery só aceita contract_impact none; alteração de contrato exige implementation.');
|
|
41
|
+
error.code = 'WENDKEEP_DELIVERY_CONTRACT_IMPACT';
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
work_kind: kind,
|
|
46
|
+
profile: String(profile || (kind === 'delivery' ? 'ASSURE' : '')).trim().toUpperCase(),
|
|
47
|
+
contract_impact: impact,
|
|
48
|
+
operation_risk: risks,
|
|
49
|
+
...(sourceChange ? { source_change: String(sourceChange) } : {}),
|
|
50
|
+
...(sourceCommit ? { source_commit: String(sourceCommit) } : {}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function classifyWorkRequest(prompt) {
|
|
55
|
+
const text = String(prompt || '').toLowerCase();
|
|
56
|
+
if (/acompanhar|acompanhe|status|actions|diff|inspecion/.test(text)) return 'inspection';
|
|
57
|
+
if (/token\s+(?:expir|venc)|credencial.*(?:expir|venc)/.test(text)) return 'recovery';
|
|
58
|
+
if (/corrig|corrij|alter|implementar|package\.json.*errad|workflow.*(?:errad|falh)/.test(text)) return 'implementation';
|
|
59
|
+
if (/merge|push|public|publish|tag|release/.test(text)) return 'delivery';
|
|
60
|
+
if (/texto|formata|manuten/.test(text)) return 'maintenance';
|
|
61
|
+
return 'implementation';
|
|
62
|
+
}
|