summaryrefslogtreecommitdiff
path: root/internal/api
diff options
context:
space:
mode:
authorClaudomator Agent <agent@claudomator>2026-03-26 08:35:11 +0000
committerClaudomator Agent <agent@claudomator>2026-03-26 08:35:11 +0000
commit4affaae6853c260346afe344dfb8d46ff497530f (patch)
tree23daa66c5183694c8cd7cb83d8551357b2c960b1 /internal/api
parent8bb9ac1328fc4f6b8d8e7aae0d6ea706e502c245 (diff)
add AcceptanceCriterion struct, replace steps with acceptance_criteria in elaboratedStoryValidation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/api')
-rw-r--r--internal/api/elaborate.go15
-rw-r--r--internal/api/elaborate_test.go44
2 files changed, 55 insertions, 4 deletions
diff --git a/internal/api/elaborate.go b/internal/api/elaborate.go
index dd51c7d..37380c2 100644
--- a/internal/api/elaborate.go
+++ b/internal/api/elaborate.go
@@ -287,11 +287,18 @@ type elaboratedStoryTask struct {
Subtasks []elaboratedStorySubtask `json:"subtasks"`
}
+// AcceptanceCriterion is a single verifiable condition in a story validation.
+type AcceptanceCriterion struct {
+ Name string `json:"name"`
+ Verification string `json:"verification"`
+ TestRef string `json:"test_ref,omitempty"`
+}
+
// elaboratedStoryValidation describes how to verify the story was successful.
type elaboratedStoryValidation struct {
- Type string `json:"type"`
- Steps []string `json:"steps"`
- SuccessCriteria string `json:"success_criteria"`
+ Type string `json:"type"`
+ AcceptanceCriteria []AcceptanceCriterion `json:"acceptance_criteria"`
+ SuccessCriteria string `json:"success_criteria"`
}
// elaboratedStory is the full implementation plan produced by story elaboration.
@@ -320,7 +327,7 @@ Output ONLY valid JSON matching this schema:
],
"validation": {
"type": "build|test|smoke",
- "steps": ["step1", "step2"],
+ "acceptance_criteria": [{"name": "...", "verification": "...", "test_ref": "optional"}],
"success_criteria": "what success looks like"
}
}
diff --git a/internal/api/elaborate_test.go b/internal/api/elaborate_test.go
index 32cec3c..34269e9 100644
--- a/internal/api/elaborate_test.go
+++ b/internal/api/elaborate_test.go
@@ -477,6 +477,50 @@ func TestElaborateTask_NoRawNarrativeWithoutExplicitProjectDir(t *testing.T) {
}
}
+func TestElaboratedStoryValidation_AcceptanceCriteriaSchema(t *testing.T) {
+ raw := `{
+ "type": "test",
+ "acceptance_criteria": [
+ {"name": "API returns 200", "verification": "curl /health returns 200", "test_ref": "TestHealthCheck"},
+ {"name": "DB migrates cleanly", "verification": "migration runs without error"}
+ ],
+ "success_criteria": "all checks pass"
+ }`
+
+ var v elaboratedStoryValidation
+ if err := json.Unmarshal([]byte(raw), &v); err != nil {
+ t.Fatalf("unmarshal failed: %v", err)
+ }
+
+ if len(v.AcceptanceCriteria) != 2 {
+ t.Fatalf("expected 2 acceptance_criteria, got %d", len(v.AcceptanceCriteria))
+ }
+
+ ac0 := v.AcceptanceCriteria[0]
+ if ac0.Name != "API returns 200" {
+ t.Errorf("ac[0].name: want %q, got %q", "API returns 200", ac0.Name)
+ }
+ if ac0.Verification != "curl /health returns 200" {
+ t.Errorf("ac[0].verification: want %q, got %q", "curl /health returns 200", ac0.Verification)
+ }
+ if ac0.TestRef != "TestHealthCheck" {
+ t.Errorf("ac[0].test_ref: want %q, got %q", "TestHealthCheck", ac0.TestRef)
+ }
+
+ ac1 := v.AcceptanceCriteria[1]
+ if ac1.Name != "DB migrates cleanly" {
+ t.Errorf("ac[1].name: want %q, got %q", "DB migrates cleanly", ac1.Name)
+ }
+ // test_ref omitted — backward compat: must be empty string, not an error
+ if ac1.TestRef != "" {
+ t.Errorf("ac[1].test_ref: want empty (omitempty), got %q", ac1.TestRef)
+ }
+
+ if v.SuccessCriteria != "all checks pass" {
+ t.Errorf("success_criteria: want %q, got %q", "all checks pass", v.SuccessCriteria)
+ }
+}
+
func TestElaborateTask_AppendsRawNarrative(t *testing.T) {
srv, _ := testServer(t)