ariadna 1.3.0 → 2.0.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.
- checksums.yaml +4 -4
- data/ariadna.gemspec +0 -1
- data/data/agents/ariadna-codebase-mapper.md +34 -722
- data/data/agents/ariadna-debugger.md +44 -1139
- data/data/agents/ariadna-executor.md +75 -396
- data/data/agents/ariadna-planner.md +78 -1215
- data/data/agents/ariadna-roadmapper.md +55 -582
- data/data/agents/ariadna-verifier.md +60 -702
- data/data/ariadna/templates/config.json +8 -33
- data/data/ariadna/workflows/debug.md +28 -0
- data/data/ariadna/workflows/execute-phase.md +31 -513
- data/data/ariadna/workflows/map-codebase.md +20 -319
- data/data/ariadna/workflows/new-milestone.md +20 -365
- data/data/ariadna/workflows/new-project.md +19 -880
- data/data/ariadna/workflows/plan-phase.md +24 -443
- data/data/ariadna/workflows/progress.md +20 -376
- data/data/ariadna/workflows/quick.md +19 -221
- data/data/ariadna/workflows/roadmap-ops.md +28 -0
- data/data/ariadna/workflows/verify-work.md +23 -560
- data/data/commands/ariadna/add-phase.md +11 -22
- data/data/commands/ariadna/debug.md +11 -143
- data/data/commands/ariadna/execute-phase.md +12 -30
- data/data/commands/ariadna/insert-phase.md +7 -14
- data/data/commands/ariadna/map-codebase.md +16 -49
- data/data/commands/ariadna/new-milestone.md +12 -25
- data/data/commands/ariadna/new-project.md +22 -26
- data/data/commands/ariadna/plan-phase.md +13 -22
- data/data/commands/ariadna/progress.md +16 -6
- data/data/commands/ariadna/quick.md +9 -11
- data/data/commands/ariadna/remove-phase.md +9 -12
- data/data/commands/ariadna/verify-work.md +14 -19
- data/data/skills/rails-backend/API.md +138 -0
- data/data/skills/rails-backend/CONTROLLERS.md +154 -0
- data/data/skills/rails-backend/JOBS.md +132 -0
- data/data/skills/rails-backend/MODELS.md +213 -0
- data/data/skills/rails-backend/SKILL.md +169 -0
- data/data/skills/rails-frontend/ASSETS.md +154 -0
- data/data/skills/rails-frontend/COMPONENTS.md +253 -0
- data/data/skills/rails-frontend/SKILL.md +187 -0
- data/data/skills/rails-frontend/VIEWS.md +168 -0
- data/data/skills/rails-performance/PROFILING.md +106 -0
- data/data/skills/rails-performance/SKILL.md +217 -0
- data/data/skills/rails-security/AUDIT.md +118 -0
- data/data/skills/rails-security/SKILL.md +422 -0
- data/data/skills/rails-testing/FIXTURES.md +78 -0
- data/data/skills/rails-testing/SKILL.md +160 -0
- data/data/skills/rails-testing/SYSTEM-TESTS.md +73 -0
- data/lib/ariadna/installer.rb +11 -15
- data/lib/ariadna/tools/cli.rb +0 -12
- data/lib/ariadna/tools/config_manager.rb +10 -72
- data/lib/ariadna/tools/frontmatter.rb +23 -1
- data/lib/ariadna/tools/init.rb +201 -401
- data/lib/ariadna/tools/model_profiles.rb +6 -14
- data/lib/ariadna/tools/phase_manager.rb +1 -10
- data/lib/ariadna/tools/state_manager.rb +170 -451
- data/lib/ariadna/tools/template_filler.rb +4 -12
- data/lib/ariadna/tools/verification.rb +21 -399
- data/lib/ariadna/uninstaller.rb +9 -0
- data/lib/ariadna/version.rb +1 -1
- data/lib/ariadna.rb +1 -0
- metadata +20 -91
- data/data/agents/ariadna-backend-executor.md +0 -261
- data/data/agents/ariadna-frontend-executor.md +0 -259
- data/data/agents/ariadna-integration-checker.md +0 -418
- data/data/agents/ariadna-phase-researcher.md +0 -469
- data/data/agents/ariadna-plan-checker.md +0 -622
- data/data/agents/ariadna-project-researcher.md +0 -618
- data/data/agents/ariadna-research-synthesizer.md +0 -236
- data/data/agents/ariadna-test-executor.md +0 -266
- data/data/ariadna/references/checkpoints.md +0 -772
- data/data/ariadna/references/continuation-format.md +0 -249
- data/data/ariadna/references/decimal-phase-calculation.md +0 -65
- data/data/ariadna/references/git-integration.md +0 -248
- data/data/ariadna/references/git-planning-commit.md +0 -38
- data/data/ariadna/references/model-profile-resolution.md +0 -32
- data/data/ariadna/references/model-profiles.md +0 -73
- data/data/ariadna/references/phase-argument-parsing.md +0 -61
- data/data/ariadna/references/planning-config.md +0 -194
- data/data/ariadna/references/questioning.md +0 -153
- data/data/ariadna/references/rails-conventions.md +0 -416
- data/data/ariadna/references/tdd.md +0 -267
- data/data/ariadna/references/ui-brand.md +0 -160
- data/data/ariadna/references/verification-patterns.md +0 -853
- data/data/ariadna/templates/codebase/architecture.md +0 -481
- data/data/ariadna/templates/codebase/concerns.md +0 -380
- data/data/ariadna/templates/codebase/conventions.md +0 -434
- data/data/ariadna/templates/codebase/integrations.md +0 -328
- data/data/ariadna/templates/codebase/stack.md +0 -189
- data/data/ariadna/templates/codebase/structure.md +0 -418
- data/data/ariadna/templates/codebase/testing.md +0 -606
- data/data/ariadna/templates/context.md +0 -283
- data/data/ariadna/templates/continue-here.md +0 -78
- data/data/ariadna/templates/debug-subagent-prompt.md +0 -91
- data/data/ariadna/templates/phase-prompt.md +0 -609
- data/data/ariadna/templates/planner-subagent-prompt.md +0 -117
- data/data/ariadna/templates/research-project/ARCHITECTURE.md +0 -439
- data/data/ariadna/templates/research-project/FEATURES.md +0 -168
- data/data/ariadna/templates/research-project/PITFALLS.md +0 -406
- data/data/ariadna/templates/research-project/STACK.md +0 -251
- data/data/ariadna/templates/research-project/SUMMARY.md +0 -247
- data/data/ariadna/templates/state.md +0 -176
- data/data/ariadna/templates/summary-complex.md +0 -59
- data/data/ariadna/templates/summary-minimal.md +0 -41
- data/data/ariadna/templates/summary-standard.md +0 -48
- data/data/ariadna/templates/user-setup.md +0 -310
- data/data/ariadna/workflows/add-phase.md +0 -111
- data/data/ariadna/workflows/add-todo.md +0 -157
- data/data/ariadna/workflows/audit-milestone.md +0 -241
- data/data/ariadna/workflows/check-todos.md +0 -176
- data/data/ariadna/workflows/complete-milestone.md +0 -644
- data/data/ariadna/workflows/diagnose-issues.md +0 -219
- data/data/ariadna/workflows/discovery-phase.md +0 -289
- data/data/ariadna/workflows/discuss-phase.md +0 -408
- data/data/ariadna/workflows/execute-plan.md +0 -448
- data/data/ariadna/workflows/help.md +0 -470
- data/data/ariadna/workflows/insert-phase.md +0 -129
- data/data/ariadna/workflows/list-phase-assumptions.md +0 -178
- data/data/ariadna/workflows/pause-work.md +0 -122
- data/data/ariadna/workflows/plan-milestone-gaps.md +0 -256
- data/data/ariadna/workflows/remove-phase.md +0 -154
- data/data/ariadna/workflows/research-phase.md +0 -74
- data/data/ariadna/workflows/resume-project.md +0 -306
- data/data/ariadna/workflows/set-profile.md +0 -80
- data/data/ariadna/workflows/settings.md +0 -145
- data/data/ariadna/workflows/transition.md +0 -493
- data/data/ariadna/workflows/update.md +0 -212
- data/data/ariadna/workflows/verify-phase.md +0 -226
- data/data/commands/ariadna/add-todo.md +0 -42
- data/data/commands/ariadna/audit-milestone.md +0 -42
- data/data/commands/ariadna/check-todos.md +0 -41
- data/data/commands/ariadna/complete-milestone.md +0 -136
- data/data/commands/ariadna/discuss-phase.md +0 -86
- data/data/commands/ariadna/help.md +0 -22
- data/data/commands/ariadna/list-phase-assumptions.md +0 -50
- data/data/commands/ariadna/pause-work.md +0 -35
- data/data/commands/ariadna/plan-milestone-gaps.md +0 -40
- data/data/commands/ariadna/reapply-patches.md +0 -110
- data/data/commands/ariadna/research-phase.md +0 -187
- data/data/commands/ariadna/resume-work.md +0 -40
- data/data/commands/ariadna/set-profile.md +0 -34
- data/data/commands/ariadna/settings.md +0 -36
- data/data/commands/ariadna/update.md +0 -37
- data/data/guides/backend.md +0 -3069
- data/data/guides/frontend.md +0 -1479
- data/data/guides/performance.md +0 -1193
- data/data/guides/security.md +0 -1522
- data/data/guides/style-guide.md +0 -1091
- data/data/guides/testing.md +0 -504
- data/data/templates.md +0 -94
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rails-testing
|
|
3
|
+
description: Ruby on Rails testing conventions — Minitest strategy, fixtures, system tests. Use when writing tests, setting up test data, or running test suites.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rails Testing Skill
|
|
7
|
+
|
|
8
|
+
Testing strategy for Rails applications using **Minitest** and **fixtures**. Not RSpec, not FactoryBot.
|
|
9
|
+
|
|
10
|
+
**Sub-files:**
|
|
11
|
+
- [FIXTURES.md](FIXTURES.md) — Fixture patterns and test data management
|
|
12
|
+
- [SYSTEM-TESTS.md](SYSTEM-TESTS.md) — System and integration test conventions
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Philosophy
|
|
17
|
+
|
|
18
|
+
- **Test at the model level first** — Business logic lives in models; that's where tests provide the most value
|
|
19
|
+
- **Fixtures over factories** — YAML fixtures are deterministic, fast, and require no setup code
|
|
20
|
+
- **Always set `Current.session`** — Lambda defaults and multi-tenancy depend on it; forgetting it is the #1 test failure
|
|
21
|
+
- **Test behavior, not implementation** — Assert on outcomes, not internal method calls
|
|
22
|
+
- **Use `assert_difference` for state changes** — Catches false positives that boolean checks alone miss
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## File Organization
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
test/
|
|
30
|
+
models/
|
|
31
|
+
card/
|
|
32
|
+
archivable_test.rb # Concern tests get their own file
|
|
33
|
+
card_test.rb
|
|
34
|
+
controllers/
|
|
35
|
+
cards/
|
|
36
|
+
closures_controller_test.rb
|
|
37
|
+
jobs/
|
|
38
|
+
notify_recipients_job_test.rb
|
|
39
|
+
fixtures/
|
|
40
|
+
cards.yml
|
|
41
|
+
sessions.yml
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Standard Test Structure
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
require "test_helper"
|
|
50
|
+
|
|
51
|
+
class Card::CloseableTest < ActiveSupport::TestCase
|
|
52
|
+
setup do
|
|
53
|
+
Current.session = sessions(:david) # Always — sets user + account context
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
test "close creates closure record and marks card closed" do
|
|
57
|
+
card = cards(:logo)
|
|
58
|
+
|
|
59
|
+
assert_difference -> { Closure.count }, +1 do
|
|
60
|
+
card.close
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
assert card.closed?
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Model Testing
|
|
71
|
+
|
|
72
|
+
### State Changes
|
|
73
|
+
|
|
74
|
+
Nest `assert_difference` blocks when multiple record types change together:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
assert_difference -> { Closure.count }, +1 do
|
|
78
|
+
assert_difference -> { Event.count }, +1 do
|
|
79
|
+
card.close
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
assert card.closed?
|
|
84
|
+
assert_equal "card_closed", Event.last.action
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Event Attributes
|
|
88
|
+
|
|
89
|
+
Count alone is insufficient — always verify attributes:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
event = Event.last
|
|
93
|
+
assert_equal "card_archived", event.action
|
|
94
|
+
assert_equal card, event.eventable
|
|
95
|
+
assert_equal Current.user, event.creator
|
|
96
|
+
# For events with particulars:
|
|
97
|
+
assert_equal old_board.name, event.particulars["old_board"]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Scopes
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
card.archive
|
|
104
|
+
|
|
105
|
+
assert_includes Card.archived, card
|
|
106
|
+
assert_not_includes Card.unarchived, card
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Controller Testing
|
|
112
|
+
|
|
113
|
+
Inherit from `ActionDispatch::IntegrationTest`. Assert on model state after the request:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
class Cards::ClosuresControllerTest < ActionDispatch::IntegrationTest
|
|
117
|
+
setup do
|
|
118
|
+
Current.session = sessions(:david)
|
|
119
|
+
@card = cards(:logo)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
test "create closes the card" do
|
|
123
|
+
post card_closure_path(@card.number)
|
|
124
|
+
assert @card.reload.closed?
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Job Testing
|
|
132
|
+
|
|
133
|
+
Test synchronous logic and async enqueuing separately:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
# Business logic — fast, no job infrastructure
|
|
137
|
+
test "process_data updates records" do
|
|
138
|
+
record.process_data
|
|
139
|
+
assert record.processed?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Async wrapper — verify enqueuing
|
|
143
|
+
test "process_data_later enqueues job" do
|
|
144
|
+
assert_enqueued_with(job: ProcessDataJob, args: [record]) do
|
|
145
|
+
record.process_data_later
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Multi-tenancy is automatic — `Current.account` is captured and restored by the job extension. Setting `Current.session` in `setup` is sufficient.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Common Gotchas
|
|
155
|
+
|
|
156
|
+
**1. Missing `Current.session`** — Lambda defaults (`default: -> { Current.user }`) raise `nil` errors. Put it in `setup`, not inline.
|
|
157
|
+
|
|
158
|
+
**2. Only checking event count** — After `assert_difference`, verify `Event.last.action`, `eventable`, and `creator`.
|
|
159
|
+
|
|
160
|
+
**3. Skipping `assert_difference`** — `assert card.archived?` can pass if the flag is set wrong. Wrap the action in `assert_difference -> { Card::Archive.count }, +1` to confirm the record was created.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# System Tests & Integration Tests
|
|
2
|
+
|
|
3
|
+
Part of the [Rails Testing Skill](SKILL.md).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## When to Write System Tests
|
|
8
|
+
|
|
9
|
+
System tests (browser-driven via Capybara) are reserved for **critical user flows**:
|
|
10
|
+
|
|
11
|
+
- Authentication and session management
|
|
12
|
+
- Multi-step form submissions
|
|
13
|
+
- JavaScript-dependent interactions (Turbo streams, Stimulus)
|
|
14
|
+
- Flows that span multiple controllers
|
|
15
|
+
|
|
16
|
+
For business logic, prefer model tests — faster and more precise.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Test Type Selection
|
|
21
|
+
|
|
22
|
+
| Concern | Use |
|
|
23
|
+
|---|---|
|
|
24
|
+
| Business logic, state changes | Model test (`ActiveSupport::TestCase`) |
|
|
25
|
+
| Controller routing, response codes | `ActionDispatch::IntegrationTest` |
|
|
26
|
+
| Full browser flow, JS interactions | System test (`ApplicationSystemTestCase`) |
|
|
27
|
+
| Job enqueuing | `assert_enqueued_with` in model/controller test |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## System Test Structure
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
require "application_system_test_case"
|
|
35
|
+
|
|
36
|
+
class CardLifecycleTest < ApplicationSystemTestCase
|
|
37
|
+
setup do
|
|
38
|
+
sign_in_as users(:david)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
test "user can close and reopen a card" do
|
|
42
|
+
visit card_path(cards(:logo))
|
|
43
|
+
click_on "Close"
|
|
44
|
+
assert_text "Card closed"
|
|
45
|
+
|
|
46
|
+
click_on "Reopen"
|
|
47
|
+
assert_text "Card reopened"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Define `sign_in_as` in `ApplicationSystemTestCase` — do not repeat the login flow in each test.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Integration Tests (Controller-Level)
|
|
57
|
+
|
|
58
|
+
For HTTP assertions without a browser, set `Current.session` directly:
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
class Cards::ClosuresControllerTest < ActionDispatch::IntegrationTest
|
|
62
|
+
setup do
|
|
63
|
+
Current.session = sessions(:david)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
test "POST creates closure and redirects" do
|
|
67
|
+
post card_closure_path(cards(:logo).number)
|
|
68
|
+
assert_redirected_to card_path(cards(:logo).number)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
No browser sign-in flow needed — `Current.session` assignment is sufficient for controller tests.
|
data/lib/ariadna/installer.rb
CHANGED
|
@@ -21,7 +21,7 @@ module Ariadna
|
|
|
21
21
|
|
|
22
22
|
copy_commands
|
|
23
23
|
copy_agents
|
|
24
|
-
|
|
24
|
+
copy_skills
|
|
25
25
|
copy_content
|
|
26
26
|
write_version
|
|
27
27
|
install_statusline
|
|
@@ -125,7 +125,7 @@ module Ariadna
|
|
|
125
125
|
|
|
126
126
|
def source_manifest_keys
|
|
127
127
|
keys = []
|
|
128
|
-
%w[commands/ariadna agents
|
|
128
|
+
%w[commands/ariadna agents skills ariadna].each do |subdir|
|
|
129
129
|
src_base = subdir == "agents" ? source_dir : source_dir
|
|
130
130
|
dir = File.join(src_base, subdir)
|
|
131
131
|
next unless File.directory?(dir)
|
|
@@ -142,7 +142,7 @@ module Ariadna
|
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
def cleanup_empty_dirs
|
|
145
|
-
%w[commands/ariadna agents
|
|
145
|
+
%w[commands/ariadna agents skills ariadna].each do |subdir|
|
|
146
146
|
dir = File.join(@target_dir, subdir)
|
|
147
147
|
next unless File.directory?(dir)
|
|
148
148
|
|
|
@@ -201,23 +201,19 @@ module Ariadna
|
|
|
201
201
|
puts " \u2713 Installed #{count} agents"
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
-
def
|
|
205
|
-
src = File.join(source_dir, "
|
|
206
|
-
dest = File.join(@target_dir, "
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
FileUtils.cp(file, File.join(dest, File.basename(file)))
|
|
211
|
-
end
|
|
212
|
-
count = Dir[File.join(dest, "*.md")].size
|
|
213
|
-
puts " \u2713 Installed #{count} guides"
|
|
204
|
+
def copy_skills
|
|
205
|
+
src = File.join(source_dir, "skills")
|
|
206
|
+
dest = File.join(@target_dir, "skills")
|
|
207
|
+
copy_tree(src, dest)
|
|
208
|
+
count = Dir[File.join(dest, "*")].count { |d| File.directory?(d) }
|
|
209
|
+
puts " \u2713 Installed #{count} skills"
|
|
214
210
|
end
|
|
215
211
|
|
|
216
212
|
def copy_content
|
|
217
213
|
src = File.join(source_dir, "ariadna")
|
|
218
214
|
dest = File.join(@target_dir, "ariadna")
|
|
219
215
|
copy_tree(src, dest)
|
|
220
|
-
puts " \u2713 Installed ariadna/ (workflows, templates
|
|
216
|
+
puts " \u2713 Installed ariadna/ (workflows, templates)"
|
|
221
217
|
end
|
|
222
218
|
|
|
223
219
|
def write_version
|
|
@@ -240,7 +236,7 @@ module Ariadna
|
|
|
240
236
|
|
|
241
237
|
def generate_manifest_entries
|
|
242
238
|
entries = {}
|
|
243
|
-
%w[commands/ariadna agents
|
|
239
|
+
%w[commands/ariadna agents skills ariadna].each do |subdir|
|
|
244
240
|
dir = File.join(@target_dir, subdir)
|
|
245
241
|
next unless File.directory?(dir)
|
|
246
242
|
|
data/lib/ariadna/tools/cli.rb
CHANGED
|
@@ -30,9 +30,6 @@ module Ariadna
|
|
|
30
30
|
when "verify-path-exists"
|
|
31
31
|
require_relative "utilities"
|
|
32
32
|
Utilities.verify_path_exists(argv, raw: raw)
|
|
33
|
-
when "verify-summary"
|
|
34
|
-
require_relative "verification"
|
|
35
|
-
Verification.verify_summary(argv, raw: raw)
|
|
36
33
|
when "config-ensure-section"
|
|
37
34
|
require_relative "config_manager"
|
|
38
35
|
ConfigManager.ensure_section(argv, raw: raw)
|
|
@@ -42,12 +39,6 @@ module Ariadna
|
|
|
42
39
|
when "history-digest"
|
|
43
40
|
require_relative "state_manager"
|
|
44
41
|
StateManager.history_digest(argv, raw: raw)
|
|
45
|
-
when "summary-extract"
|
|
46
|
-
require_relative "state_manager"
|
|
47
|
-
StateManager.summary_extract(argv, raw: raw)
|
|
48
|
-
when "state-snapshot"
|
|
49
|
-
require_relative "state_manager"
|
|
50
|
-
StateManager.snapshot(argv, raw: raw)
|
|
51
42
|
when "phase-plan-index"
|
|
52
43
|
require_relative "phase_manager"
|
|
53
44
|
PhaseManager.plan_index(argv, raw: raw)
|
|
@@ -63,9 +54,6 @@ module Ariadna
|
|
|
63
54
|
when "milestone"
|
|
64
55
|
require_relative "phase_manager"
|
|
65
56
|
PhaseManager.milestone_dispatch(argv, raw: raw)
|
|
66
|
-
when "validate"
|
|
67
|
-
require_relative "verification"
|
|
68
|
-
Verification.validate_dispatch(argv, raw: raw)
|
|
69
57
|
when "progress"
|
|
70
58
|
require_relative "roadmap_analyzer"
|
|
71
59
|
RoadmapAnalyzer.progress(argv, raw: raw)
|
|
@@ -7,70 +7,25 @@ module Ariadna
|
|
|
7
7
|
module ConfigManager
|
|
8
8
|
DEFAULTS = {
|
|
9
9
|
"model_profile" => "balanced",
|
|
10
|
-
"
|
|
11
|
-
"search_gitignored" => false,
|
|
10
|
+
"verifier" => true,
|
|
12
11
|
"branching_strategy" => "none",
|
|
13
12
|
"phase_branch_template" => "ariadna/phase-{phase}-{slug}",
|
|
14
13
|
"milestone_branch_template" => "ariadna/{milestone}-{slug}",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"parallelization" => true,
|
|
19
|
-
"execution_mode" => "vertical",
|
|
20
|
-
"team_execution" => false
|
|
14
|
+
"commit_docs" => true,
|
|
15
|
+
"search_gitignored" => false,
|
|
16
|
+
"parallelization" => true
|
|
21
17
|
}.freeze
|
|
22
18
|
|
|
23
19
|
def self.load_config(cwd = Dir.pwd)
|
|
24
20
|
config_path = File.join(cwd, ".ariadna_planning", "config.json")
|
|
25
21
|
return DEFAULTS.dup unless File.exist?(config_path)
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return parsed[key] if parsed.key?(key)
|
|
32
|
-
|
|
33
|
-
if nested && parsed[nested[:section]].is_a?(Hash)
|
|
34
|
-
val = parsed[nested[:section]][nested[:field]]
|
|
35
|
-
return val unless val.nil?
|
|
36
|
-
end
|
|
37
|
-
nil
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
parallelization = begin
|
|
41
|
-
val = get.call("parallelization")
|
|
42
|
-
if val.is_a?(Hash) && val.key?("enabled")
|
|
43
|
-
val["enabled"]
|
|
44
|
-
elsif [true, false].include?(val)
|
|
45
|
-
val
|
|
46
|
-
else
|
|
47
|
-
DEFAULTS["parallelization"]
|
|
48
|
-
end
|
|
23
|
+
parsed = JSON.parse(File.read(config_path))
|
|
24
|
+
result = DEFAULTS.dup
|
|
25
|
+
DEFAULTS.each_key do |key|
|
|
26
|
+
result[key] = parsed[key] unless parsed[key].nil?
|
|
49
27
|
end
|
|
50
|
-
|
|
51
|
-
# nil_or helper: use default only when value is nil (preserves false)
|
|
52
|
-
nil_or = ->(val, default) { val.nil? ? default : val }
|
|
53
|
-
|
|
54
|
-
{
|
|
55
|
-
"model_profile" => get.call("model_profile") || DEFAULTS["model_profile"],
|
|
56
|
-
"commit_docs" => nil_or.call(get.call("commit_docs", { section: "planning", field: "commit_docs" }), DEFAULTS["commit_docs"]),
|
|
57
|
-
"search_gitignored" => nil_or.call(get.call("search_gitignored", { section: "planning", field: "search_gitignored" }), DEFAULTS["search_gitignored"]),
|
|
58
|
-
"branching_strategy" => get.call("branching_strategy", { section: "git", field: "branching_strategy" }) || DEFAULTS["branching_strategy"],
|
|
59
|
-
"phase_branch_template" => get.call("phase_branch_template", { section: "git", field: "phase_branch_template" }) || DEFAULTS["phase_branch_template"],
|
|
60
|
-
"milestone_branch_template" => get.call("milestone_branch_template", { section: "git", field: "milestone_branch_template" }) || DEFAULTS["milestone_branch_template"],
|
|
61
|
-
"research" => nil_or.call(get.call("research", { section: "workflow", field: "research" }), DEFAULTS["research"]),
|
|
62
|
-
"plan_checker" => nil_or.call(get.call("plan_checker", { section: "workflow", field: "plan_check" }), DEFAULTS["plan_checker"]),
|
|
63
|
-
"verifier" => nil_or.call(get.call("verifier", { section: "workflow", field: "verifier" }), DEFAULTS["verifier"]),
|
|
64
|
-
"parallelization" => parallelization,
|
|
65
|
-
"execution_mode" => get.call("execution_mode", { section: "execution", field: "mode" }) || DEFAULTS["execution_mode"],
|
|
66
|
-
"team_execution" => begin
|
|
67
|
-
val = get.call("team_execution", { section: "execution", field: "team" })
|
|
68
|
-
case val
|
|
69
|
-
when "auto", true, false then val
|
|
70
|
-
else DEFAULTS["team_execution"]
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
}
|
|
28
|
+
result
|
|
74
29
|
rescue JSON::ParserError
|
|
75
30
|
DEFAULTS.dup
|
|
76
31
|
end
|
|
@@ -87,24 +42,7 @@ module Ariadna
|
|
|
87
42
|
return
|
|
88
43
|
end
|
|
89
44
|
|
|
90
|
-
|
|
91
|
-
model_profile: "balanced",
|
|
92
|
-
commit_docs: true,
|
|
93
|
-
search_gitignored: false,
|
|
94
|
-
branching_strategy: "none",
|
|
95
|
-
phase_branch_template: "ariadna/phase-{phase}-{slug}",
|
|
96
|
-
milestone_branch_template: "ariadna/{milestone}-{slug}",
|
|
97
|
-
workflow: {
|
|
98
|
-
research: false,
|
|
99
|
-
plan_check: true,
|
|
100
|
-
verifier: true
|
|
101
|
-
},
|
|
102
|
-
parallelization: true,
|
|
103
|
-
execution_mode: "vertical",
|
|
104
|
-
team_execution: false
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
File.write(config_path, JSON.pretty_generate(defaults))
|
|
45
|
+
File.write(config_path, JSON.pretty_generate(DEFAULTS))
|
|
108
46
|
Output.json({ created: true, path: ".ariadna_planning/config.json" }, raw: raw, raw_value: "created")
|
|
109
47
|
end
|
|
110
48
|
|
|
@@ -7,9 +7,15 @@ module Ariadna
|
|
|
7
7
|
SCHEMAS = {
|
|
8
8
|
"plan" => %w[phase plan type],
|
|
9
9
|
"summary" => %w[phase plan subsystem],
|
|
10
|
-
"verification" => %w[phase]
|
|
10
|
+
"verification" => %w[phase],
|
|
11
|
+
"skill" => %w[name description]
|
|
11
12
|
}.freeze
|
|
12
13
|
|
|
14
|
+
SKILL_RESERVED_WORDS = %w[anthropic claude].freeze
|
|
15
|
+
SKILL_NAME_PATTERN = /\A[a-z][a-z0-9-]*\z/
|
|
16
|
+
SKILL_NAME_MAX = 64
|
|
17
|
+
SKILL_DESCRIPTION_MAX = 1024
|
|
18
|
+
|
|
13
19
|
def self.dispatch(argv, raw: false)
|
|
14
20
|
subcommand = argv.shift
|
|
15
21
|
file = argv.shift
|
|
@@ -191,6 +197,22 @@ module Ariadna
|
|
|
191
197
|
Output.error("File not found: #{file}")
|
|
192
198
|
end
|
|
193
199
|
|
|
200
|
+
def self.validate_skill(fm)
|
|
201
|
+
errors = []
|
|
202
|
+
name = fm["name"].to_s
|
|
203
|
+
description = fm["description"].to_s
|
|
204
|
+
|
|
205
|
+
errors << "name must match lowercase-hyphens pattern" unless name.match?(SKILL_NAME_PATTERN)
|
|
206
|
+
errors << "name must be #{SKILL_NAME_MAX} chars or fewer" if name.length > SKILL_NAME_MAX
|
|
207
|
+
if SKILL_RESERVED_WORDS.any? { |w| name.include?(w) }
|
|
208
|
+
errors << "name contains reserved word"
|
|
209
|
+
end
|
|
210
|
+
errors << "description must not be empty" if description.empty?
|
|
211
|
+
errors << "description must be #{SKILL_DESCRIPTION_MAX} chars or fewer" if description.length > SKILL_DESCRIPTION_MAX
|
|
212
|
+
|
|
213
|
+
errors
|
|
214
|
+
end
|
|
215
|
+
|
|
194
216
|
# --- Private helpers ---
|
|
195
217
|
|
|
196
218
|
def self.parse_yaml(yaml_str)
|