wendkeep 0.79.0 → 0.80.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 CHANGED
@@ -4,6 +4,32 @@ All notable changes to **wendkeep** are documented here. Format based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.80.0] — 2026-08-24
8
+
9
+ ### Added
10
+
11
+ - **Task Contract v1.** `wendkeep task list/show/evaluate/claim/release` deriva contratos tipados
12
+ da change e do active context, com IDs estáveis, hashes de HEAD/tarefas/spec/manifesto,
13
+ diagnósticos estruturados e leases exclusivos recuperáveis sob o lock do `SESSION_REGISTRY`.
14
+ - **Artifact gates bounded.** Manifestos v1 suportam `name`, `path`, `glob` e `file-count`, com
15
+ fallback de filesystem explícito, ignores seguros, limites de tempo/quantidade e bloqueio de
16
+ path escape, symlink ou junction externo.
17
+ - **Handoff Contract v1.** SessionStop publica handoff causal com task, artifacts, Evidence
18
+ Envelope, decisões, próximas ações, blockers e hashes; ASSURE exige contrato verificado e
19
+ handoffs históricos permanecem `legacy-reported`.
20
+ - **Schemas e guias bilíngues.** Schemas públicos de task/handoff/artifacts, README e guias PT-BR/EN
21
+ documentam comandos, erros, recovery, autoridade e compatibilidade.
22
+
23
+ ### Changed
24
+
25
+ - **Execute → Verify machine-checkable.** `verify` preserva o Evidence Envelope recém-capturado,
26
+ grava `task-evaluation.json` e bloqueia sucesso/pacote deep quando checkbox, requisito, sensor,
27
+ artifact, dependência ou binding causal ainda estiver aberto; `[phase:verify]` separa a tarefa
28
+ final de revisão/arquivo sem enfraquecer o gate de archive.
29
+ - **Memória e Observer sem autoridade duplicada.** `handoff.latest` passa a transportar o contrato
30
+ estruturado verificado pela mesma outbox sanitizada; resumos heurísticos continuam apenas como
31
+ projeção reportada.
32
+
7
33
  ## [0.79.0] — 2026-08-23
8
34
 
9
35
  ### Added
package/README.en.md CHANGED
@@ -242,7 +242,7 @@ The README is the map; the guides provide syntax, options, exit codes, examples,
242
242
  | **Managed worktrees** | `worktree create/list/status/open/finish/cleanup/remove/prune`, merge proof, preflight, crash-safe cleanup/common gate, and receipts | [Managed worktrees](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/worktrees.md) |
243
243
  | **Active context** | `active_contexts` by `repository_id`/`worktree_id`/`work_session_id`, causal transition, quarantine, and explicit recovery | [Active context](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/context.md) |
244
244
  | **Operating profiles** | `profile`, `flow`, always-on Keep Core, and Wend Runtime governance | [Operating profiles](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/operating-profiles.md) |
245
- | **Changes and verification** | `change`, specs, sensors, TDD, evidence, and archive | [Changes and verification](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/changes-and-verification.md) |
245
+ | **Changes and verification** | `change`, specs, sensors, TDD, evidence, Task Contracts, and archive | [Changes and verification](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/changes-and-verification.md) |
246
246
  | **Shared memory** | CORE, SHARED, status, validation, repair, and curation | [Memory](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/memory.md) |
247
247
  | **Sessions and import** | causal hooks, observability reconciliation, and Claude/Codex backfill | [Sessions and import](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/sessions-and-import.md) |
248
248
  | **Notes and knowledge** | BUG/APR/ADR, repairs, renumbering, lessons, and dashboard | [Notes and knowledge](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/notes-and-knowledge.md) |
package/README.md CHANGED
@@ -242,7 +242,7 @@ The README is the map; the guides provide syntax, options, exit codes, examples,
242
242
  | **Managed worktrees** | `worktree create/list/status/open/finish/cleanup/remove/prune`, merge proof, preflight, crash-safe cleanup/common gate, and receipts | [Managed worktrees](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/worktrees.md) |
243
243
  | **Active context** | `active_contexts` by `repository_id`/`worktree_id`/`work_session_id`, causal transition, quarantine, and explicit recovery | [Active context](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/context.md) |
244
244
  | **Operating profiles** | `profile`, `flow`, always-on Keep Core, and Wend Runtime governance | [Operating profiles](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/operating-profiles.md) |
245
- | **Changes and verification** | `change`, specs, sensors, TDD, evidence, and archive | [Changes and verification](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/changes-and-verification.md) |
245
+ | **Changes and verification** | `change`, specs, sensors, TDD, evidence, Task Contracts, and archive | [Changes and verification](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/changes-and-verification.md) |
246
246
  | **Shared memory** | CORE, SHARED, status, validation, repair, and curation | [Memory](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/memory.md) |
