@ancleto/spec 0.11.0 → 0.12.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.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Orchestrates tasks and delegates to subagents for spec-driven development
3
3
  mode: primary
4
- model: opencode-go/qwen3.7-plus
4
+ model: opencode-go/deepseek-v4.1-flash
5
5
  color: '#6366f1'
6
6
  tools:
7
7
  read: true
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Reviews completed changes for correctness, conventions, and approved-scope compliance
3
3
  mode: subagent
4
- model: opencode-go/qwen3.6-plus
4
+ model: opencode-go/deepseek-v4.1-flash
5
5
  temperature: 0.1
6
6
  color: '#ef4444'
7
7
  tools:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  description: Generates aspec change artifacts for spec-driven development
3
3
  mode: subagent
4
- model: opencode-go/qwen3.7-plus
4
+ model: opencode-go/deepseek-v4.1-flash
5
5
  temperature: 0.2
6
6
  color: '#f59e0b'
7
7
  tools:
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Update a change's artifacts when a definition changed
3
+ ---
4
+
5
+ Invoke the `ancleto-update` skill with the Skill tool and follow it exactly; do not improvise steps.
6
+
7
+ **Input**: the argument after `/cleto-update` is a change name (kebab-case) and what changed (a definition to apply, or a spec that already changed). If the change name is omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
8
+
9
+ This is the artifact-update skill, not the CLI `ancleto update` (which reinstalls the package).
10
+
11
+ Work Item grounding (when the request references a Work Item) is owned by `@context-resolver`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ancleto/spec",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Orquestador SDD liviano con subagentes optimizados para costo/tokens",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,87 @@
1
+ ---
2
+ name: ancleto-update
3
+ description: Update the artifacts of an existing change when a definition changed (a main spec, the scope, or a design decision). Merges in place or writes a rev2, preserving unmentioned content. No external binaries required.
4
+ license: MIT
5
+ compatibility: No external binaries required. Works with any agent runtime.
6
+ metadata:
7
+ author: ancleto
8
+ version: '1.0'
9
+ ---
10
+
11
+ # aspec Update
12
+
13
+ **Artifacts language**: write artifact content in the user's conversation language (or the project's configured `language` in `.ancletorc`). Keywords (`Requirement`, `Scenario`, `SHALL`, `WHEN`/`THEN`/`AND`, `ADDED/MODIFIED/REMOVED/RENAMED Requirements`) and file/directory names are literal and MUST NOT be translated.
14
+
15
+ Update the artifacts of an existing change when a definition changed. This skill edits the change's own artifacts with a non-destructive merge; it does not implement code.
16
+
17
+ **Name disambiguation**: this skill (`ancleto-update`, command `/cleto-update`) updates change artifacts. It is NOT the CLI `ancleto update`, which reinstalls the package over an existing install. Never confuse the two.
18
+
19
+ **Input**: a change name (e.g., `add-auth`) and what changed — either a definition the user describes, or a spec that already changed. If the change name is omitted, infer it from conversation context; if vague or ambiguous you MUST list the directories under `aspec/changes/` (excluding `archive/`) and ask the user to select.
20
+
21
+ **IMPORTANT**: Do NOT guess or auto-select a change. If the name resolves inside `aspec/changes/archive/`, STOP and report it — never edit an archived change.
22
+
23
+ ## Steps
24
+
25
+ ### 1. Select the change
26
+
27
+ - A name is provided → use it.
28
+ - Omitted → infer from context. If ambiguous, list `aspec/changes/` (excluding `archive/`) and ask.
29
+ - If it resolves to `aspec/changes/archive/<name>/`, report and stop.
30
+
31
+ ### 2. Read the artifacts
32
+
33
+ Read every present artifact — they constrain what you write:
34
+
35
+ - `proposal.md` — what & why
36
+ - `specs/<capability>/spec.md` — delta requirements
37
+ - `design.md` — how
38
+ - `tasks.md` — implementation checklist
39
+
40
+ If `context.md` exists, read it as background (Work Item title, description, acceptance criteria); it is NOT an artifact and MUST NOT be copied into output.
41
+
42
+ ### 3. Consolidate what changed
43
+
44
+ Two sources, and they are **additive** (neither replaces the other):
45
+
46
+ - **Drift (delta ↔ main)**: for each delta spec at `aspec/changes/<name>/specs/<capability>/spec.md`, compare against the main spec at `aspec/specs/<capability>/spec.md`. Report requirements present in the delta but absent or changed in the main spec (and vice versa).
47
+ - **User-described change**: the definition change the user states.
48
+
49
+ If neither source yields anything actionable, report and stop.
50
+
51
+ ### 4. Classify by owner and apply a non-destructive merge
52
+
53
+ Each change has exactly one owning artifact:
54
+
55
+ | What changed | Owning artifact |
56
+ |---|---|
57
+ | Requirement / specified behavior | `specs/<capability>/spec.md` (`## MODIFIED Requirements`) |
58
+ | Design decision | `design.md` |
59
+ | Scope | `proposal.md` |
60
+ | New work | `tasks.md` |
61
+
62
+ Apply the **minimal** edit to each owning artifact and **preserve everything not mentioned**:
63
+
64
+ - **specs/**: use `## MODIFIED Requirements`, adding or editing scenarios only. Do NOT copy existing scenarios; preserve requirements the delta does not mention.
65
+ - **tasks.md**: add new tasks at the end of their phase; preserve `- [x]` marks; do NOT reorder existing tasks.
66
+ - **proposal.md / design.md**: edit the affected section; leave the rest intact.
67
+
68
+ The merge MUST be idempotent: running twice with the same change yields the same result.
69
+
70
+ ### 5. Choose the write mode (ask the user)
71
+
72
+ Ask the user which write mode to use, and do NOT pick one yourself:
73
+
74
+ - **in-place**: edit the owning artifact in place, then append an entry recording the applied change to a `## Change Log` section in `proposal.md` (create the section if missing).
75
+ - **rev2**: write only the affected artifacts under `aspec/changes/<name>/rev2/`, mirroring their relative paths (`rev2/proposal.md`, `rev2/specs/<capability>/spec.md`, …), leaving the originals untouched; also write `rev2/README.md` with the reason, the date, and the source of the change.
76
+
77
+ ### 6. Report and offer verify
78
+
79
+ Summarize the artifacts updated and the drift resolved. If the change altered specified behavior, offer `ancleto-verify`.
80
+
81
+ ## Guardrails
82
+
83
+ - **Merge, not overwrite**: preserve all content the change does not mention; be idempotent.
84
+ - **Scope**: only the change's own artifacts. NEVER edit `aspec/changes/archive/`, the main specs under `aspec/specs/` (that is `ancleto-sync-specs`), or implementation code (that is `ancleto-apply`).
85
+ - **Ask, don't guess**: if the change, the owning artifact, or the write mode is unclear, ask.
86
+ - **Never auto-select** the change; exclude `archive/` from the candidate list.
87
+ - Never conflate this skill with the CLI `ancleto update`.
@@ -15,12 +15,13 @@ Map of the spec-driven lifecycle. It does not execute work — it routes: given
15
15
  ## The Lifecycle
16
16
 
17
17
  ```
18
- explore → new/propose/ff → continue* → apply → verify → archive
18
+ explore → new/propose/ff → continue* → update? → apply → verify → archive
19
19
  ↑ ↓
20
20
  onboard bulk-archive (batches)
21
21
  ```
22
22
 
23
23
  `*` continue runs once per missing artifact until the set is complete.
24
+ `?` update runs only when a definition changes (a main spec, the scope, or a design decision).
24
25
 
25
26
  ## Routing Rules
26
27
 
@@ -32,6 +33,7 @@ explore → new/propose/ff → continue* → apply → verify → archive
32
33
  | Start structured work | `ancleto-new` (step by step) or `ancleto-propose` (all artifacts at once) |
33
34
  | Full draft when the path is clear | `ancleto-ff` |
34
35
  | Resume a change missing artifacts | `ancleto-continue` (repeat until complete) |
36
+ | Update artifacts after a definition changed | `ancleto-update` |
35
37
  | Implement tasks from ready artifacts | `ancleto-apply` |
36
38
  | Check implementation before closing | `ancleto-verify` |
37
39
  | Close one finished change (sync its specs) | `ancleto-archive` |
@@ -48,6 +50,7 @@ explore → new/propose/ff → continue* → apply → verify → archive
48
50
  | `specs/` or `design.md` missing | `ancleto-continue` |
49
51
  | `tasks.md` missing | `ancleto-continue` |
50
52
  | All artifacts present, tasks unchecked | `ancleto-apply` |
53
+ | A definition changed (main spec, scope, design) | `ancleto-update` |
51
54
  | All tasks checked | `ancleto-verify`, then `ancleto-archive` |
52
55
  | Several changes complete | `ancleto-bulk-archive` |
53
56
 
package/src/cli/index.js CHANGED
@@ -2029,7 +2029,7 @@ async function memoryListCmd(flags) {
2029
2029
  return
2030
2030
  }
2031
2031
  for (const n of nodes) {
2032
- console.log(` [${n.type}/${n.scope}${n.status === 'active' ? '' : '/' + n.status}] ${n.memory_key}`)
2032
+ console.log(` [${n.type}/${n.scope}${n.status === 'active' ? '' : '/' + n.status}] ${n.memory_key} (${n.source})`)
2033
2033
  console.log(` ${n.content}`)
2034
2034
  }
2035
2035
  console.log(`ancleto: ${nodes.length} nodo(s)`)
@@ -182,6 +182,14 @@ function buildEngine(db) {
182
182
  return []
183
183
  }
184
184
 
185
+ // Procedencia de escritura (runtime-owned, solo auditoria): `source` se resuelve
186
+ // exclusivamente desde `context.source || 'runtime'` y NUNCA desde `input` (un
187
+ // `source` forjado en los args del caller se ignora, igual que `id`/`status`/
188
+ // `confidence`). Vocabulario: `mcp:ancleto-memory` (tools MCP), `cli:import`
189
+ // (import del CLI), `runtime` (default programatico), `agent:<rol>` (solo si el
190
+ // runtime confiable lo provee, nunca el caller). Frontera: la procedencia es
191
+ // descriptiva; el motor NO la usa para autorizar ni denegar escrituras (sin
192
+ // canal de identidad autenticado no hay enforcement).
185
193
  function recordNode(input, context = {}) {
186
194
  const memory_key = String(input.memory_key || '').trim()
187
195
  const type = input.type
@@ -232,11 +240,11 @@ function buildEngine(db) {
232
240
  }
233
241
  const clause = where.length ? `WHERE ${where.join(' AND ')}` : ''
234
242
  const rows = db.prepare(
235
- `SELECT ${PUBLIC_COLUMNS}, status FROM memory_nodes n ${clause}
243
+ `SELECT ${PUBLIC_COLUMNS}, n.source, status FROM memory_nodes n ${clause}
236
244
  ORDER BY CASE n.scope WHEN 'task' THEN 0 WHEN 'feature' THEN 1 WHEN 'project' THEN 2 ELSE 9 END,
237
245
  n.created_at DESC, n.rowid DESC`
238
246
  ).all(...params)
239
- return rows.map((r) => ({ ...publicNode(r), status: r.status }))
247
+ return rows.map((r) => ({ ...publicNode(r), source: r.source, status: r.status }))
240
248
  }
241
249
 
242
250
  // Devuelve los nodos activos en el formato de export. Sanitiza paths
@@ -294,7 +302,7 @@ function buildEngine(db) {
294
302
  insertNode.run(
295
303
  newId, memory_key, entry.type, entry.scope,
296
304
  String(entry.content), String(entry.justification || ''),
297
- 'import', 1, entry.createdAt
305
+ 'cli:import', 1, entry.createdAt
298
306
  )
299
307
  summary.updated++
300
308
  } else {
@@ -311,7 +319,7 @@ function buildEngine(db) {
311
319
  insertNode.run(
312
320
  randomUUID(), memory_key, entry.type, entry.scope,
313
321
  String(entry.content), String(entry.justification || ''),
314
- 'import', 1, entry.createdAt
322
+ 'cli:import', 1, entry.createdAt
315
323
  )
316
324
  summary.inserted++
317
325
  }