Skip to content

Commit e08731f

Browse files
committed
fix(validate): allow undecided decisions without selected option
Only require selected option on decisions with decided lifecycle states (accepted, implemented, adopted). Allows expressing intentionally undecided decisions in proposed, experimental, or deferred states without validation errors. Fixes issue #24
1 parent f6a3033 commit e08731f

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

src/operations/validate.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,19 @@ export const validateOp = defineOperation({
161161
}
162162
}
163163

164-
// INV13: Decisions must have options and selected
164+
// INV13: Decisions must have options and selected (if decided)
165165
for (const n of input.doc.nodes.filter((n) => n.type === "decision")) {
166166
if (!n.options || n.options.length === 0) {
167167
issues.push(`${n.id} (${n.name}): decision has no options`);
168168
}
169-
if (!n.selected) {
169+
// Only require selected option if the decision is in a "decided" state
170+
// Decided states: accepted, implemented, adopted
171+
// Undecided states allowed: proposed, experimental, deferred
172+
const isDecided =
173+
hasLifecycleState(n, "accepted") ||
174+
hasLifecycleState(n, "implemented") ||
175+
hasLifecycleState(n, "adopted");
176+
if (isDecided && !n.selected) {
170177
issues.push(`${n.id} (${n.name}): decision has no selected option`);
171178
}
172179
}

tests/validate.unit.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,20 +78,36 @@ describe("validate", () => {
7878
assert.ok(result.issues.some((i) => i.includes("has no options")));
7979
});
8080

81-
it("decisions without selected detected", () => {
81+
it("decisions without selected detected (when decided)", () => {
8282
const doc = makeDoc([
8383
{
8484
id: "DEC1",
8585
type: "decision",
8686
name: "Dec",
8787
options: [{ id: "a", description: "A" }],
88+
lifecycle: { accepted: true },
8889
},
8990
]);
9091
const result = validateOp({ doc });
9192
assert.equal(result.valid, false);
9293
assert.ok(result.issues.some((i) => i.includes("has no selected")));
9394
});
9495

96+
it("undecided decisions without selected allowed", () => {
97+
const doc = makeDoc([
98+
{
99+
id: "DEC1",
100+
type: "decision",
101+
name: "Dec",
102+
options: [{ id: "a", description: "A" }],
103+
// No lifecycle state means undecided (like proposed)
104+
},
105+
]);
106+
const result = validateOp({ doc });
107+
assert.equal(result.valid, true);
108+
assert.ok(!result.issues.some((i) => i.includes("has no selected")));
109+
});
110+
95111
it("realises accepts capability → stage", () => {
96112
const doc = makeDoc(
97113
[

0 commit comments

Comments
 (0)