247
247
  | **Sessions and import** | causal hooks, observability reconciliation, and Claude/Codex backfill | [Sessions and import](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/sessions-and-import.md) |
248
248
  | **Notes and knowledge** | BUG/APR/ADR, repairs, renumbering, lessons, and dashboard | [Notes and knowledge](https://github.com/rogersialves/wendkeep/blob/main/docs/en/commands/notes-and-knowledge.md) |
@@ -29,6 +29,8 @@ npx wendkeep change new <slug> [--simple|--guide] [--session <id>]
29
29
  npx wendkeep change status [slug] [--session <id>]
30
30
  npx wendkeep spec effective [--change <slug>] [--session <id>]
31
31
  npx wendkeep sensors list
32
+ npx wendkeep task list [--change <slug>] [--session <id>] [--json]
33
+ npx wendkeep task evaluate <task-id> [--change <slug>] [--session <id>] [--json]
32
34
  npx wendkeep verify [--deep] [--change <slug>] [--session <id>]
33
35
  npx wendkeep change archive <slug> [--json] [--session <id>]
34
36
  npx wendkeep change archive recover <operation-id> --change <slug> [--spec-action rollback|resume] [--json]
@@ -48,6 +50,8 @@ npx wendkeep change archive recover <operation-id> --change <slug> [--spec-actio
48
50
  only one unambiguous active context for the worktree is accepted; ambiguity returns exit `2`.
49
51
  - `change relink [--apply]` and `change backlink [--apply]` repair graph links; preview is default.
50
52
  - `change abandon <slug>` drops work without an ADR; `archive --force` needs explicit human choice.
53
+ - `task list/show/evaluate` derives read-only contracts from change authorship. `task claim/release`
54
+ controls owner/lease in the causal active context.
51
55
  - `change archive recover <operation-id> --change <slug> [--spec-action rollback|resume]` inspects a
52
56
  pending transaction by default; with `rollback` or `resume`, it converges only the spec
53
57
  promotion prepared in the journal, under the operation lock and validation. It never promotes the
@@ -148,6 +152,52 @@ cleanup is automatic. Sanitized `operation_id` and
148
152
  Text and --json use the same sanitized diagnostic with code, operation, state, blocker, expected,
149
153
  observed, recovery, reason_codes, diagnostics, and repair.
150
154
 
155
+ ## Task Contracts, artifacts, and handoffs
156
+
157
+ Task Contract v1 is a rebuildable projection whose authorship remains in `tarefas.md`, the
158
+ effective spec, and the change's `artifacts.json`. The contract neither copies spec text nor infers
159
+ a requirement from chat. Project, active context, HEAD, and tasks/spec/manifest hashes bind the
160
+ projection; any mismatch yields `stale`.
161
+
162
+ ```markdown
163
+ - [ ] 2.3 produce report [req:REP-1] [sensor:tests] [depends:2.2] [artifact:report]
164
+ - [ ] 9.1 review the deep package and archive [req:REP-1] [phase:verify]
165
+ ```
166
+
167
+ ```powershell
168
+ npx wendkeep task list --session <id> [--change <slug>] [--json]
169
+ npx wendkeep task show 2.3 --session <id> [--json]
170
+ npx wendkeep task evaluate 2.3 --session <id> [--json]
171
+ npx wendkeep task claim 2.3 --session <id> [--lease-seconds 900] [--json]
172
+ npx wendkeep task release 2.3 --session <id> [--json]
173
+ ```
174
+
175
+ `list`, `show`, and `evaluate` do not write. `claim` and `release` use the atomic
176
+ `SESSION_REGISTRY` lock, are scoped by repository/worktree/work session/change/task, and reject a
177
+ concurrent owner. An expired lease can be recovered; release by a non-owner fails with
178
+ `TASK_LEASE_NOT_OWNER`.
179
+
180
+ An artifact manifest uses `schema_version: 1` and an `artifacts` list; each named entry may use
181
+ type `name`, `path`, `glob`, or `file-count`. The `fromFilesystem` fallback is explicit, never reads
182
+ content, ignores `.git`, `.worktrees`, `node_modules`, and `dist`, has time/entry limits, and fails
183
+ closed on path escape or an external symlink/junction.
184
+
185
+ A checkbox is an authored signal, not proof. `task evaluate` returns `can_complete`, missing
186
+ requirements, sensors, artifacts, dependencies, and `blocking_findings`. In a causal active
187
+ context, `verify` may write `evidencia.json`, but it cannot report success or create the deep
188
+ package while any default `execute` task is blocked; diagnostics live in `task-evaluation.json`.
189
+ Use `[phase:verify]` only for the review/archive task that necessarily runs after the deep package:
190
+ it does not participate in the Execute → Verify gate, remains blocked in individual evaluation,
191
+ and must still be completed before `change archive`.
192
+
193
+ SessionStop binds source/target, task, artifacts, Evidence Envelope, decisions, next actions,
194
+ blockers, and HEAD/tasks/spec hashes. ASSURE requires a verified Handoff Contract v1; it is optional
195
+ in other profiles. Historical summaries remain `legacy-reported`. Shared memory, brain injection,
196
+ and Observer consume the same sanitized projection.
197
+
198
+ Public schemas: `schema/task-contract-v1.schema.json`,
199
+ `schema/artifact-manifest-v1.schema.json`, and `schema/handoff-contract-v1.schema.json`.
200
+
151
201
  ## Tool-scope fence
152
202
 
153
203
  `change-guard` is also projected to Codex `PreToolUse`. Before a Git mutation or supported writing
@@ -142,3 +142,9 @@ from a busy lock or skipped path without reopening the original session.
142
142
 
143
143
  Read [Operating profiles](operating-profiles.md), [retroactive import](retroactive-import.md),
144
144
  [costs and observability](costs-and-observability.md), and [notes](notes-and-knowledge.md).
145
+ # Handoff Contract v1
146
+
147
+ In a causal active context, SessionStop projects a typed handoff with task, artifacts, Evidence
148
+ Envelope, decisions, next actions, blockers, and HEAD/tasks/spec hashes. ASSURE blocks publication
149
+ when that verified contract cannot be produced; other profiles preserve the optional fallback.
150
+ Historical handoffs remain `legacy-reported`. See [Changes and verification](changes-and-verification.md).
@@ -154,3 +154,12 @@ destructive cleanup is automatic. A failure retains `published-recovery-required
154
154
  Return to the [change lifecycle](changes-and-verification.md) for archive, review
155
155
  [Operating profiles](operating-profiles.md), or use [maintenance](maintenance-and-diagnostics.md)
156
156
  when no change exists.
157
+ # Task Contract gate
158
+
159
+ After running sensors and writing the current Evidence Envelope, `verify` evaluates Task Contracts
160
+ for the causal active context. An open checkbox, missing requirement/sensor/artifact, open
161
+ dependency, or stale binding returns exit `1`, preserves `evidencia.json`, writes
162
+ `task-evaluation.json`, and does not create the deep package. Tasks explicitly authored with
163
+ `[phase:verify]` are excluded only from this transition because they depend on the deep package;
164
+ they remain open for the archive gate. See
165
+ [Changes and verification](changes-and-verification.md).
@@ -29,6 +29,8 @@ npx wendkeep change new <slug> [--simple|--guide] [--session <id>]
29
29
  npx wendkeep change status [slug] [--session <id>]
30
30
  npx wendkeep spec effective [--change <slug>] [--session <id>]
31
31
  npx wendkeep sensors list
32
+ npx wendkeep task list [--change <slug>] [--session <id>] [--json]
33
+ npx wendkeep task evaluate <task-id> [--change <slug>] [--session <id>] [--json]
32
34
  npx wendkeep verify [--deep] [--change <slug>] [--session <id>]
33
35
  npx wendkeep change archive <slug> [--json] [--session <id>]
34
36
  npx wendkeep change archive recover <operation-id> --change <slug> [--spec-action rollback|resume] [--json]
@@ -48,6 +50,8 @@ npx wendkeep change archive recover <operation-id> --change <slug> [--spec-actio
48
50
  somente um contexto ativo inequívoco da worktree é aceito; ambiguidade retorna exit `2`.
49
51
  - `change relink [--apply]` e `change backlink [--apply]` reparam o grafo; dry-run é o padrão.
50
52
  - `change abandon <slug>` descarta sem ADR; `archive --force` exige decisão humana explícita.
53
+ - `task list/show/evaluate` projeta contratos read-only da autoria da change. `task claim/release`
54
+ controla owner/lease no active context causal.
51
55
  - `change archive recover <operation-id> --change <slug> [--spec-action rollback|resume]` inspeciona
52
56
  uma transação pendente por padrão; com `rollback` ou `resume`, converge somente a promoção de
53
57
  specs preparada no journal, sob lock e validação. Não promove a change, não apaga o journal e não
@@ -147,6 +151,52 @@ destrutivo automático. Os campos sanitizados
147
151
  Texto e --json usam o mesmo diagnóstico sanitizado com code, operation, state, blocker, expected,
148
152
  observed, recovery, reason_codes, diagnostics e repair.
149
153
 
154
+ ## Task Contracts, artifacts e handoffs
155
+
156
+ Task Contract v1 é uma projeção reconstruível cuja autoria permanece em `tarefas.md`, na spec
157
+ efetiva e no `artifacts.json` da change. O contrato não copia a spec nem infere requisito do chat.
158
+ Projeto, active context, HEAD e hashes de tarefas/spec/manifesto vinculam a projeção; divergência
159
+ resulta em `stale`.
160
+
161
+ ```markdown
162
+ - [ ] 2.3 gerar relatório [req:REP-1] [sensor:tests] [depends:2.2] [artifact:report]
163
+ - [ ] 9.1 revisar pacote deep e arquivar [req:REP-1] [phase:verify]
164
+ ```
165
+
166
+ ```powershell
167
+ npx wendkeep task list --session <id> [--change <slug>] [--json]
168
+ npx wendkeep task show 2.3 --session <id> [--json]
169
+ npx wendkeep task evaluate 2.3 --session <id> [--json]
170
+ npx wendkeep task claim 2.3 --session <id> [--lease-seconds 900] [--json]
171
+ npx wendkeep task release 2.3 --session <id> [--json]
172
+ ```
173
+
174
+ `list`, `show` e `evaluate` não escrevem. `claim` e `release` usam o lock atômico do
175
+ `SESSION_REGISTRY`, são escopados por repository/worktree/work session/change/task e recusam owner
176
+ concorrente. Lease expirada pode ser retomada; release por não-owner falha com
177
+ `TASK_LEASE_NOT_OWNER`.
178
+
179
+ Um manifesto de artifacts usa `schema_version: 1` e uma lista `artifacts`; cada entrada nomeada
180
+ pode ter tipo `name`, `path`, `glob` ou `file-count`. O fallback `fromFilesystem` é explícito, não
181
+ lê conteúdo, ignora `.git`, `.worktrees`, `node_modules` e `dist`, tem limites de tempo/quantidade
182
+ e falha fechado em path escape, symlink ou junction externo.
183
+
184
+ Checkbox é sinal autoral, não prova. `task evaluate` retorna `can_complete`, requisitos, sensores,
185
+ artifacts e dependências ausentes, além de `blocking_findings`. No active context causal, `verify`
186
+ pode gravar `evidencia.json`, mas não anuncia sucesso nem cria o pacote deep enquanto houver task
187
+ bloqueada na fase padrão `execute`; o diagnóstico fica em `task-evaluation.json`. Use
188
+ `[phase:verify]` somente para a tarefa de revisão/arquivo que necessariamente ocorre depois do
189
+ pacote deep: ela não integra o gate Execute → Verify, permanece bloqueada na avaliação individual
190
+ e ainda precisa ser concluída antes de `change archive`.
191
+
192
+ SessionStop vincula origem/destino, task, artifacts, Evidence Envelope, decisões, próximas ações,
193
+ blockers e hashes de HEAD/tarefas/spec. ASSURE exige Handoff Contract v1 verificado; nos demais
194
+ perfis ele é opcional. Handoffs históricos permanecem `legacy-reported`. Memória compartilhada,
195
+ brain injection e Observer consomem a mesma projeção sanitizada.
196
+
197
+ Schemas públicos: `schema/task-contract-v1.schema.json`,
198
+ `schema/artifact-manifest-v1.schema.json` e `schema/handoff-contract-v1.schema.json`.
199
+
150
200
  ## Cerca de escopo para ferramentas
151
201
 
152
202
  O `change-guard` também é projetado para o `PreToolUse` do Codex. Antes de uma mutação Git ou de
@@ -141,3 +141,10 @@ confirmada de um lock ocupado ou de um caminho que foi pulado, sem reabrir a ses
141
141
 
142
142
  Leia [Perfis de Operação](operating-profiles.md), [importação retroativa](retroactive-import.md),
143
143
  [custos e observabilidade](costs-and-observability.md) e [notas](notes-and-knowledge.md).
144
+ # Handoff Contract v1
145
+
146
+ Em active context causal, o SessionStop projeta um handoff tipado com task, artifacts, Evidence
147
+ Envelope, decisões, próximas ações, blockers e hashes de HEAD/tarefas/spec. ASSURE bloqueia a
148
+ publicação quando esse contrato verificado não pode ser produzido; outros perfis preservam o
149
+ fallback opcional. Handoffs históricos continuam `legacy-reported`. Veja
150
+ [Changes e verificação](changes-and-verification.md).
@@ -155,3 +155,11 @@ destrutivo automático. Falha mantém `published-recovery-required`.
155
155
  Volte ao [ciclo de changes](changes-and-verification.md) para archive, confira os
156
156
  [Perfis de Operação](operating-profiles.md) ou consulte
157
157
  [manutenção](maintenance-and-diagnostics.md) quando não houver change.
158
+ # Gate de Task Contract
159
+
160
+ Depois de executar sensores e gravar o Evidence Envelope atual, `verify` avalia os Task Contracts
161
+ do active context causal. Checkbox aberta, requisito/sensor/artifact ausente, dependência aberta ou
162
+ binding stale bloqueia exit `1`, preserva `evidencia.json`, grava `task-evaluation.json` e não cria
163
+ o pacote deep. Tasks explicitamente autoradas com `[phase:verify]` ficam fora apenas desta
164
+ transição porque dependem do pacote deep; continuam abertas para o gate de archive. Veja
165
+ [Changes e verificação](changes-and-verification.md).
@@ -283,18 +283,36 @@ export function parseTasks(md) {
283
283
  const re = /^-\s+\[( |x)\]\s+(\S+)\s+(.*)$/gm;
284
284
  const sensorReG = /\[sensor:\s*([\w.-]+)\]/g;
285
285
  const reqReG = new RegExp(`\\[req:\\s*(${REQ_ID_RE_SRC})\\]`, 'g');
286
+ const dependencyReG = /\[depends:\s*([\w.-]+)\]/g;
287
+ const artifactReG = /\[artifact:\s*([\w.-]+)\]/g;
288
+ const phaseReG = /\[phase:\s*([\w.-]+)\]/g;
286
289
  let m;
287
290
  while ((m = re.exec(String(md))) !== null) {
288
291
  let text = m[3].trim();
289
292
  const sensors = [...new Set([...text.matchAll(sensorReG)].map((entry) => entry[1]))];
290
293
  const reqs = [...text.matchAll(reqReG)].map((r) => r[1]);
294
+ const dependencies = [...new Set([...text.matchAll(dependencyReG)].map((entry) => entry[1]))];
295
+ const artifacts = [...new Set([...text.matchAll(artifactReG)].map((entry) => entry[1]))];
296
+ const phases = [...new Set([...text.matchAll(phaseReG)].map((entry) => entry[1]))];
291
297
  const sensor = sensors[0];
292
298
  if (sensors.length) text = text.replace(sensorReG, '');
293
299
  if (reqs.length) text = text.replace(reqReG, '');
300
+ if (dependencies.length) text = text.replace(dependencyReG, '');
301
+ if (artifacts.length) text = text.replace(artifactReG, '');
302
+ if (phases.length) text = text.replace(phaseReG, '');
294
303
  text = text.replace(/\s+/g, ' ').trim();
295
304
  // `sensor` stays as alias of the first id — older consumers keep working.
296
305
  // `req` stays as alias of the first id — older consumers keep working.
297
- tasks.push({ id: m[2], text, done: m[1] === 'x', ...(sensor ? { sensor, sensors } : {}), ...(reqs.length ? { req: reqs[0], reqs } : {}) });
306
+ tasks.push({
307
+ id: m[2],
308
+ text,
309
+ done: m[1] === 'x',
310
+ ...(sensor ? { sensor, sensors } : {}),
311
+ ...(reqs.length ? { req: reqs[0], reqs } : {}),
312
+ ...(dependencies.length ? { dependencies } : {}),
313
+ ...(artifacts.length ? { artifacts } : {}),
314
+ ...(phases.length ? { phase: phases[0] } : {}),
315
+ });
298
316
  }
299
317
  return tasks;
300
318
  }
@@ -27,6 +27,11 @@ import {
27
27
  import { enqueueMemoryEvent, projectMemoryOutbox } from './memory-store.mjs';
28
28
  import { detectMemoryMode } from './memory-mode.mjs';
29
29
  import { sanitizeMemoryText } from './memory-schema.mjs';
30
+ import {
31
+ buildStructuredTaskHandoff,
32
+ buildTaskContractSnapshot,
33
+ evaluateTaskContracts,
34
+ } from '../src/task-contracts.mjs';
30
35
  import { assertVaultPathSafe } from './vault-path-safety.mjs';
31
36
  import {
32
37
  projectStopMemoryAttempt,
@@ -1406,6 +1411,40 @@ export async function main({
1406
1411
  summary: finalSummary,
1407
1412
  noteRel: sessionRel,
1408
1413
  });
1414
+ let structuredSharedHandoff = sharedHandoff;
1415
+ const effectiveProfile = String(
1416
+ handoffEvidenceAuthority?.context?.operating_profile_task?.profile
1417
+ || entry?.operating_profile_task?.profile
1418
+ || 'GOVERN',
1419
+ ).toUpperCase();
1420
+ if (handoffEvidenceAuthority?.mode === 'contextual') {
1421
+ try {
1422
+ const taskSnapshot = activeContextChangeSlug
1423
+ ? buildTaskContractSnapshot({
1424
+ vaultBase,
1425
+ projectRoot: input.cwd || process.cwd(),
1426
+ changeSlug: activeContextChangeSlug,
1427
+ identity: handoffEvidenceAuthority.identity,
1428
+ })
1429
+ : null;
1430
+ structuredSharedHandoff = buildStructuredTaskHandoff({
1431
+ profile: effectiveProfile,
1432
+ sessionId,
1433
+ snapshot: taskSnapshot,
1434
+ evaluations: taskSnapshot ? evaluateTaskContracts(taskSnapshot) : [],
1435
+ context: handoffEvidenceAuthority.context,
1436
+ shared: sharedHandoff,
1437
+ });
1438
+ } catch (error) {
1439
+ if (effectiveProfile === 'ASSURE') {
1440
+ const detail = `${error?.code || 'HANDOFF_STRUCTURED_REQUIRED'}: ${error?.message || error}`;
1441
+ process.stderr.write(`[wendkeep] Stop ASSURE bloqueado: ${detail}\n`);
1442
+ writeHookOutput({ systemMessage: `wendkeep: Stop ASSURE exige handoff estruturado: ${detail}` });
1443
+ return;
1444
+ }
1445
+ structuredSharedHandoff = sharedHandoff;
1446
+ }
1447
+ }
1409
1448
  memoryHandoff = {
1410
1449
  projectId,
1411
1450
  identity,
@@ -1418,7 +1457,7 @@ export async function main({
1418
1457
  observedAt: turnIdentity.observedAt || new Date(0).toISOString(),
1419
1458
  summary: finalSummary,
1420
1459
  evidence: memoryEvidence,
1421
- ...(sharedHandoff ? { shared: sharedHandoff } : {}),
1460
+ ...(structuredSharedHandoff ? { shared: structuredSharedHandoff } : {}),
1422
1461
  };
1423
1462
  memoryAttempt = stageMemory(vaultBase, {
1424
1463
  handoff: memoryHandoff,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wendkeep",
3
- "version": "0.79.0",
3
+ "version": "0.80.0",
4
4
  "description": "Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -41,7 +41,7 @@
41
41
  "node": ">=18"
42
42
  },
43
43
  "scripts": {
44
- "precheck": "node --check src/change.mjs && node --check src/archive-operation-lock.mjs && node --check src/worktree.mjs && node --check src/worktree-cleanup.mjs && node --check src/provenance-gate.mjs && node --check src/provenance-sources.mjs && node --check src/receipt-ledger.mjs && node --check src/evidence-envelope.mjs && node --check src/context.mjs && node --check src/active-context-health.mjs && node --check src/active-context-runtime.mjs && node --check hooks/active-context-store.mjs && node --check hooks/change-core.mjs && node --check hooks/brain-inject.mjs && node --check hooks/change-context.mjs && node --check packages/vault/src/worktree-metadata.mjs && node --check packages/vault/src/evidence-envelope.mjs",
44
+ "precheck": "node --check src/task-contracts.mjs && node --check src/task-leases.mjs && node --check src/task.mjs && node --check src/change.mjs && node --check src/archive-operation-lock.mjs && node --check src/worktree.mjs && node --check src/worktree-cleanup.mjs && node --check src/provenance-gate.mjs && node --check src/provenance-sources.mjs && node --check src/receipt-ledger.mjs && node --check src/evidence-envelope.mjs && node --check src/context.mjs && node --check src/active-context-health.mjs && node --check src/active-context-runtime.mjs && node --check hooks/active-context-store.mjs && node --check hooks/change-core.mjs && node --check hooks/brain-inject.mjs && node --check hooks/change-context.mjs && node --check hooks/session-stop.mjs && node --check packages/vault/src/worktree-metadata.mjs && node --check packages/vault/src/evidence-envelope.mjs && node --check packages/vault/src/memory-handoff.mjs",
45
45
  "check": "node --check scripts/release.mjs && node --check scripts/release-plan.mjs && node --check scripts/release-provenance.mjs && node --check scripts/run-scope.mjs && node --check src/release-provenance.mjs && node --check bin/wendkeep.mjs && node --check packages/cli/src/index.mjs && node --check src/init.mjs && node --check src/doctor.mjs && node --check src/active-context-health.mjs && node --check src/project-vault.mjs && node --check src/observer-auth.mjs && node --check src/observer-privacy.mjs && node --check src/observer-snapshot.mjs && node --check src/observer-store.mjs && node --check src/observer-memory.mjs && node --check src/observer-memory-publish.mjs && node --check src/observer-sql-store.mjs && node --check src/observer-sql-migrate.mjs && node --check src/observer-sql-publish.mjs && node --check src/observer-transcript-store.mjs && node --check src/observer-server.mjs && node --check src/observer.mjs && node --check src/observer-publish.mjs && node --check src/operating-profile.mjs && node --check src/profile.mjs && node --check src/flow.mjs && node --check src/work-kind.mjs && node --check src/delivery.mjs && node --check web/observer/app.mjs && node --check hooks/observer-publish.mjs && node --check hooks/evidence-context.mjs && node --check hooks/active-context-handoff-evidence.mjs && node --check hooks/evidence-recall.mjs && node --check hooks/memory-scope.mjs && node --check hooks/operating-profile-runtime.mjs && node --check hooks/operating-profile-task-store.mjs && node --check hooks/flow-core.mjs && node --check hooks/flow-protected-policy.mjs && node --check hooks/git-snapshot.mjs && node --check hooks/vault-path-safety.mjs && node --check hooks/vault-runtime-store.mjs && node --check packages/harness/src/index.mjs && node --check packages/harness/src/flow-store.mjs && node --check packages/harness/src/operating-profile.mjs && node --check packages/harness/src/sensors-core.mjs && node --check packages/integrations/src/host-hooks.mjs && node --check packages/integrations/src/hook-envelope.mjs && node --check packages/integrations/src/prompt-content.mjs && node --check packages/integrations/src/transcript-usage.mjs && node --check packages/integrations/src/transcripts.mjs && node --check packages/integrations/src/session-identity.mjs && node --check packages/integrations/src/index.mjs && node --check packages/mcp/src/config.mjs && node --check packages/mcp/src/index.mjs && node --check packages/vault/src/index.mjs && node --check packages/vault/src/project-vault.mjs && node --check packages/vault/src/vault-path-safety.mjs && node --check packages/vault/src/locale.mjs && node --check packages/vault/src/memory-schema.mjs && node --check packages/vault/src/memory-mode.mjs && node --check packages/vault/src/memory-scope.mjs && node --check packages/vault/src/memory-candidate-policy.mjs && node --check packages/vault/src/evidence-recall.mjs && node --check packages/vault/src/memory-handoff.mjs && node --check packages/vault/src/memory-store.mjs && node --check packages/vault/src/validate-core.mjs && node --check packages/vault/src/validate-memory.mjs",
46
46
  "test": "node --test --test-concurrency=2",
47
47
  "test:core": "node scripts/run-scope.mjs core",
@@ -64,6 +64,8 @@ Usage:
64
64
  wendkeep context repair --key <repository:worktree:work-session> --revision <n> --reason <text> --session <id> [--json]
65
65
  Inspect or explicitly recover a quarantined causal scope conflict.
66
66
  Repair revalidates orphan/removed contexts or expired request leases without deleting history.
67
+ wendkeep task <sub> Typed task contracts: list | show | evaluate | claim | release.
68
+ Resolves the change from the causal active context; supports --session/--change/--json.
67
69
  wendkeep change <sub> Change lifecycle: new [--simple|--guide] | use | bind <slug> --session <id> | continue | list | show |
68
70
  status | done <id> | undone <id> | diff | archive [--force] | abandon | relink | backlink.
69
71
  --session <id> selects the causal active_context for implicit change operations.
@@ -292,6 +294,11 @@ async function main(argv) {
292
294
  runChange(rest);
293
295
  break;
294
296
  }
297
+ case 'task': {
298
+ const { runTask } = await import('../../../src/task.mjs');
299
+ process.exit(runTask(rest));
300
+ break;
301
+ }
295
302
  case 'session': {
296
303
  const { runSession } = await import('../../../src/session.mjs');
297
304
  runSession(rest);
@@ -61,6 +61,10 @@ export function normalizeSharedHandoff(shared) {
61
61
  const value = sanitizeValue(shared[field]);
62
62
  if (hasMeaningfulValue(value)) normalized[field] = value;
63
63
  }
64
+ if (Object.hasOwn(shared, 'handoff_contract')) {
65
+ const contract = sanitizeValue(shared.handoff_contract);
66
+ if (hasMeaningfulValue(contract)) normalized.handoff_contract = contract;
67
+ }
64
68
 
65
69
  return Object.keys(normalized).length ? normalized : null;
66
70
  }
@@ -257,6 +261,17 @@ export function buildSessionMemoryEvents({
257
261
  const events = [];
258
262
 
259
263
  if (normalizedShared) {
264
+ if (normalizedShared.handoff_contract) {
265
+ const contract = normalizedShared.handoff_contract;
266
+ events.push(makeEvent(context, {
267
+ memoryKey: 'handoff.latest',
268
+ value: contract,
269
+ authority: contract.schema_version === 1 && contract.authority === 'verified'
270
+ ? 'verified' : 'reported',
271
+ evidence: Array.isArray(contract.evidence) && contract.evidence.length
272
+ ? contract.evidence : [noteRel],
273
+ }));
274
+ }
260
275
  for (const [field, memoryKey] of SHARED_HANDOFF_FIELDS) {
261
276
  if (!Object.hasOwn(normalizedShared, field)) continue;
262
277
  events.push(makeEvent(context, {
@@ -0,0 +1,35 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://wendkeep.dev/schema/artifact-manifest-v1.schema.json",
4
+ "title": "WendKeep Artifact Manifest v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "artifacts"],
8
+ "properties": {
9
+ "schema_version": { "const": 1 },
10
+ "artifacts": {
11
+ "type": "array",
12
+ "items": { "$ref": "#/$defs/artifact" }
13
+ }
14
+ },
15
+ "$defs": {
16
+ "artifact": {
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["name", "type"],
20
+ "properties": {
21
+ "name": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
22
+ "type": { "enum": ["name", "path", "glob", "file-count"] },
23
+ "path": { "type": "string" },
24
+ "glob": { "type": "string" },
25
+ "min": { "type": "integer", "minimum": 0 },
26
+ "max": { "type": "integer", "minimum": 0 },
27
+ "fromFilesystem": { "type": "boolean" }
28
+ },
29
+ "allOf": [
30
+ { "if": { "properties": { "type": { "const": "path" } } }, "then": { "required": ["path"] } },
31
+ { "if": { "properties": { "type": { "enum": ["glob", "file-count"] } } }, "then": { "required": ["glob"] } }
32
+ ]
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://wendkeep.dev/schema/handoff-contract-v1.schema.json",
4
+ "title": "WendKeep Handoff Contract v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version", "handoff_id", "from", "to", "active_context_id", "task_id",
9
+ "task_contract_id", "artifacts", "evidence", "decisions", "next_actions", "blockers",
10
+ "head_sha", "tasks_sha256", "spec_sha256", "authority"
11
+ ],
12
+ "properties": {
13
+ "schema_version": { "const": 1 },
14
+ "handoff_id": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
15
+ "from": { "type": "string", "minLength": 1 },
16
+ "to": { "type": "string", "minLength": 1 },
17
+ "active_context_id": { "type": "string", "minLength": 1 },
18
+ "task_id": { "type": "string" },
19
+ "task_contract_id": { "type": "string" },
20
+ "artifacts": { "$ref": "#/$defs/stringArray" },
21
+ "evidence": { "$ref": "#/$defs/stringArray" },
22
+ "decisions": { "$ref": "#/$defs/stringArray" },
23
+ "next_actions": { "$ref": "#/$defs/stringArray" },
24
+ "blockers": { "$ref": "#/$defs/stringArray" },
25
+ "head_sha": { "type": "string", "minLength": 1 },
26
+ "tasks_sha256": { "type": "string", "minLength": 1 },
27
+ "spec_sha256": { "type": "string", "minLength": 1 },
28
+ "authority": { "enum": ["verified", "reported"] }
29
+ },
30
+ "$defs": {
31
+ "stringArray": {
32
+ "type": "array",
33
+ "items": { "type": "string" },
34
+ "uniqueItems": true
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://wendkeep.dev/schema/task-contract-v1.schema.json",
4
+ "title": "WendKeep Task Contract v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version", "contract_id", "task_id", "change_slug", "title", "phase", "status",
9
+ "inputs", "expected_outputs", "acceptance_criteria", "requirement_ids",
10
+ "required_sensors", "required_artifacts", "dependencies", "owner", "work_session_id",
11
+ "evidence_envelope_id", "checked", "authored_sha256", "binding"
12
+ ],
13
+ "properties": {
14
+ "schema_version": { "const": 1 },
15
+ "contract_id": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
16
+ "task_id": { "type": "string", "minLength": 1 },
17
+ "change_slug": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$" },
18
+ "title": { "type": "string" },
19
+ "phase": { "enum": ["execute", "verify"] },
20
+ "status": { "enum": ["ready", "blocked", "pending-evaluation", "completed", "stale"] },
21
+ "inputs": { "$ref": "#/$defs/stringArray" },
22
+ "expected_outputs": { "$ref": "#/$defs/stringArray" },
23
+ "acceptance_criteria": { "$ref": "#/$defs/stringArray" },
24
+ "requirement_ids": { "$ref": "#/$defs/stringArray" },
25
+ "required_sensors": { "$ref": "#/$defs/stringArray" },
26
+ "required_artifacts": { "$ref": "#/$defs/stringArray" },
27
+ "dependencies": { "$ref": "#/$defs/stringArray" },
28
+ "owner": { "type": ["string", "null"] },
29
+ "work_session_id": { "type": ["string", "null"] },
30
+ "evidence_envelope_id": { "type": ["string", "null"] },
31
+ "checked": { "type": "boolean" },
32
+ "authored_sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
33
+ "binding": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "required": [
37
+ "project_id", "active_context_id", "head_sha", "tasks_sha256",
38
+ "effective_spec_sha256", "artifact_manifest_sha256"
39
+ ],
40
+ "properties": {
41
+ "project_id": { "type": "string", "minLength": 1 },
42
+ "active_context_id": { "type": "string", "minLength": 1 },
43
+ "head_sha": { "type": "string", "minLength": 1 },
44
+ "tasks_sha256": { "type": "string", "minLength": 1 },
45
+ "effective_spec_sha256": { "type": "string", "minLength": 1 },
46
+ "artifact_manifest_sha256": { "type": "string", "minLength": 1 }
47
+ }
48
+ }
49
+ },
50
+ "$defs": {
51
+ "stringArray": {
52
+ "type": "array",
53
+ "items": { "type": "string" },
54
+ "uniqueItems": true
55
+ }
56
+ }
57
+ }