dotdog 0.5.0 → 0.6.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.
- package/dist/cli.js +137 -11
- package/kits/dao/SPEC.dog +39 -0
- package/kits/dao/constitution.dog +9 -0
- package/kits/dao/data-model.dog +134 -0
- package/kits/saas/constitution.dog +8 -0
- package/package.json +4 -1
package/dist/cli.js
CHANGED
|
@@ -2751,7 +2751,7 @@ function parseBlocks(lines, start, end) {
|
|
|
2751
2751
|
let i = start;
|
|
2752
2752
|
while (i < end) {
|
|
2753
2753
|
const line = lines[i];
|
|
2754
|
-
const entityMatch = line.match(
|
|
2754
|
+
const entityMatch = line.match(/^#{3,5}\s+Entity:\s*(.+)/);
|
|
2755
2755
|
if (entityMatch) {
|
|
2756
2756
|
const result = parseStructuredBlock(lines, i, end, "entity", entityMatch[1]);
|
|
2757
2757
|
if (result) {
|
|
@@ -2760,7 +2760,7 @@ function parseBlocks(lines, start, end) {
|
|
|
2760
2760
|
continue;
|
|
2761
2761
|
}
|
|
2762
2762
|
}
|
|
2763
|
-
const relMatch = line.match(
|
|
2763
|
+
const relMatch = line.match(/^#{3,5}\s+Relationship:\s*(.+)/);
|
|
2764
2764
|
if (relMatch) {
|
|
2765
2765
|
const result = parseStructuredBlock(lines, i, end, "relationship", relMatch[1]);
|
|
2766
2766
|
if (result) {
|
|
@@ -2769,7 +2769,7 @@ function parseBlocks(lines, start, end) {
|
|
|
2769
2769
|
continue;
|
|
2770
2770
|
}
|
|
2771
2771
|
}
|
|
2772
|
-
const eventMatch = line.match(
|
|
2772
|
+
const eventMatch = line.match(/^#{3,5}\s+Event:\s*(.+)/);
|
|
2773
2773
|
if (eventMatch) {
|
|
2774
2774
|
const result = parseStructuredBlock(lines, i, end, "event", eventMatch[1]);
|
|
2775
2775
|
if (result) {
|
|
@@ -2778,7 +2778,7 @@ function parseBlocks(lines, start, end) {
|
|
|
2778
2778
|
continue;
|
|
2779
2779
|
}
|
|
2780
2780
|
}
|
|
2781
|
-
const predMatch = line.match(
|
|
2781
|
+
const predMatch = line.match(/^#{3,5}\s+Prediction:\s*(.+)/);
|
|
2782
2782
|
if (predMatch) {
|
|
2783
2783
|
const result = parseStructuredBlock(lines, i, end, "prediction", predMatch[1]);
|
|
2784
2784
|
if (result) {
|
|
@@ -2787,6 +2787,83 @@ function parseBlocks(lines, start, end) {
|
|
|
2787
2787
|
continue;
|
|
2788
2788
|
}
|
|
2789
2789
|
}
|
|
2790
|
+
if (lines[i].startsWith("```")) {
|
|
2791
|
+
let yamlEnd = i + 1;
|
|
2792
|
+
while (yamlEnd < end && !lines[yamlEnd].startsWith("```"))
|
|
2793
|
+
yamlEnd++;
|
|
2794
|
+
if (yamlEnd < end) {
|
|
2795
|
+
const yamlContent = lines.slice(i + 1, yamlEnd);
|
|
2796
|
+
const yaml = parseSimpleYAML(yamlContent);
|
|
2797
|
+
if (yaml.prediction) {
|
|
2798
|
+
blocks.push({
|
|
2799
|
+
kind: "prediction",
|
|
2800
|
+
statement: yaml.prediction || "",
|
|
2801
|
+
description: yaml.description || "",
|
|
2802
|
+
trigger: yaml.trigger || "",
|
|
2803
|
+
timeframe: yaml.timeframe || "",
|
|
2804
|
+
confidence: yaml.confidence || 0,
|
|
2805
|
+
measurement: yaml.measurement || "",
|
|
2806
|
+
status: yaml.status || "pending",
|
|
2807
|
+
yaml,
|
|
2808
|
+
lineStart: i + 1,
|
|
2809
|
+
lineEnd: yamlEnd
|
|
2810
|
+
});
|
|
2811
|
+
i = yamlEnd + 1;
|
|
2812
|
+
continue;
|
|
2813
|
+
}
|
|
2814
|
+
if (yaml.entity) {
|
|
2815
|
+
blocks.push({
|
|
2816
|
+
kind: "entity",
|
|
2817
|
+
name: yaml.entity || "",
|
|
2818
|
+
description: yaml.description || "",
|
|
2819
|
+
type: yaml.type || "node",
|
|
2820
|
+
properties: {},
|
|
2821
|
+
states: Array.isArray(yaml.states) ? yaml.states : [],
|
|
2822
|
+
lifecycle: [],
|
|
2823
|
+
yaml,
|
|
2824
|
+
lineStart: i + 1,
|
|
2825
|
+
lineEnd: yamlEnd
|
|
2826
|
+
});
|
|
2827
|
+
i = yamlEnd + 1;
|
|
2828
|
+
continue;
|
|
2829
|
+
}
|
|
2830
|
+
if (yaml.relationship || yaml.verb) {
|
|
2831
|
+
blocks.push({
|
|
2832
|
+
kind: "relationship",
|
|
2833
|
+
source: yaml.source || "",
|
|
2834
|
+
target: yaml.target || "",
|
|
2835
|
+
verb: yaml.verb || "connects",
|
|
2836
|
+
description: yaml.description || "",
|
|
2837
|
+
cardinality: yaml.cardinality || "N:M",
|
|
2838
|
+
required: false,
|
|
2839
|
+
cascade: "none",
|
|
2840
|
+
invariants: [],
|
|
2841
|
+
yaml,
|
|
2842
|
+
lineStart: i + 1,
|
|
2843
|
+
lineEnd: yamlEnd
|
|
2844
|
+
});
|
|
2845
|
+
i = yamlEnd + 1;
|
|
2846
|
+
continue;
|
|
2847
|
+
}
|
|
2848
|
+
if (yaml.event) {
|
|
2849
|
+
blocks.push({
|
|
2850
|
+
kind: "event",
|
|
2851
|
+
name: yaml.event || "",
|
|
2852
|
+
trigger: yaml.trigger || "",
|
|
2853
|
+
payload: {},
|
|
2854
|
+
preconditions: [],
|
|
2855
|
+
postconditions: [],
|
|
2856
|
+
sideEffects: [],
|
|
2857
|
+
probability: null,
|
|
2858
|
+
yaml,
|
|
2859
|
+
lineStart: i + 1,
|
|
2860
|
+
lineEnd: yamlEnd
|
|
2861
|
+
});
|
|
2862
|
+
i = yamlEnd + 1;
|
|
2863
|
+
continue;
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2790
2867
|
if (/^\|.+\|/.test(line) && i + 1 < end && /^\|[-| ]+\|/.test(lines[i + 1])) {
|
|
2791
2868
|
const table = parseTable(lines, i, end);
|
|
2792
2869
|
if (table) {
|
|
@@ -2795,8 +2872,46 @@ function parseBlocks(lines, start, end) {
|
|
|
2795
2872
|
continue;
|
|
2796
2873
|
}
|
|
2797
2874
|
}
|
|
2875
|
+
if (lines[i].startsWith("```")) {
|
|
2876
|
+
let yamlEnd = i + 1;
|
|
2877
|
+
while (yamlEnd < end && !lines[yamlEnd].startsWith("```"))
|
|
2878
|
+
yamlEnd++;
|
|
2879
|
+
if (yamlEnd < end && yamlEnd > i + 1) {
|
|
2880
|
+
const yamlContent = lines.slice(i + 1, yamlEnd);
|
|
2881
|
+
const yaml = parseSimpleYAML(yamlContent);
|
|
2882
|
+
const key = yaml.prediction ? "prediction" : yaml.entity ? "entity" : yaml.event ? "event" : yaml.relationship || yaml.verb ? "relationship" : null;
|
|
2883
|
+
if (key) {
|
|
2884
|
+
if (key === "prediction") {
|
|
2885
|
+
blocks.push({ kind: "prediction", statement: yaml.prediction || "", description: yaml.description || "", trigger: yaml.trigger || "", timeframe: yaml.timeframe || "", confidence: yaml.confidence || 0, measurement: yaml.measurement || "", status: yaml.status || "pending", yaml, lineStart: i + 1, lineEnd: yamlEnd });
|
|
2886
|
+
} else if (key === "entity") {
|
|
2887
|
+
blocks.push({ kind: "entity", name: yaml.entity || "", description: yaml.description || "", type: yaml.type || "node", properties: {}, states: Array.isArray(yaml.states) ? yaml.states : [], lifecycle: [], yaml, lineStart: i + 1, lineEnd: yamlEnd });
|
|
2888
|
+
} else if (key === "event") {
|
|
2889
|
+
blocks.push({ kind: "event", name: yaml.event || "", trigger: yaml.trigger || "", payload: {}, preconditions: [], postconditions: [], sideEffects: [], probability: null, yaml, lineStart: i + 1, lineEnd: yamlEnd });
|
|
2890
|
+
} else if (key === "relationship") {
|
|
2891
|
+
blocks.push({ kind: "relationship", source: yaml.source || "", target: yaml.target || "", verb: yaml.verb || "connects", description: yaml.description || "", cardinality: yaml.cardinality || "N:M", required: false, cascade: "none", invariants: [], yaml, lineStart: i + 1, lineEnd: yamlEnd });
|
|
2892
|
+
}
|
|
2893
|
+
i = yamlEnd + 1;
|
|
2894
|
+
continue;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
if (lines[i].startsWith("```")) {
|
|
2899
|
+
let fenceEnd = i + 1;
|
|
2900
|
+
while (fenceEnd < end && !lines[fenceEnd].startsWith("```"))
|
|
2901
|
+
fenceEnd++;
|
|
2902
|
+
const blockEnd = fenceEnd < end ? fenceEnd + 1 : end;
|
|
2903
|
+
blocks.push({
|
|
2904
|
+
kind: "prose",
|
|
2905
|
+
content: lines.slice(i, blockEnd).join(`
|
|
2906
|
+
`).trim(),
|
|
2907
|
+
lineStart: i + 1,
|
|
2908
|
+
lineEnd: blockEnd
|
|
2909
|
+
});
|
|
2910
|
+
i = blockEnd;
|
|
2911
|
+
continue;
|
|
2912
|
+
}
|
|
2798
2913
|
const proseStart = i;
|
|
2799
|
-
while (i < end && !isBlockStart(lines[i])) {
|
|
2914
|
+
while (i < end && !isBlockStart(lines[i]) && !lines[i].startsWith("```")) {
|
|
2800
2915
|
i++;
|
|
2801
2916
|
}
|
|
2802
2917
|
const proseLines = lines.slice(proseStart, i).filter((l) => l.trim() !== "" || i === proseStart + 1);
|
|
@@ -2813,7 +2928,7 @@ function parseBlocks(lines, start, end) {
|
|
|
2813
2928
|
return blocks;
|
|
2814
2929
|
}
|
|
2815
2930
|
function isBlockStart(line) {
|
|
2816
|
-
return
|
|
2931
|
+
return /^#{3,5}\s+(Entity|Relationship|Event|Prediction):/.test(line) || /^\|.+\|/.test(line);
|
|
2817
2932
|
}
|
|
2818
2933
|
function parseStructuredBlock(lines, start, end, kind, headerRest) {
|
|
2819
2934
|
let i = start + 1;
|
|
@@ -3390,7 +3505,7 @@ program2.command("validate [dir]").action((d = ".") => {
|
|
|
3390
3505
|
const missing = ["SPEC.dog", "constitution.dog", "data-model.dog"].filter((f) => !files.includes(f));
|
|
3391
3506
|
const optional = ["COPY.dog", "plan.dog", "DESIGN-SYSTEM.dog", "INDEX.dog"].filter((f) => !files.includes(f));
|
|
3392
3507
|
console.log(source_default.bold(`
|
|
3393
|
-
${p} : ${files.length} .dog files, ${100 - Math.round(
|
|
3508
|
+
${p} : ${files.length} .dog files, ${Math.max(0, 100 - Math.round(missing.length * 3 / 20 * 100))}% complete`));
|
|
3394
3509
|
for (const f of files)
|
|
3395
3510
|
console.log(source_default.gray(` ${f}`));
|
|
3396
3511
|
if (missing.length) {
|
|
@@ -3398,7 +3513,7 @@ program2.command("validate [dir]").action((d = ".") => {
|
|
|
3398
3513
|
hasErrors = true;
|
|
3399
3514
|
}
|
|
3400
3515
|
if (optional.length)
|
|
3401
|
-
console.log(source_default.
|
|
3516
|
+
console.log(source_default.gray(` Optional: ${optional.join(", ")} — not required for 100%`));
|
|
3402
3517
|
}
|
|
3403
3518
|
}
|
|
3404
3519
|
if (!found)
|
|
@@ -3832,7 +3947,7 @@ Spec Analysis
|
|
|
3832
3947
|
for (const f of missingReq)
|
|
3833
3948
|
gaps.push(`\uD83D\uDD34 ${f}: Missing required file`);
|
|
3834
3949
|
for (const f of missingOpt)
|
|
3835
|
-
gaps.push(
|
|
3950
|
+
gaps.push(`ℹ️ ${f}: Optional file not present`);
|
|
3836
3951
|
const entityNames = new Set(allEntities.map((e) => e.name));
|
|
3837
3952
|
for (const e of allEntities) {
|
|
3838
3953
|
if (!e.description || e.description.length < 10)
|
|
@@ -4469,8 +4584,19 @@ program2.command("resolve <name>").description("Mark a prediction as correct, wr
|
|
|
4469
4584
|
if (block.kind === "prediction") {
|
|
4470
4585
|
const b = block;
|
|
4471
4586
|
if ((b.statement || b.name || "").toLowerCase().includes(name.toLowerCase())) {
|
|
4472
|
-
|
|
4473
|
-
const
|
|
4587
|
+
let headingIdx = -1;
|
|
4588
|
+
const stmt = b.statement || b.name || "";
|
|
4589
|
+
for (const prefix of ["###", "####", "#####"]) {
|
|
4590
|
+
for (const fmt of [`${prefix} Prediction: ${stmt}`, `${prefix} ${stmt}`]) {
|
|
4591
|
+
const idx = content.indexOf(fmt);
|
|
4592
|
+
if (idx >= 0) {
|
|
4593
|
+
headingIdx = idx;
|
|
4594
|
+
break;
|
|
4595
|
+
}
|
|
4596
|
+
}
|
|
4597
|
+
if (headingIdx >= 0)
|
|
4598
|
+
break;
|
|
4599
|
+
}
|
|
4474
4600
|
if (headingIdx >= 0) {
|
|
4475
4601
|
const blockStart = content.indexOf("```yaml", headingIdx);
|
|
4476
4602
|
const blockEnd = content.indexOf("```", blockStart + 7);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# DAO Governance
|
|
2
|
+
|
|
3
|
+
> Community governance starter. Members create proposals, vote, and execute approved treasury actions.
|
|
4
|
+
|
|
5
|
+
## Product
|
|
6
|
+
|
|
7
|
+
A DAO governance workspace. Members join with voting power, submit proposals, cast votes, and execute passed proposals against a shared treasury.
|
|
8
|
+
|
|
9
|
+
## What the User Sees
|
|
10
|
+
|
|
11
|
+
### Screen: Proposal List
|
|
12
|
+
|
|
13
|
+
+------------------------------------------+
|
|
14
|
+
| DAO Proposals [New] |
|
|
15
|
+
|------------------------------------------|
|
|
16
|
+
| Upgrade Grants Program Active |
|
|
17
|
+
| Fund Security Audit Passed |
|
|
18
|
+
| Sponsor Builder Event Draft |
|
|
19
|
+
+------------------------------------------+
|
|
20
|
+
|
|
21
|
+
### Screen: Proposal Detail
|
|
22
|
+
|
|
23
|
+
+------------------------------------------+
|
|
24
|
+
| Upgrade Grants Program |
|
|
25
|
+
|------------------------------------------|
|
|
26
|
+
| Status: Active |
|
|
27
|
+
| For: 62% Against: 18% Abstain: 20% |
|
|
28
|
+
| |
|
|
29
|
+
| [Vote For] [Vote Against] [Abstain] |
|
|
30
|
+
+------------------------------------------+
|
|
31
|
+
|
|
32
|
+
## User Stories
|
|
33
|
+
|
|
34
|
+
| ID | Story | Pri | Acceptance |
|
|
35
|
+
|----|-------|-----|------------|
|
|
36
|
+
| US-01 | Member creates a proposal | P0 | Proposal appears as draft |
|
|
37
|
+
| US-02 | Member casts a vote | P0 | Vote is counted once |
|
|
38
|
+
| US-03 | Proposal reaches quorum | P0 | Status becomes passed or rejected |
|
|
39
|
+
| US-04 | Approved proposal executes | P1 | Treasury allocation is recorded |
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# DAO Governance Constitution
|
|
2
|
+
|
|
3
|
+
## Core Rules
|
|
4
|
+
|
|
5
|
+
1. One member can cast one vote per proposal.
|
|
6
|
+
2. Proposal execution requires both quorum and majority approval.
|
|
7
|
+
3. Treasury allocations must reference an approved proposal.
|
|
8
|
+
4. Proposal state changes are append-only and auditable.
|
|
9
|
+
5. Voting power is read at the proposal snapshot block.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# DAO Governance Data Model
|
|
2
|
+
|
|
3
|
+
## Entities
|
|
4
|
+
|
|
5
|
+
### Entity: Member
|
|
6
|
+
|
|
7
|
+
A DAO participant with voting power.
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
entity: Member
|
|
11
|
+
type: entity
|
|
12
|
+
properties:
|
|
13
|
+
id:
|
|
14
|
+
type: string
|
|
15
|
+
required: true
|
|
16
|
+
wallet:
|
|
17
|
+
type: string
|
|
18
|
+
required: true
|
|
19
|
+
voting_power:
|
|
20
|
+
type: number
|
|
21
|
+
required: true
|
|
22
|
+
states: [active, delegated, suspended]
|
|
23
|
+
lifecycle: active → delegated → suspended
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Entity: Proposal
|
|
27
|
+
|
|
28
|
+
A governance item submitted for member voting.
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
entity: Proposal
|
|
32
|
+
type: entity
|
|
33
|
+
properties:
|
|
34
|
+
id:
|
|
35
|
+
type: string
|
|
36
|
+
required: true
|
|
37
|
+
title:
|
|
38
|
+
type: string
|
|
39
|
+
required: true
|
|
40
|
+
quorum_required:
|
|
41
|
+
type: number
|
|
42
|
+
required: true
|
|
43
|
+
status:
|
|
44
|
+
type: enum
|
|
45
|
+
required: true
|
|
46
|
+
states: [draft, active, passed, rejected, executed]
|
|
47
|
+
lifecycle: draft → active → passed → executed
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Entity: Vote
|
|
51
|
+
|
|
52
|
+
A member decision on a proposal.
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
entity: Vote
|
|
56
|
+
type: event
|
|
57
|
+
properties:
|
|
58
|
+
member_id:
|
|
59
|
+
type: string
|
|
60
|
+
required: true
|
|
61
|
+
proposal_id:
|
|
62
|
+
type: string
|
|
63
|
+
required: true
|
|
64
|
+
choice:
|
|
65
|
+
type: enum
|
|
66
|
+
required: true
|
|
67
|
+
weight:
|
|
68
|
+
type: number
|
|
69
|
+
required: true
|
|
70
|
+
states: [cast, counted]
|
|
71
|
+
lifecycle: cast → counted
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Entity: TreasuryAllocation
|
|
75
|
+
|
|
76
|
+
Funds allocated by an executed proposal.
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
entity: TreasuryAllocation
|
|
80
|
+
type: entity
|
|
81
|
+
properties:
|
|
82
|
+
id:
|
|
83
|
+
type: string
|
|
84
|
+
required: true
|
|
85
|
+
proposal_id:
|
|
86
|
+
type: string
|
|
87
|
+
required: true
|
|
88
|
+
amount:
|
|
89
|
+
type: number
|
|
90
|
+
required: true
|
|
91
|
+
recipient:
|
|
92
|
+
type: string
|
|
93
|
+
required: true
|
|
94
|
+
states: [approved, executed, canceled]
|
|
95
|
+
lifecycle: approved → executed
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Relationships
|
|
99
|
+
|
|
100
|
+
### Relationship: Member → Proposal
|
|
101
|
+
|
|
102
|
+
```yaml
|
|
103
|
+
relationship: Member → Proposal
|
|
104
|
+
verb: submits
|
|
105
|
+
cardinality: 1:N
|
|
106
|
+
required: true
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Relationship: Member → Vote
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
relationship: Member → Vote
|
|
113
|
+
verb: casts
|
|
114
|
+
cardinality: 1:N
|
|
115
|
+
required: true
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Relationship: Proposal → Vote
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
relationship: Proposal → Vote
|
|
122
|
+
verb: receives
|
|
123
|
+
cardinality: 1:N
|
|
124
|
+
required: true
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Relationship: Proposal → TreasuryAllocation
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
relationship: Proposal → TreasuryAllocation
|
|
131
|
+
verb: authorizes
|
|
132
|
+
cardinality: 1:1
|
|
133
|
+
required: false
|
|
134
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# SaaS Platform Constitution
|
|
2
|
+
|
|
3
|
+
## Core Rules
|
|
4
|
+
|
|
5
|
+
1. Organizations own their workspace data.
|
|
6
|
+
2. Users access data only through organization membership.
|
|
7
|
+
3. Plan limits must be enforced before billable usage is accepted.
|
|
8
|
+
4. Subscription state controls access to paid features.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dotdog",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "CLI tool for structured software specifications. Validate .dog files, compile .dag graphs, query via MCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -47,5 +47,8 @@
|
|
|
47
47
|
"homepage": "https://specdog.github.io/dotdog",
|
|
48
48
|
"bugs": {
|
|
49
49
|
"url": "https://github.com/specdog/dotdog/issues"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"lint": "bun run --check src/"
|
|
50
53
|
}
|
|
51
54
|
}
|