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 +26 -0
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/docs/en/commands/changes-and-verification.md +50 -0
- package/docs/en/commands/sessions-and-import.md +6 -0
- package/docs/en/commands/verify.md +9 -0
- package/docs/pt-BR/commands/changes-and-verification.md +50 -0
- package/docs/pt-BR/commands/sessions-and-import.md +7 -0
- package/docs/pt-BR/commands/verify.md +8 -0
- package/hooks/change-core.mjs +19 -1
- package/hooks/session-stop.mjs +40 -1
- package/package.json +2 -2
- package/packages/cli/src/index.mjs +7 -0
- package/packages/vault/src/memory-handoff.mjs +15 -0
- package/schema/artifact-manifest-v1.schema.json +35 -0
- package/schema/handoff-contract-v1.schema.json +37 -0
- package/schema/task-contract-v1.schema.json +57 -0
- package/src/task-contracts.mjs +510 -0
- package/src/task-leases.mjs +105 -0
- package/src/task.mjs +115 -0
- package/src/verify.mjs +32 -0
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).
|
package/hooks/change-core.mjs
CHANGED
|
@@ -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({
|
|
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
|
}
|
package/hooks/session-stop.mjs
CHANGED
|
@@ -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
|
-
...(
|
|
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.
|
|
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
|
+
}
|