thoth-agents 0.3.9 → 0.3.10

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.
@@ -9,7 +9,7 @@
9
9
  {
10
10
  "name": "thoth-agents",
11
11
  "description": "Adaptive root orchestration, six specialist subagents, and a runtime-autonomous Spec Kit-compatible SDD bundle.",
12
- "version": "0.3.9",
12
+ "version": "0.3.10",
13
13
  "author": {
14
14
  "name": "thoth-agents maintainers"
15
15
  },
@@ -2,6 +2,9 @@ import type { AgentDispatchMethod, AgentRoleName } from '../harness/core/agent-p
2
2
  import type { HarnessCapabilities, HarnessId } from '../harness/types';
3
3
  export type AgentPromptRole = AgentRoleName;
4
4
  export interface LifecycleNomenclature {
5
+ freshDelegation: string;
6
+ sameAssignmentContinuation: string;
7
+ independentContext: string;
5
8
  statusAction: string;
6
9
  terminalState: string;
7
10
  nonterminalState: string;
@@ -34,7 +34,7 @@ import {
34
34
  updateOpenCodeMainConfig,
35
35
  writeConfig,
36
36
  writeLiteConfig
37
- } from "./chunk-2TLT6WWY.js";
37
+ } from "./chunk-M2WF46BP.js";
38
38
 
39
39
  // src/cli/codex-paths.ts
40
40
  import { homedir } from "os";
@@ -802,6 +802,9 @@ var OPENCODE_PROMPT_DIALECT = {
802
802
  progressTool: "todowrite",
803
803
  hostStatusSurface: "task_status",
804
804
  lifecycle: {
805
+ freshDelegation: "`task` without `task_id`",
806
+ sameAssignmentContinuation: "`task` with the prior `task_id`",
807
+ independentContext: "omitting `task_id` creates an isolated child session",
805
808
  statusAction: "wait, poll, and collect",
806
809
  terminalState: "terminal task_status result",
807
810
  nonterminalState: "nonterminal task_status result",
@@ -835,6 +838,9 @@ var CODEX_PROMPT_DIALECT = {
835
838
  progressTool: "functions.update_plan",
836
839
  hostStatusSurface: "collaboration.list_agents",
837
840
  lifecycle: {
841
+ freshDelegation: '`collaboration.spawn_agent` with `fork_turns="none"`',
842
+ sameAssignmentContinuation: "`collaboration.followup_task` for the existing agent",
843
+ independentContext: '`fork_turns="none"` prevents parent-history inheritance',
838
844
  statusAction: "wait and inspect status",
839
845
  terminalState: "terminal mailbox completion or failure update",
840
846
  nonterminalState: "collaboration.wait_agent timeout or silence",
@@ -875,6 +881,9 @@ var CLAUDE_CODE_PROMPT_DIALECT = {
875
881
  progressTool: "TodoWrite",
876
882
  hostStatusSurface: "TodoWrite",
877
883
  lifecycle: {
884
+ freshDelegation: "a normal `Agent` invocation",
885
+ sameAssignmentContinuation: "`SendMessage` to the prior agent ID",
886
+ independentContext: "do not use `fork` for independent work",
878
887
  statusAction: "wait, inspect, and collect",
879
888
  terminalState: "terminal TaskOutput result",
880
889
  nonterminalState: "nonterminal TaskOutput result",
@@ -1013,6 +1022,10 @@ var ORCHESTRATION_POLICY = {
1013
1022
  "Delegate only when specialization, context isolation, or independent parallel work creates a net gain.",
1014
1023
  "Prefer child agents for read-heavy exploration, research, analysis, and verification.",
1015
1024
  "Use one writer for each mutable surface and never parallelize overlapping writes.",
1025
+ "A fresh subagent instance is the default when the objective, SDD phase, mutable surface, or independent judgment changes.",
1026
+ "Continue an existing subagent only to steer, complete, or clarify the same bounded assignment; completed agents are not a reusable role pool.",
1027
+ "Every Oracle plan review, verification round, and approval or PASS judgment uses a fresh Oracle instance; reuse is limited to clarifying current findings.",
1028
+ "Wait and status operations collect only the active nonterminal assignment and do not authorize later reuse.",
1016
1029
  "Child agents return distilled evidence instead of raw logs or file dumps."
1017
1030
  ]
1018
1031
  };
@@ -1201,85 +1214,6 @@ var SDD_PHASES = [
1201
1214
  reason: "Transactionally sync declared durable deltas and close verified work with a dated audit trail."
1202
1215
  }
1203
1216
  ];
1204
- var SDD_ARTIFACT_GRAPH = [
1205
- {
1206
- id: "spec",
1207
- path: "spec.md",
1208
- producedBy: "specify",
1209
- consumes: [],
1210
- requiredFor: ["accelerated", "full"]
1211
- },
1212
- {
1213
- id: "plan",
1214
- path: "plan.md",
1215
- producedBy: "plan",
1216
- consumes: ["spec"],
1217
- requiredFor: ["accelerated", "full"]
1218
- },
1219
- {
1220
- id: "tasks",
1221
- path: "tasks.md",
1222
- producedBy: "tasks",
1223
- consumes: ["spec", "plan"],
1224
- requiredFor: ["accelerated", "full"]
1225
- },
1226
- {
1227
- id: "requirements-checklist",
1228
- path: "checklists/requirements.md",
1229
- producedBy: "checklist",
1230
- consumes: ["spec"],
1231
- requiredFor: []
1232
- },
1233
- {
1234
- id: "research",
1235
- path: "research.md",
1236
- producedBy: "plan",
1237
- consumes: ["spec"],
1238
- requiredFor: []
1239
- },
1240
- {
1241
- id: "data-model",
1242
- path: "data-model.md",
1243
- producedBy: "plan",
1244
- consumes: ["spec"],
1245
- requiredFor: []
1246
- },
1247
- {
1248
- id: "contracts",
1249
- path: "contracts/",
1250
- producedBy: "plan",
1251
- consumes: ["spec"],
1252
- requiredFor: []
1253
- },
1254
- {
1255
- id: "quickstart",
1256
- path: "quickstart.md",
1257
- producedBy: "plan",
1258
- consumes: ["spec", "plan"],
1259
- requiredFor: []
1260
- },
1261
- {
1262
- id: "plan-review",
1263
- path: "plan-review.md",
1264
- producedBy: "plan-review",
1265
- consumes: ["spec", "plan", "tasks"],
1266
- requiredFor: []
1267
- },
1268
- {
1269
- id: "verify-report",
1270
- path: "verify-report.md",
1271
- producedBy: "verify",
1272
- consumes: ["spec", "plan", "tasks"],
1273
- requiredFor: ["accelerated", "full"]
1274
- },
1275
- {
1276
- id: "archive-report",
1277
- path: "archive-report.md",
1278
- producedBy: "archive",
1279
- consumes: ["spec", "plan", "tasks", "verify-report"],
1280
- requiredFor: ["accelerated", "full"]
1281
- }
1282
- ];
1283
1217
  var SDD_ROUTE_EXECUTION_POLICIES = [
1284
1218
  {
1285
1219
  route: "direct",
@@ -1372,13 +1306,6 @@ function getSddRouteExecutionPolicy(route) {
1372
1306
  validationGates: [...policy.validationGates]
1373
1307
  };
1374
1308
  }
1375
- function getSddArtifactGraph() {
1376
- return SDD_ARTIFACT_GRAPH.map((artifact) => ({
1377
- ...artifact,
1378
- consumes: [...artifact.consumes],
1379
- requiredFor: [...artifact.requiredFor]
1380
- }));
1381
- }
1382
1309
  function getSddPhase(id) {
1383
1310
  const phase = SDD_PHASES.find((candidate) => candidate.id === id);
1384
1311
  if (!phase) {
@@ -1486,9 +1413,6 @@ function renderSddRoute(route) {
1486
1413
  function renderRoleDirectory() {
1487
1414
  return getAgentPackContract().roles.filter((role) => role.name !== "orchestrator").map((role) => `- ${roleTemplate(role.name)}: ${role.responsibility}`).join("\n");
1488
1415
  }
1489
- function renderArtifactSummary() {
1490
- return getSddArtifactGraph().filter((artifact) => artifact.requiredFor.length === 0).map((artifact) => artifact.path).join(", ");
1491
- }
1492
1416
  function createOrchestratorPromptSections() {
1493
1417
  const workflow = getSddWorkflowContract();
1494
1418
  const policy = getAgentPackContract().orchestrationPolicy;
@@ -1504,10 +1428,19 @@ You are the adaptive root for thoth-agents. Keep requirements, decisions, execut
1504
1428
  - Delegate only for net gain from specialization, context isolation, review, or safe parallelism. The maximum delegation depth is ${policy.maxDelegationDepth}; children never delegate.
1505
1429
  - Maintain one writer for each mutable surface. Parallelize only independent work with no overlapping writes.
1506
1430
  - Keep prompts bounded; request distilled evidence, not raw logs or full files.
1431
+ - Preserve unrelated changes; report changed files, evidence, risks, and capability gaps.
1507
1432
  - Use \`{{userQuestionTool}}\` only when a material unresolved choice changes the result. Continue all safe non-blocked work first.
1508
1433
  - Use \`{{progressTool}}\` only when the work genuinely has multiple dependent steps.
1509
1434
  </operating-model>
1510
1435
 
1436
+ <delegation-lifecycle>
1437
+ - A new objective, SDD phase, mutable surface, or independent judgment is a work boundary: start a fresh specialist using {{lifecycleFreshDelegation}}. Never treat completed agents as a reusable role pool.
1438
+ - Independent context: {{lifecycleIndependentContext}}.
1439
+ - Continue with {{lifecycleSameAssignmentContinuation}} only to steer, complete, or clarify the same bounded assignment; never to cross a work boundary.
1440
+ - {{lifecycleSameSessionProbe}} only collects the active nonterminal assignment and does not authorize later reuse.
1441
+ - Every Oracle plan review, verification round, and approval or PASS judgment uses a fresh Oracle instance. An existing Oracle session may only clarify its current findings.
1442
+ </delegation-lifecycle>
1443
+
1511
1444
  <routing>
1512
1445
  ${renderRoleDirectory()}
1513
1446
  </routing>
@@ -1541,29 +1474,20 @@ ${renderRoleDirectory()}
1541
1474
  </external-skills>
1542
1475
 
1543
1476
  <memory>
1544
- - Load the installed \`thoth-mem\` skill for resume or prior work and provider-backed memory; never invent its protocol.
1545
- - Preserve a durable decision, root cause, convention, or discovery only when reusable. Root owns the stable root session ID, project, lifecycle, real-user intent, and authorization; never invent identity or confirmed effects.
1546
- - Follow the skill at verified compaction and a meaningful semantic boundary. Children receive only bounded MEMORY dispatch and never own root lifecycle.
1547
- - \`openspec/\` remains canonical; do not mirror SDD phase artifacts into provider memory. A memory failure degrades memory only and does not block unrelated implementation or verification.
1477
+ - Load the installed \`thoth-mem\` skill for resume or prior work; never invent its protocol.
1478
+ - Preserve a durable decision, root cause, convention, or discovery only when reusable. Root owns the stable root session ID, project, lifecycle, real-user intent, and authorization.
1479
+ - Follow the skill at verified compaction or a meaningful semantic boundary; children receive only bounded MEMORY and never own root lifecycle.
1480
+ - \`openspec/\` remains canonical; do not mirror SDD phase artifacts. A memory failure does not block unrelated work.
1548
1481
  </memory>
1549
1482
 
1550
1483
  <artifacts>
1551
- - Preserve Spec Kit semantics inside ${workflow.artifactRoot}.
1552
- - Accelerated and Full require spec.md, plan.md, tasks.md, verify-report.md, and archive-report.md; optional when useful: ${renderArtifactSummary()}.
1553
- - Root owns openspec/ coordination and thoth-sdd gates; product work has one writer, and root alone moves [~] -> [x] after evidence.
1554
- - ${roleTemplate("oracle")} returns read-only findings. Root persists verification; after PASS, archive syncs declared durable deltas and moves the change to openspec/changes/archive/YYYY-MM-DD-<feature>/.
1484
+ - In ${workflow.artifactRoot}, Accelerated and Full require spec.md, plan.md, tasks.md, verify-report.md, and archive-report.md.
1485
+ - Root owns openspec/ gates and task state, moves [~] -> [x] after evidence, and keeps one product writer.
1486
+ - ${roleTemplate("oracle")} returns read-only findings; root persists verification and archives declared durable deltas after PASS.
1555
1487
  </artifacts>
1556
1488
 
1557
- <execution>
1558
- - Validate contracts and tests first; use test-first work for behavior. Root or one writer implements; do not delegate merely because an agent exists.
1559
- - ${roleTemplate("oracle")} owns selected plan review and every verification; root and writers never self-approve.
1560
- - Preserve unrelated working-tree changes. Never instruct an agent to discard them.
1561
- - Report changed files, evidence, risks, and capability gaps truthfully.
1562
- </execution>
1563
-
1564
1489
  <delegation>
1565
- - Dispatch through \`{{delegationTool}}\` with this envelope; use the same boundaries for non-SDD work.
1566
- - Parallelize only independent work and await requested results before synthesis.
1490
+ - Use this envelope for all \`{{delegationTool}}\` delegation; parallelize only independent work and await results.
1567
1491
  - Child return fields: conclusion, evidence, verification, risks, openQuestions, nextAction.
1568
1492
 
1569
1493
  ${renderSddPhaseDispatchTemplate()}
@@ -1741,6 +1665,15 @@ function renderRoleText(section, dialect) {
1741
1665
  ).replaceAll("{{userQuestionTool}}", dialect.tools.userQuestionTool).replaceAll("{{progressTool}}", dialect.tools.progressTool).replaceAll(
1742
1666
  "{{lifecycleStatusAction}}",
1743
1667
  dialect.tools.lifecycle.statusAction
1668
+ ).replaceAll(
1669
+ "{{lifecycleFreshDelegation}}",
1670
+ dialect.tools.lifecycle.freshDelegation
1671
+ ).replaceAll(
1672
+ "{{lifecycleIndependentContext}}",
1673
+ dialect.tools.lifecycle.independentContext
1674
+ ).replaceAll(
1675
+ "{{lifecycleSameAssignmentContinuation}}",
1676
+ dialect.tools.lifecycle.sameAssignmentContinuation
1744
1677
  ).replaceAll(
1745
1678
  "{{lifecycleTerminalState}}",
1746
1679
  dialect.tools.lifecycle.terminalState
@@ -14,7 +14,7 @@ import {
14
14
  getStepBudgetPromptSection,
15
15
  loadAgentPrompt,
16
16
  renderRolePrompt
17
- } from "./chunk-2TLT6WWY.js";
17
+ } from "./chunk-M2WF46BP.js";
18
18
 
19
19
  // src/agents/deep.ts
20
20
  var DEEP_PROMPT = renderRolePrompt(
package/dist/cli/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  getOpenCodeVersion,
5
5
  isOpenCodeInstalled,
6
6
  opencodeAdapter
7
- } from "../chunk-MY2GIOOH.js";
7
+ } from "../chunk-TKIDBRM3.js";
8
8
  import {
9
9
  CODEX_ROLE_NAMES,
10
10
  REQUIRED_SKILLS,
@@ -47,7 +47,7 @@ import {
47
47
  listOperationHarnesses,
48
48
  normalizeEffortSelection,
49
49
  syncOpenCodeOwnedSkills
50
- } from "../chunk-XEEYNMPC.js";
50
+ } from "../chunk-JBG7XMIG.js";
51
51
  import {
52
52
  ALL_AGENT_NAMES,
53
53
  detectCurrentConfig,
@@ -58,7 +58,7 @@ import {
58
58
  resolveExecutingPackageVersion,
59
59
  updateOpenCodeMainConfig,
60
60
  writeLiteConfig
61
- } from "../chunk-2TLT6WWY.js";
61
+ } from "../chunk-M2WF46BP.js";
62
62
 
63
63
  // src/cli/index.ts
64
64
  import { pathToFileURL } from "url";
@@ -2,8 +2,8 @@ import {
2
2
  defaultTuiOperations,
3
3
  effortChoicesForModel,
4
4
  listOperationHarnesses
5
- } from "../../chunk-XEEYNMPC.js";
6
- import "../../chunk-2TLT6WWY.js";
5
+ } from "../../chunk-JBG7XMIG.js";
6
+ import "../../chunk-M2WF46BP.js";
7
7
 
8
8
  // src/cli/tui/index.tsx
9
9
  import { render } from "ink";
@@ -311,7 +311,7 @@ export declare const SDD_PHASE_PROTOCOLS: readonly [{
311
311
  readonly id: "specify";
312
312
  readonly objective: "Create a testable, implementation-neutral feature contract in spec.md.";
313
313
  readonly requiredInputs: ["User intent and accepted scope", "Explore handoff when the route is full", "Project constitution and existing public contracts when relevant"];
314
- readonly instructions: ["Record intent and scope as Why, Impact, and Affected capabilities before prescribing any contract.", "Define prioritized independent stories and map each story to its FR-### and SC-### contracts with Covers metadata.", "Give every normative MUST or SHALL requirement a descriptive title and declare it INTERNAL or as an ADDED, MODIFIED, REMOVED, or RENAMED durable capability delta.", "Classify every success criterion as buildable or outcome; both remain measurable, but only buildable criteria require implementation task coverage.", "Capture assumptions, acceptance scenarios, edge cases, dependencies, and explicit non-goals.", "Use a clarification marker only when no safe default exists and the answer materially changes scope or behavior."];
314
+ readonly instructions: ["Record intent and scope as Why, Impact, and Affected capabilities before prescribing any contract.", "Define prioritized independent stories and map each story to its FR-### and SC-### contracts with Covers metadata.", "Inspect each affected canonical capability before choosing durable metadata: ADDED is only for genuinely new behavior; MODIFIED and REMOVED preserve exact existing titles; RENAMED names the exact previous title; only ADDED is valid when the capability is absent.", "Give every normative MUST or SHALL requirement a descriptive title and declare it INTERNAL or as an ADDED, MODIFIED, REMOVED, or RENAMED durable capability delta; treat an ADDED warning on an existing capability as a required semantic-overlap review.", "Classify every success criterion as buildable or outcome; both remain measurable, but only buildable criteria require implementation task coverage.", "Capture assumptions, acceptance scenarios, edge cases, dependencies, and explicit non-goals.", "Use a clarification marker only when no safe default exists and the answer materially changes scope or behavior."];
315
315
  readonly allowedWrites: ["openspec/changes/<feature>/spec.md"];
316
316
  readonly outputSchema: ["spec.md path", "requirements summary", "open clarifications"];
317
317
  readonly doneWhen: ["Every accepted FR and SC is testable, materially unambiguous, mapped to a story, and implementation-neutral."];
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  renderOpenCodeAgentConfigs,
4
4
  spawn,
5
5
  spawnSync
6
- } from "./chunk-MY2GIOOH.js";
6
+ } from "./chunk-TKIDBRM3.js";
7
7
  import {
8
8
  POLL_INTERVAL_BACKGROUND_MS,
9
9
  context7,
@@ -12,7 +12,7 @@ import {
12
12
  grep_app,
13
13
  loadPluginConfig,
14
14
  stripJsonComments
15
- } from "./chunk-2TLT6WWY.js";
15
+ } from "./chunk-M2WF46BP.js";
16
16
 
17
17
  // src/index.ts
18
18
  import { fileURLToPath as fileURLToPath3 } from "url";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Adaptive multi-harness orchestration with seven roles, runtime-autonomous Spec Kit-compatible SDD routes, and external provider boundaries.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -34,7 +34,7 @@
34
34
  {
35
35
  "path": "agents/orchestrator.md",
36
36
  "kind": "agent-config",
37
- "sha256": "sha256:b39b6cb996d5dcb38dc66bb83bfd7d6dacbb75d9e1de9097ce2416e64d630b9e"
37
+ "sha256": "sha256:9a1a9de425e7499110e457b58481d953391d07841dc319f0f8c7b09d64fa8998"
38
38
  },
39
39
  {
40
40
  "path": "agents/quick.md",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Adaptive multi-harness agent pack with seven roles and a runtime-autonomous Spec Kit-compatible SDD bundle for Claude Code.",
5
5
  "author": {
6
6
  "name": "thoth-agents"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thoth-agents",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Bundled Spec Kit-compatible SDD skills and MCP configuration; the seven-role global Codex layer requires thoth-agents CLI setup.",
5
5
  "skills": "./skills/",
6
6
  "mcpServers": "./codex.mcp.json"
@@ -13,10 +13,19 @@ You are the adaptive root for thoth-agents. Keep requirements, decisions, execut
13
13
  - Delegate only for net gain from specialization, context isolation, review, or safe parallelism. The maximum delegation depth is 1; children never delegate.
14
14
  - Maintain one writer for each mutable surface. Parallelize only independent work with no overlapping writes.
15
15
  - Keep prompts bounded; request distilled evidence, not raw logs or full files.
16
+ - Preserve unrelated changes; report changed files, evidence, risks, and capability gaps.
16
17
  - Use `AskUserQuestion` only when a material unresolved choice changes the result. Continue all safe non-blocked work first.
17
18
  - Use `TodoWrite` only when the work genuinely has multiple dependent steps.
18
19
  </operating-model>
19
20
 
21
+ <delegation-lifecycle>
22
+ - A new objective, SDD phase, mutable surface, or independent judgment is a work boundary: start a fresh specialist using a normal `Agent` invocation. Never treat completed agents as a reusable role pool.
23
+ - Independent context: do not use `fork` for independent work.
24
+ - Continue with `SendMessage` to the prior agent ID only to steer, complete, or clarify the same bounded assignment; never to cross a work boundary.
25
+ - TaskOutput on the same task session only collects the active nonterminal assignment and does not authorize later reuse.
26
+ - Every Oracle plan review, verification round, and approval or PASS judgment uses a fresh Oracle instance. An existing Oracle session may only clarify its current findings.
27
+ </delegation-lifecycle>
28
+
20
29
  <routing>
21
30
  - thoth-agents:explorer: Resolve broad or uncertain repository questions and return distilled evidence.
22
31
  - thoth-agents:librarian: Gather current authoritative evidence and separate documented facts from inference.
@@ -55,29 +64,20 @@ You are the adaptive root for thoth-agents. Keep requirements, decisions, execut
55
64
  </external-skills>
56
65
 
57
66
  <memory>
58
- - Load the installed `thoth-mem` skill for resume or prior work and provider-backed memory; never invent its protocol.
59
- - Preserve a durable decision, root cause, convention, or discovery only when reusable. Root owns the stable root session ID, project, lifecycle, real-user intent, and authorization; never invent identity or confirmed effects.
60
- - Follow the skill at verified compaction and a meaningful semantic boundary. Children receive only bounded MEMORY dispatch and never own root lifecycle.
61
- - `openspec/` remains canonical; do not mirror SDD phase artifacts into provider memory. A memory failure degrades memory only and does not block unrelated implementation or verification.
67
+ - Load the installed `thoth-mem` skill for resume or prior work; never invent its protocol.
68
+ - Preserve a durable decision, root cause, convention, or discovery only when reusable. Root owns the stable root session ID, project, lifecycle, real-user intent, and authorization.
69
+ - Follow the skill at verified compaction or a meaningful semantic boundary; children receive only bounded MEMORY and never own root lifecycle.
70
+ - `openspec/` remains canonical; do not mirror SDD phase artifacts. A memory failure does not block unrelated work.
62
71
  </memory>
63
72
 
64
73
  <artifacts>
65
- - Preserve Spec Kit semantics inside openspec/changes/<feature>/.
66
- - Accelerated and Full require spec.md, plan.md, tasks.md, verify-report.md, and archive-report.md; optional when useful: checklists/requirements.md, research.md, data-model.md, contracts/, quickstart.md, plan-review.md.
67
- - Root owns openspec/ coordination and thoth-sdd gates; product work has one writer, and root alone moves [~] -> [x] after evidence.
68
- - thoth-agents:oracle returns read-only findings. Root persists verification; after PASS, archive syncs declared durable deltas and moves the change to openspec/changes/archive/YYYY-MM-DD-<feature>/.
74
+ - In openspec/changes/<feature>/, Accelerated and Full require spec.md, plan.md, tasks.md, verify-report.md, and archive-report.md.
75
+ - Root owns openspec/ gates and task state, moves [~] -> [x] after evidence, and keeps one product writer.
76
+ - thoth-agents:oracle returns read-only findings; root persists verification and archives declared durable deltas after PASS.
69
77
  </artifacts>
70
78
 
71
- <execution>
72
- - Validate contracts and tests first; use test-first work for behavior. Root or one writer implements; do not delegate merely because an agent exists.
73
- - thoth-agents:oracle owns selected plan review and every verification; root and writers never self-approve.
74
- - Preserve unrelated working-tree changes. Never instruct an agent to discard them.
75
- - Report changed files, evidence, risks, and capability gaps truthfully.
76
- </execution>
77
-
78
79
  <delegation>
79
- - Dispatch through `Agent` with this envelope; use the same boundaries for non-SDD work.
80
- - Parallelize only independent work and await requested results before synthesis.
80
+ - Use this envelope for all `Agent` delegation; parallelize only independent work and await results.
81
81
  - Child return fields: conclusion, evidence, verification, risks, openQuestions, nextAction.
82
82
 
83
83
  ## PHASE
@@ -31,6 +31,13 @@ The script reads delta metadata directly from `spec.md`:
31
31
  - `[RENAMED capability FROM Previous title]` renames and replaces it.
32
32
  - `[INTERNAL]` never changes `openspec/specs/`.
33
33
 
34
+ The SDD validator preflights these operations against the canonical requirement
35
+ titles from `specify` onward. Archive reuses the same ordered parser and
36
+ preflight as a final defense before staging writes, preserving stable
37
+ `SDD-SPEC-DELTA-*` incompatibility codes. An `ADDED` warning for an existing
38
+ nonempty capability requires semantic-overlap review because differently named
39
+ requirements cannot be proven distinct by exact-title tooling alone.
40
+
34
41
  All delta targets are validated before any permanent specification changes. The
35
42
  operation stages and rolls back the full canonical update if a delta, report
36
43
  update, or final move raises a handled error in the active process. Report and
@@ -10,8 +10,13 @@ import {
10
10
  writeFileSync,
11
11
  } from 'node:fs';
12
12
  import { basename, dirname, join, resolve } from 'node:path';
13
+ import {
14
+ CAPABILITY_PATTERN,
15
+ parseCanonicalSpec,
16
+ parseRequirementDelta as parseSharedRequirementDelta,
17
+ preflightRequirementDeltas,
18
+ } from '../../thoth-sdd/scripts/durable-deltas.mjs';
13
19
 
14
- const CAPABILITY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
20
  const TEST_FAULTS = new Set(
16
21
  (process.env.THOTH_ARCHIVE_TEST_FAULT ?? '')
17
22
  .split(',')
@@ -270,26 +275,8 @@ function assertArchivable(change, contract) {
270
275
  }
271
276
 
272
277
  function parseDeltaMetadata(metadata) {
273
- if (metadata === 'INTERNAL') return { operation: 'INTERNAL' };
274
-
275
- const change = /^(ADDED|MODIFIED|REMOVED)\s+(\S+)$/.exec(metadata);
276
- if (change && CAPABILITY_PATTERN.test(change[2])) {
277
- return { operation: change[1], capability: change[2] };
278
- }
279
-
280
- const rename = /^RENAMED\s+(\S+)\s+FROM\s+(.+)$/.exec(metadata);
281
- if (
282
- rename &&
283
- CAPABILITY_PATTERN.test(rename[1]) &&
284
- rename[2].trim().length > 0
285
- ) {
286
- return {
287
- operation: 'RENAMED',
288
- capability: rename[1],
289
- previousTitle: rename[2].trim(),
290
- };
291
- }
292
-
278
+ const delta = parseSharedRequirementDelta(metadata);
279
+ if (delta) return delta;
293
280
  throw new Error(`Invalid requirement delta metadata: [${metadata}]`);
294
281
  }
295
282
 
@@ -418,27 +405,6 @@ function parseChangeSpec(spec) {
418
405
  };
419
406
  }
420
407
 
421
- function parseCanonicalSpec(content) {
422
- const headings = [...content.matchAll(/^### Requirement:\s+(.+?)\s*$/gm)];
423
- const requirements = new Map();
424
- const prefix =
425
- headings.length === 0
426
- ? content.trimEnd()
427
- : content.slice(0, headings[0].index).trimEnd();
428
-
429
- for (const [index, heading] of headings.entries()) {
430
- const start = heading.index ?? 0;
431
- const end = headings[index + 1]?.index ?? content.length;
432
- const title = heading[1].trim();
433
- if (requirements.has(title)) {
434
- throw new Error(`Canonical specification repeats requirement: ${title}`);
435
- }
436
- requirements.set(title, content.slice(start, end).trim());
437
- }
438
-
439
- return { prefix, requirements };
440
- }
441
-
442
408
  function newCanonicalSpec(capability) {
443
409
  const title = capability
444
410
  .split('-')
@@ -480,41 +446,34 @@ function planCanonicalUpdates(specRoot, deltas) {
480
446
  for (const [capability, capabilityDeltas] of byCapability) {
481
447
  const path = join(specRoot, capability, 'spec.md');
482
448
  const present = existsSync(path);
483
- const canonical = present
484
- ? parseCanonicalSpec(readFileSync(path, 'utf8'))
485
- : newCanonicalSpec(capability);
449
+ let canonical;
450
+ try {
451
+ canonical = present
452
+ ? parseCanonicalSpec(readFileSync(path, 'utf8'))
453
+ : newCanonicalSpec(capability);
454
+ } catch (error) {
455
+ throw new Error(
456
+ `SDD-SPEC-DELTA-BASELINE: ${capability} canonical baseline is invalid: ${errorMessage(error)}`,
457
+ );
458
+ }
459
+ const preflight = preflightRequirementDeltas({
460
+ capability,
461
+ present,
462
+ requirements: canonical.requirements,
463
+ deltas: capabilityDeltas,
464
+ });
465
+ if (preflight.errors.length > 0) {
466
+ const [first] = preflight.errors;
467
+ throw new Error(`${first.code}: ${first.message}`);
468
+ }
486
469
 
487
470
  for (const delta of capabilityDeltas) {
488
471
  if (delta.operation === 'ADDED') {
489
- if (canonical.requirements.has(delta.title)) {
490
- throw new Error(
491
- `${capability} already contains requirement: ${delta.title}`,
492
- );
493
- }
494
472
  canonical.requirements.set(delta.title, renderRequirement(delta));
495
473
  continue;
496
474
  }
497
475
 
498
- if (!present) {
499
- throw new Error(
500
- `${capability} has no canonical specification for ${delta.operation}`,
501
- );
502
- }
503
-
504
476
  if (delta.operation === 'RENAMED') {
505
- if (!canonical.requirements.has(delta.previousTitle)) {
506
- throw new Error(
507
- `${capability} does not contain requirement: ${delta.previousTitle}`,
508
- );
509
- }
510
- if (
511
- delta.previousTitle !== delta.title &&
512
- canonical.requirements.has(delta.title)
513
- ) {
514
- throw new Error(
515
- `${capability} already contains requirement: ${delta.title}`,
516
- );
517
- }
518
477
  const entries = [...canonical.requirements.entries()];
519
478
  canonical.requirements = new Map(
520
479
  entries.map(([title, block]) =>
@@ -526,11 +485,6 @@ function planCanonicalUpdates(specRoot, deltas) {
526
485
  continue;
527
486
  }
528
487
 
529
- if (!canonical.requirements.has(delta.title)) {
530
- throw new Error(
531
- `${capability} does not contain requirement: ${delta.title}`,
532
- );
533
- }
534
488
  if (delta.operation === 'MODIFIED') {
535
489
  canonical.requirements.set(delta.title, renderRequirement(delta));
536
490
  } else if (delta.operation === 'REMOVED') {
@@ -11,6 +11,16 @@ implementation-neutral.
11
11
  `None`).
12
12
  - Define prioritized, independently testable `US#` stories. Every story needs
13
13
  `Covers: FR-###, SC-###` and Given/When/Then acceptance scenarios.
14
+ - Before choosing durable delta metadata, read
15
+ `openspec/specs/<capability>/spec.md` when it exists and compare the exact
16
+ `### Requirement:` titles and normative behavior:
17
+ - use `ADDED` only when no canonical requirement already expresses the
18
+ behavior;
19
+ - use `MODIFIED` or `REMOVED` only with the exact existing title;
20
+ - use `RENAMED <capability> FROM <exact previous title>` when the title changes;
21
+ - only `ADDED` is valid when the canonical capability does not exist.
22
+ A validator warning on `ADDED` into an existing nonempty capability requires
23
+ semantic-overlap review; exact-title validity alone does not prove new intent.
14
24
  - Give every sequential FR a descriptive title, a normative `MUST`/`SHALL`
15
25
  statement, and exactly one delta marker: `INTERNAL`, `ADDED`, `MODIFIED`,
16
26
  `REMOVED`, or `RENAMED ... FROM ...`.
@@ -20,4 +30,5 @@ implementation-neutral.
20
30
  - State assumptions, dependencies, edge cases, and explicit non-goals.
21
31
 
22
32
  Do not advance while a material ambiguity would change scope, behavior,
23
- architecture, or durable delta intent.
33
+ architecture, or durable delta intent. Run the `specify` gate before planning;
34
+ do not defer canonical delta reconciliation until archive.