thoth-agents 0.3.10 → 0.3.12

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.
Files changed (52) hide show
  1. package/README.md +119 -33
  2. package/dist/{chunk-TKIDBRM3.js → chunk-5UBJB5QO.js} +4 -1
  3. package/dist/{chunk-M2WF46BP.js → chunk-DX4U3AD3.js} +387 -231
  4. package/dist/{chunk-JBG7XMIG.js → chunk-VPVBYBPU.js} +602 -180
  5. package/dist/cli/codex-plugin-install.d.ts +16 -2
  6. package/dist/cli/index.js +7 -5
  7. package/dist/cli/tui/index.js +2 -2
  8. package/dist/config/constants.d.ts +3 -3
  9. package/dist/harness/adapters/claude-code.d.ts +8 -0
  10. package/dist/harness/adapters/codex-surfaces.d.ts +2 -2
  11. package/dist/harness/core/agent-pack.d.ts +60 -5
  12. package/dist/harness/core/sdd.d.ts +32 -19
  13. package/dist/harness/writers/codex-plugin-package.d.ts +1 -0
  14. package/dist/index.js +2 -2
  15. package/package.json +4 -4
  16. package/plugin/.claude-plugin/.thoth-agents-plugin-assets.json +7 -7
  17. package/plugin/.claude-plugin/plugin.json +1 -1
  18. package/plugin/.codex-plugin/plugin.json +1 -1
  19. package/plugin/agents/deep.md +10 -31
  20. package/plugin/agents/designer.md +10 -31
  21. package/plugin/agents/explorer.md +10 -30
  22. package/plugin/agents/librarian.md +10 -30
  23. package/plugin/agents/oracle.md +11 -31
  24. package/plugin/agents/orchestrator.md +48 -35
  25. package/plugin/agents/quick.md +10 -31
  26. package/plugin/skills/plan-reviewer/SKILL.md +54 -15
  27. package/plugin/skills/thoth-archive/SKILL.md +5 -0
  28. package/plugin/skills/thoth-archive/scripts/archive.mjs +30 -10
  29. package/plugin/skills/thoth-constitution/SKILL.md +5 -0
  30. package/plugin/skills/thoth-constitution/templates/constitution.md +19 -2
  31. package/plugin/skills/thoth-init/SKILL.md +5 -0
  32. package/plugin/skills/thoth-sdd/SKILL.md +48 -11
  33. package/plugin/skills/thoth-sdd/references/phases/implement.md +44 -6
  34. package/plugin/skills/thoth-sdd/references/phases/tasks.md +23 -2
  35. package/plugin/skills/thoth-sdd/references/phases/verify.md +7 -5
  36. package/plugin/skills/thoth-sdd/scripts/validate.mjs +263 -30
  37. package/plugin/skills/thoth-sdd/templates/tasks.md +18 -2
  38. package/skills/README.md +2 -1
  39. package/skills/plan-reviewer/SKILL.md +54 -15
  40. package/skills/thoth-archive/SKILL.md +5 -0
  41. package/skills/thoth-archive/scripts/archive.mjs +30 -10
  42. package/skills/thoth-constitution/SKILL.md +5 -0
  43. package/skills/thoth-constitution/templates/constitution.md +19 -2
  44. package/skills/thoth-init/SKILL.md +5 -0
  45. package/skills/thoth-sdd/SKILL.md +48 -11
  46. package/skills/thoth-sdd/references/phases/implement.md +44 -6
  47. package/skills/thoth-sdd/references/phases/tasks.md +23 -2
  48. package/skills/thoth-sdd/references/phases/verify.md +7 -5
  49. package/skills/thoth-sdd/scripts/validate.mjs +263 -30
  50. package/skills/thoth-sdd/templates/tasks.md +18 -2
  51. package/.agents/plugins/marketplace.json +0 -20
  52. package/.claude-plugin/marketplace.json +0 -21
@@ -540,6 +540,97 @@ function exactTaskPath(line) {
540
540
  return normalized.toLowerCase();
541
541
  }
542
542
 
543
+ function taskDependencyEdges(content) {
544
+ return [...content.matchAll(/\bT\d{3}(?:\s*->\s*T\d{3})+\b/g)].flatMap(
545
+ ([chain]) => {
546
+ const ids = chain.match(/\bT\d{3}\b/g) ?? [];
547
+ return ids.slice(1).map((target, index) => ({
548
+ source: ids[index],
549
+ target,
550
+ }));
551
+ },
552
+ );
553
+ }
554
+
555
+ function parseParallelGroups(content) {
556
+ const lines = content.split(/\r?\n/);
557
+ const headingIndexes = lines
558
+ .map((line, index) => (/^###\s+Group\s+P\d+\s*$/.test(line) ? index : -1))
559
+ .filter((index) => index >= 0);
560
+ const groups = [];
561
+ let valid = headingIndexes.length > 0;
562
+
563
+ if (
564
+ lines
565
+ .slice(0, headingIndexes[0] ?? lines.length)
566
+ .some((line) => line.trim())
567
+ ) {
568
+ valid = false;
569
+ }
570
+
571
+ for (const [groupIndex, start] of headingIndexes.entries()) {
572
+ const end = headingIndexes[groupIndex + 1] ?? lines.length;
573
+ const groupId = /^###\s+Group\s+P(\d+)\s*$/.exec(lines[start])?.[1];
574
+ const body = lines.slice(start + 1, end).filter((line) => line.trim());
575
+ const lanes = [];
576
+ let prerequisites;
577
+ let barrier;
578
+ let rationale;
579
+ for (const line of body) {
580
+ const lane =
581
+ /^- Lane L(\d+): (T\d{3}(?: -> T\d{3})*)(?: \| Owner: (\S+))?\s*$/.exec(
582
+ line,
583
+ );
584
+ if (lane) {
585
+ lanes.push({
586
+ number: Number.parseInt(lane[1], 10),
587
+ taskIds: lane[2].split(' -> '),
588
+ owner: lane[3],
589
+ });
590
+ continue;
591
+ }
592
+ const prerequisiteLine =
593
+ /^- Prerequisites: (None|T\d{3}(?:, T\d{3})*)\s*$/.exec(line);
594
+ if (prerequisiteLine && prerequisites === undefined) {
595
+ prerequisites =
596
+ prerequisiteLine[1] === 'None' ? [] : prerequisiteLine[1].split(', ');
597
+ continue;
598
+ }
599
+ const barrierLine = /^- Barrier: (Final verification|T\d{3})\s*$/.exec(
600
+ line,
601
+ );
602
+ if (barrierLine && barrier === undefined) {
603
+ barrier = barrierLine[1];
604
+ continue;
605
+ }
606
+ const rationaleLine = /^- Rationale: (\S.*)$/.exec(line);
607
+ if (rationaleLine && rationale === undefined) {
608
+ rationale = rationaleLine[1];
609
+ continue;
610
+ }
611
+ valid = false;
612
+ }
613
+ if (
614
+ groupId === undefined ||
615
+ lanes.length < 2 ||
616
+ rationale === undefined ||
617
+ !isConcreteEvidence(rationale) ||
618
+ lanes.some((lane, index) => lane.number !== index + 1)
619
+ ) {
620
+ valid = false;
621
+ }
622
+ groups.push({
623
+ number: Number.parseInt(groupId ?? '0', 10),
624
+ lanes,
625
+ prerequisites,
626
+ barrier,
627
+ rationale,
628
+ });
629
+ }
630
+ if (groups.some((group, index) => group.number !== index + 1)) valid = false;
631
+ return { groups, valid };
632
+ }
633
+
543
634
  function validateTasks(content, errors, spec) {
544
635
  const artifact = 'tasks.md';
545
636
  const taskLines = content
@@ -644,12 +735,6 @@ function validateTasks(content, errors, spec) {
644
735
  }));
645
736
  const tasksById = new Map(taskRecords.map((task) => [task.id, task]));
646
737
  const hasParallelTask = taskRecords.some((task) => task.parallel);
647
- const parallelLines = (parallelSection ?? '')
648
- .split(/\r?\n/)
649
- .map((line) => [line, [...new Set(line.match(/\bT\d{3}\b/g) ?? [])]])
650
- .filter(([, ids]) => ids.length >= 2);
651
- const referencedTaskIds = new Set(parallelLines.flatMap(([, ids]) => ids));
652
- const hasParallelExample = parallelLines.length > 0;
653
738
  const noParallelMatch = /^-\s+None:\s*(\S.*)$/im.exec(parallelSection ?? '');
654
739
  const explainsNoParallelWork =
655
740
  noParallelMatch !== null && isConcreteEvidence(noParallelMatch[1]);
@@ -657,42 +742,190 @@ function validateTasks(content, errors, spec) {
657
742
  left === right ||
658
743
  left.startsWith(`${right}/`) ||
659
744
  right.startsWith(`${left}/`);
660
- const unknownReferences = [...referencedTaskIds].filter(
661
- (id) => !tasksById.has(id),
745
+ const noneOnly =
746
+ explainsNoParallelWork &&
747
+ parallelSection?.trim() === noParallelMatch?.[0].trim();
748
+ const { groups, valid: validGroupGrammar } = parseParallelGroups(
749
+ parallelSection ?? '',
662
750
  );
663
- const omittedParallelTasks = taskRecords
664
- .filter((task) => task.parallel && !referencedTaskIds.has(task.id))
665
- .map((task) => task.id);
666
- const examplesWithoutParallelTask = parallelLines.some(
667
- ([, ids]) => !ids.some((id) => tasksById.get(id)?.parallel),
751
+ const declaredDependencies = taskDependencyEdges(
752
+ sectionContent(content, 'Dependencies') ?? '',
668
753
  );
669
- const overlappingExamples = parallelLines.some(([, ids]) => {
670
- const paths = ids.map((id) => tasksById.get(id)?.path);
671
- if (paths.some((path) => path === undefined)) return true;
672
- return paths.some((path, index) =>
673
- paths.slice(index + 1).some((other) => pathsOverlap(path, other)),
674
- );
675
- });
754
+ const hasGroups = groups.length > 0;
676
755
  if (
677
756
  parallelSection === undefined ||
678
- (explainsNoParallelWork && hasParallelExample) ||
679
- (hasParallelTask &&
680
- (!hasParallelExample ||
681
- explainsNoParallelWork ||
682
- unknownReferences.length > 0 ||
683
- omittedParallelTasks.length > 0 ||
684
- examplesWithoutParallelTask ||
685
- overlappingExamples)) ||
686
- (!hasParallelTask && !explainsNoParallelWork)
757
+ (hasParallelTask && (!hasGroups || explainsNoParallelWork)) ||
758
+ (!hasParallelTask && !noneOnly) ||
759
+ (explainsNoParallelWork && !noneOnly)
687
760
  ) {
688
761
  errors.push(
689
762
  issue(
690
763
  'SDD-TASK-PARALLEL',
691
764
  artifact,
692
- 'Document existing [P] tasks with non-overlapping paths, or explain why no safe parallel work exists.',
765
+ 'Declare strict parallel groups for [P] tasks, or provide one exclusive evidence-backed None reason.',
766
+ ),
767
+ );
768
+ }
769
+ if (hasGroups && !validGroupGrammar) {
770
+ errors.push(
771
+ issue(
772
+ 'SDD-TASK-LANE-GRAMMAR',
773
+ artifact,
774
+ 'Groups and lanes must use unique sequential IDs, at least two lanes, and every required field.',
693
775
  ),
694
776
  );
695
777
  }
778
+
779
+ if (hasGroups && validGroupGrammar) {
780
+ const memberships = groups.flatMap((group) =>
781
+ group.lanes.flatMap((lane) =>
782
+ lane.taskIds.map((taskId) => ({ group, lane, taskId })),
783
+ ),
784
+ );
785
+ const membershipCounts = new Map();
786
+ for (const { taskId } of memberships) {
787
+ membershipCounts.set(taskId, (membershipCounts.get(taskId) ?? 0) + 1);
788
+ }
789
+ const invalidMembership =
790
+ memberships.some(({ taskId }) => !tasksById.get(taskId)?.parallel) ||
791
+ taskRecords.some(
792
+ (task) => task.parallel && membershipCounts.get(task.id) !== 1,
793
+ );
794
+ if (invalidMembership) {
795
+ errors.push(
796
+ issue(
797
+ 'SDD-TASK-LANE-MEMBERSHIP',
798
+ artifact,
799
+ 'Every [P] task must belong to exactly one lane, and lane members must be known [P] tasks.',
800
+ ),
801
+ );
802
+ }
803
+
804
+ if (
805
+ groups.some((group) =>
806
+ group.lanes.some(
807
+ (lane) => !['designer', 'quick', 'deep'].includes(lane.owner),
808
+ ),
809
+ )
810
+ ) {
811
+ errors.push(
812
+ issue(
813
+ 'SDD-TASK-LANE-OWNER',
814
+ artifact,
815
+ 'Every lane owner must be designer, quick, or deep.',
816
+ ),
817
+ );
818
+ }
819
+
820
+ for (const group of groups) {
821
+ const rationale = group.rationale ?? '';
822
+ const hasPathIndependenceEvidence =
823
+ /\b(?:path|file|surface)s?\b/i.test(rationale) &&
824
+ /\b(?:disjoint|non[- ]?overlap(?:ping)?)\b/i.test(rationale);
825
+ const hasDependencyIndependenceEvidence =
826
+ /\b(?:dependenc(?:y|ies)|consum(?:e|es|ing)|peer output|data flow)\b/i.test(
827
+ rationale,
828
+ );
829
+ if (!hasPathIndependenceEvidence || !hasDependencyIndependenceEvidence) {
830
+ errors.push(
831
+ issue(
832
+ 'SDD-TASK-LANE-RATIONALE',
833
+ artifact,
834
+ 'Parallel rationale must state concrete path-disjointness and cross-lane dependency evidence for Oracle review.',
835
+ ),
836
+ );
837
+ }
838
+
839
+ const memberIds = new Set(group.lanes.flatMap((lane) => lane.taskIds));
840
+ const prerequisites = group.prerequisites ?? [];
841
+ if (
842
+ group.prerequisites === undefined ||
843
+ new Set(prerequisites).size !== prerequisites.length ||
844
+ prerequisites.some(
845
+ (taskId) => !tasksById.has(taskId) || memberIds.has(taskId),
846
+ )
847
+ ) {
848
+ errors.push(
849
+ issue(
850
+ 'SDD-TASK-LANE-PREREQUISITE',
851
+ artifact,
852
+ 'Prerequisites must be unique known tasks outside the group.',
853
+ ),
854
+ );
855
+ }
856
+
857
+ const memberNumbers = [...memberIds].map((id) =>
858
+ Number.parseInt(id.slice(1), 10),
859
+ );
860
+ const barrierNumber = /^T\d{3}$/.test(group.barrier ?? '')
861
+ ? Number.parseInt(group.barrier.slice(1), 10)
862
+ : undefined;
863
+ if (
864
+ group.barrier !== 'Final verification' &&
865
+ (barrierNumber === undefined ||
866
+ !tasksById.has(group.barrier) ||
867
+ memberIds.has(group.barrier) ||
868
+ prerequisites.includes(group.barrier) ||
869
+ memberNumbers.some((number) => barrierNumber <= number))
870
+ ) {
871
+ errors.push(
872
+ issue(
873
+ 'SDD-TASK-LANE-BARRIER',
874
+ artifact,
875
+ 'A task barrier must be a known downstream non-member task, or use exact Final verification.',
876
+ ),
877
+ );
878
+ }
879
+
880
+ const laneByTask = new Map(
881
+ group.lanes.flatMap((lane) =>
882
+ lane.taskIds.map((taskId) => [taskId, lane.number]),
883
+ ),
884
+ );
885
+ if (
886
+ declaredDependencies.some(
887
+ ({ source, target }) =>
888
+ laneByTask.has(source) &&
889
+ laneByTask.has(target) &&
890
+ laneByTask.get(source) !== laneByTask.get(target),
891
+ )
892
+ ) {
893
+ errors.push(
894
+ issue(
895
+ 'SDD-TASK-LANE-DEPENDENCY',
896
+ artifact,
897
+ 'Declared dependencies cannot connect different lanes in one group.',
898
+ ),
899
+ );
900
+ }
901
+
902
+ const lanePaths = group.lanes.map((lane) =>
903
+ lane.taskIds.map((taskId) => tasksById.get(taskId)?.path),
904
+ );
905
+ const overlaps = lanePaths.some((paths, index) =>
906
+ lanePaths
907
+ .slice(index + 1)
908
+ .some((otherPaths) =>
909
+ paths.some(
910
+ (path) =>
911
+ path !== undefined &&
912
+ otherPaths.some(
913
+ (other) => other !== undefined && pathsOverlap(path, other),
914
+ ),
915
+ ),
916
+ ),
917
+ );
918
+ if (overlaps) {
919
+ errors.push(
920
+ issue(
921
+ 'SDD-TASK-LANE-OVERLAP',
922
+ artifact,
923
+ 'Mutable task paths cannot overlap across lanes in one group.',
924
+ ),
925
+ );
926
+ }
927
+ }
928
+ }
696
929
  }
697
930
 
698
931
  function validateChecklist(content, errors, spec, requireComplete) {
@@ -32,8 +32,24 @@ for them.
32
32
 
33
33
  ## Parallel execution
34
34
 
35
- [Replace the anchor with exactly one form: concrete task pairings covering every
36
- `[P]` task, or `- None: <evidence-backed reason>`. Never keep both forms.]
35
+ Use this exact grammar when safe parallel work exists:
36
+
37
+ ### Group P1
38
+
39
+ - Lane L1: T001 -> T002 | Owner: deep
40
+ - Lane L2: T003 -> T004 | Owner: quick
41
+ - Prerequisites: None
42
+ - Barrier: Final verification
43
+ - Rationale: Both lane path sets are disjoint and neither lane consumes peer output.
44
+
45
+ Groups and lanes are sequential and unique. Every `[P]` task belongs to exactly one lane; non-`[P]` tasks do not. Lane surfaces are the union of exact task
46
+ paths, and lanes in a group have disjoint surfaces and no cross-lane dependency.
47
+ The `Rationale` must explicitly state path-disjointness and cross-lane dependency
48
+ evidence so structural validation can gate the evidence shape and Oracle can
49
+ judge whether the claim is true.
50
+ Prerequisites name known tasks outside the group; the barrier is a downstream
51
+ task or `Final verification` after all lane members. If no group exists, use
52
+ only `- None: <evidence-backed reason>` and no task may use `[P]`.
37
53
 
38
54
  <!-- PARALLEL-EXECUTION-EVIDENCE -->
39
55
 
package/skills/README.md CHANGED
@@ -7,7 +7,8 @@ the same packaged skill trees globally under `~/.config/opencode/skills/`.
7
7
 
8
8
  Owned workflow skills are `thoth-init`, `thoth-sdd`, `thoth-constitution`,
9
9
  `thoth-archive`, and `plan-reviewer`. The last one implements the optional,
10
- user-selected, read-only Oracle review before implementation. Mandatory external
10
+ explicitly or bounded-default selected, read-only Oracle review before
11
+ implementation. Mandatory external
11
12
  skills (`simplify`, `tdd`,
12
13
  `progressive-context-router`, and `architectural-grilling`) are not copied here:
13
14
  the thoth-agents installer invokes `npx skills add` against their canonical
@@ -1,9 +1,11 @@
1
1
  ---
2
2
  name: plan-reviewer
3
- description: Review a ready Accelerated or Full SDD artifact set for execution blockers after the user chooses the optional Oracle plan review. Use only for the pre-implementation plan-review phase; return exact [OKAY] or [REJECT] semantics while keeping final verification separate.
3
+ description: Review a ready Accelerated or Full SDD artifact set for execution blockers after Oracle review is explicitly or recommended-default selected. Use only for the pre-implementation plan-review phase; return exact [OKAY] or [REJECT] semantics while keeping final verification separate.
4
+ license: MIT
5
+ compatibility: Designed for thoth-agents Accelerated and Full SDD workflows.
4
6
  metadata:
5
7
  author: thoth-agents
6
- version: '1.0'
8
+ version: "1.0"
7
9
  ---
8
10
 
9
11
  # Plan Reviewer
@@ -17,12 +19,16 @@ Use this skill only after all of these conditions hold:
17
19
 
18
20
  - The selected route is Accelerated or Full.
19
21
  - The route's `ready` validator gate has passed.
20
- - The user chose `Review plan with Oracle (Recommended)` instead of
21
- `Proceed without review`.
22
+ - `Review plan with Oracle (Recommended)` was explicitly selected or selected
23
+ by the bounded no-answer fallback.
22
24
 
23
- Do not activate automatically, and do not use it for Direct work. The user may
24
- proceed without this review. Skipping it means there is no pre-implementation
25
- Oracle review; mandatory final Oracle verify still applies.
25
+ At the ready gate, any explicit `Proceed without review` answer wins. When the
26
+ native question returns answerless, Root
27
+ makes at most three total attempts; after the third answerless result, Root
28
+ treats `Review plan with Oracle (Recommended)` as selected. This bounded
29
+ fallback is the only automatic activation. Do not use this skill for Direct
30
+ work. Explicitly proceeding without review means there is no pre-implementation
31
+ Oracle review; mandatory final verification still applies.
26
32
 
27
33
  ## Ownership and inputs
28
34
 
@@ -61,6 +67,29 @@ Judge the artifact set on five dimensions:
61
67
  Respect TDD ordering and the repository constitution when they apply. Report
62
68
  non-blocking cautions separately so they do not become artificial gates.
63
69
 
70
+ ## Native parallel executability
71
+
72
+ Structural ready validation proves that declared parallel metadata is
73
+ well-formed; it does not prove semantic independence. When `tasks.md` declares
74
+ parallel groups, Oracle must independently assess whether the plan can execute
75
+ them safely:
76
+
77
+ - Confirm each lane path union is genuinely disjoint from the other lanes and
78
+ its selected owner fits the bounded work.
79
+ - Confirm cross-lane data flow does not hide a dependency and that the declared
80
+ prerequisites and barrier reflect the real execution order.
81
+ - Confirm the handoff is sufficient for native capacity-bounded waves:
82
+ dispatch-before-wait for every admitted lane, refill before another wait, and
83
+ terminal reconciliation before releasing the barrier.
84
+ - Require a truthful sequential fallback when native concurrency or capacity is
85
+ unavailable or unproven.
86
+
87
+ For an evidence-backed `None` declaration, assess whether the claimed coupling
88
+ really prevents a safe group. Treat a structural-but-semantically-unsafe group
89
+ as a blocker only when the smallest repair is required before implementation;
90
+ keep capacity uncertainty as a non-blocking caution when the declared fallback
91
+ still makes the plan executable.
92
+
64
93
  ## Decision contract
65
94
 
66
95
  Default to `[OKAY]`. Return `[REJECT]` only when execution is genuinely blocked.
@@ -68,8 +97,10 @@ A rejection contains at most 3 actionable blockers. For each blocker, state why
68
97
  it blocks execution and the smallest concrete repair. Preserve the exact
69
98
  `[OKAY]` and `[REJECT]` tokens across every harness.
70
99
 
71
- After `[REJECT]`, Root repairs the canonical artifacts and offers the user the
72
- review-or-proceed choice again. Do not silently approve a changed plan.
100
+ After `[REJECT]`, Root repairs actionable same-intent planning artifacts,
101
+ revalidates affected gates, and starts a fresh Oracle approval round until
102
+ `[OKAY]`. A material human-owned blocker stops the loop for user input. Do not
103
+ silently approve a changed plan or reuse an Oracle to issue a new approval.
73
104
 
74
105
  ## Freshness and persistence
75
106
 
@@ -84,12 +115,20 @@ digests. Oracle returns this payload; Root is the only writer that persists it.
84
115
 
85
116
  ## Handoff
86
117
 
87
- After `[OKAY]`, Root summarizes the approved plan and must ask whether to implement or stop.
88
- Approval is not implementation authorization. Choosing
89
- `Proceed without review` at the earlier choice does authorize implementation.
90
-
91
- Plan review never replaces mandatory final Oracle verify. Every route still
92
- requires independent post-implementation verification by a non-writer.
118
+ After `[OKAY]`, Root summarizes the approved scope, approach, ownership,
119
+ verification, and material risks before asking the native question:
120
+ `Implement (Recommended)` or `Stop`. Any explicit `Stop` answer wins. If the
121
+ implementation question returns answerless, Root makes at most three total
122
+ attempts; after the third answerless result, Root treats implementation as
123
+ selected. `[OKAY]` alone does not authorize implementation before that explicit
124
+ choice or bounded fallback resolves. Choosing `Proceed without review` at the
125
+ earlier choice does authorize implementation.
126
+
127
+ Plan review never replaces or substitutes for mandatory final verification.
128
+ Every route still requires post-implementation verification, but ownership is
129
+ proportional: trivial deterministic Direct work may be Root-verified when Root
130
+ is not self-approving its own implementation; materially risky Direct work and
131
+ every Accelerated or Full final verify require a fresh read-only Oracle.
93
132
 
94
133
  ## Output
95
134
 
@@ -1,6 +1,11 @@
1
1
  ---
2
2
  name: thoth-archive
3
3
  description: Close a passing artifact-backed thoth-agents change by transactionally synchronizing explicitly declared durable requirement deltas and moving the complete audit trail to a dated archive.
4
+ license: MIT
5
+ compatibility: Requires Node.js >=22.13 and write access to the project OpenSpec tree.
6
+ metadata:
7
+ author: thoth-agents
8
+ version: "1.0"
4
9
  ---
5
10
 
6
11
  # Thoth Archive
@@ -300,16 +300,36 @@ function parseStoryScenarios(spec) {
300
300
  const covers =
301
301
  /^\*\*Covers\*\*:\s*(.+)$/im.exec(story)?.[1].match(/\bFR-\d{3}\b/g) ??
302
302
  [];
303
- const scenarios = [
304
- ...story.matchAll(
305
- /^\d+\.\s+\*\*Given\*\*\s+(.+?),\s+\*\*When\*\*\s+(.+?),\s+\*\*Then\*\*\s+(.+?)\.?\s*$/gim,
306
- ),
307
- ].map((scenario, scenarioIndex) => ({
308
- title: `US${storyMatch[1]} - ${storyMatch[2].trim()} ${scenarioIndex + 1}`,
309
- given: scenario[1].trim(),
310
- when: scenario[2].trim(),
311
- result: scenario[3].trim(),
312
- }));
303
+ const acceptanceMarker = /^\*\*Acceptance scenarios\*\*:\s*$/im.exec(story);
304
+ const acceptance = acceptanceMarker
305
+ ? story.slice((acceptanceMarker.index ?? 0) + acceptanceMarker[0].length)
306
+ : '';
307
+ const scenarioStarts = [
308
+ ...acceptance.matchAll(/^\d+\.\s+\*\*Given\*\*/gim),
309
+ ];
310
+ const scenarios = scenarioStarts.map((scenarioStart, scenarioIndex) => {
311
+ const start = scenarioStart.index ?? 0;
312
+ const end = scenarioStarts[scenarioIndex + 1]?.index ?? acceptance.length;
313
+ const normalized = acceptance
314
+ .slice(start, end)
315
+ .replace(/\s+/g, ' ')
316
+ .trim();
317
+ const scenario =
318
+ /^\d+\.\s+\*\*Given\*\*\s+(.+?),\s+\*\*When\*\*\s+(.+?),\s+\*\*Then\*\*\s+(.+?)\.?\s*$/i.exec(
319
+ normalized,
320
+ );
321
+ if (!scenario) {
322
+ throw new Error(
323
+ `US${storyMatch[1]} acceptance scenario ${scenarioIndex + 1} must use Given, When, and Then`,
324
+ );
325
+ }
326
+ return {
327
+ title: `US${storyMatch[1]} - ${storyMatch[2].trim()} ${scenarioIndex + 1}`,
328
+ given: scenario[1].trim(),
329
+ when: scenario[2].trim(),
330
+ result: scenario[3].trim(),
331
+ };
332
+ });
313
333
 
314
334
  for (const requirementId of covers) {
315
335
  const current = scenariosByRequirement.get(requirementId) ?? [];
@@ -1,6 +1,11 @@
1
1
  ---
2
2
  name: thoth-constitution
3
3
  description: Create or explicitly amend the project constitution, choose its governance SemVer bump, propagate its impact, and validate lifecycle metadata. Use only for constitution lifecycle work; routine SDD planning reads the constitution without activating this skill.
4
+ license: MIT
5
+ compatibility: Requires Node.js >=22.13 for bundled validation scripts.
6
+ metadata:
7
+ author: thoth-agents
8
+ version: "1.0"
4
9
  ---
5
10
 
6
11
  # Thoth Constitution
@@ -24,7 +24,11 @@ infrastructure without a current requirement is prohibited.
24
24
 
25
25
  Delivery MUST use the smallest coherent design that satisfies accepted
26
26
  requirements. Non-goals MUST be named, and scope cannot expand silently.
27
- SDD route selection MUST remain user-owned after an evidence-based recommendation.
27
+ Before the SDD route question, Root MUST summarize relevant context, scope,
28
+ clarity, risk, and its evidence-based recommendation. Any explicit answer wins.
29
+ When the native question returns answerless, Root MUST make at most three total
30
+ attempts; after the third answerless result, the recommended route counts as
31
+ selected.
28
32
 
29
33
  ### III. Testable contracts
30
34
 
@@ -33,7 +37,20 @@ changes MUST use test-first execution at an agreed public seam when practical.
33
37
 
34
38
  ### IV. Independent assurance
35
39
 
36
- The implementation writer cannot approve its own work. Pre-implementation plan review is optional and user-selected. When selected, it uses an independent read-only reviewer. Every final verify MUST use an independent read-only reviewer.
40
+ Every route MUST include verification proportional to the changed behavior and
41
+ risk. The implementation writer cannot approve its own work. Trivial
42
+ deterministic Direct work MAY be verified by Root when the decision is bounded
43
+ and independent of the implementation writer. Materially risky Direct work and
44
+ every Accelerated or Full final verify MUST use a fresh independent read-only
45
+ reviewer. Pre-implementation plan review is optional and is selected explicitly
46
+ or by the bounded recommended fallback; when offered, any explicit answer wins.
47
+ After the third answerless result,
48
+ `Review plan with Oracle (Recommended)` counts as selected. Actionable review
49
+ rejections MUST be repaired and revalidated before a fresh reviewer round until
50
+ approval or a material human-owned blocker. After approval, Root MUST give an
51
+ approved plan summary before asking `Implement (Recommended)` or `Stop`; any
52
+ explicit answer wins, while the third answerless result selects implementation.
53
+ Plan review never substitutes for final verification.
37
54
 
38
55
  ### V. Traceable delivery
39
56
 
@@ -1,6 +1,11 @@
1
1
  ---
2
2
  name: thoth-init
3
3
  description: Initialize or synchronize the minimum project OpenSpec governance structure required by thoth-agents SDD flows.
4
+ license: MIT
5
+ compatibility: Requires Node.js >=22.13 and write access to the target project.
6
+ metadata:
7
+ author: thoth-agents
8
+ version: "1.0"
4
9
  ---
5
10
 
6
11
  # Thoth Init