@eddacraft/anvil-aps 0.1.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.
Files changed (121) hide show
  1. package/AGENTS.md +155 -0
  2. package/LICENSE +14 -0
  3. package/README.md +57 -0
  4. package/TODO.md +40 -0
  5. package/dist/filter/context-bundle.d.ts +81 -0
  6. package/dist/filter/context-bundle.d.ts.map +1 -0
  7. package/dist/filter/context-bundle.js +230 -0
  8. package/dist/filter/index.d.ts +85 -0
  9. package/dist/filter/index.d.ts.map +1 -0
  10. package/dist/filter/index.js +169 -0
  11. package/dist/index.d.ts +16 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +15 -0
  14. package/dist/loader/index.d.ts +80 -0
  15. package/dist/loader/index.d.ts.map +1 -0
  16. package/dist/loader/index.js +253 -0
  17. package/dist/parser/index.d.ts +24 -0
  18. package/dist/parser/index.d.ts.map +1 -0
  19. package/dist/parser/index.js +22 -0
  20. package/dist/parser/parse-document.d.ts +17 -0
  21. package/dist/parser/parse-document.d.ts.map +1 -0
  22. package/dist/parser/parse-document.js +219 -0
  23. package/dist/parser/parse-index.d.ts +31 -0
  24. package/dist/parser/parse-index.d.ts.map +1 -0
  25. package/dist/parser/parse-index.js +251 -0
  26. package/dist/parser/parse-task.d.ts +30 -0
  27. package/dist/parser/parse-task.d.ts.map +1 -0
  28. package/dist/parser/parse-task.js +261 -0
  29. package/dist/state/index.d.ts +307 -0
  30. package/dist/state/index.d.ts.map +1 -0
  31. package/dist/state/index.js +689 -0
  32. package/dist/templates/generator.d.ts +71 -0
  33. package/dist/templates/generator.d.ts.map +1 -0
  34. package/dist/templates/generator.js +723 -0
  35. package/dist/templates/index.d.ts +5 -0
  36. package/dist/templates/index.d.ts.map +1 -0
  37. package/dist/templates/index.js +4 -0
  38. package/dist/types/index.d.ts +131 -0
  39. package/dist/types/index.d.ts.map +1 -0
  40. package/dist/types/index.js +107 -0
  41. package/dist/validator/index.d.ts +83 -0
  42. package/dist/validator/index.d.ts.map +1 -0
  43. package/dist/validator/index.js +611 -0
  44. package/docs/APS-Anvil-Integration.md +750 -0
  45. package/docs/APS-Conventions.md +635 -0
  46. package/docs/APS-NonGoals.md +455 -0
  47. package/docs/APS-Planning-Spec-v0.1.md +362 -0
  48. package/examples/README.md +170 -0
  49. package/examples/feature-auth.aps.md +87 -0
  50. package/examples/refactor-error-handling.aps.md +119 -0
  51. package/examples/system-ecommerce/APS.md +57 -0
  52. package/examples/system-ecommerce/modules/auth.aps.md +38 -0
  53. package/examples/system-ecommerce/modules/cart.aps.md +53 -0
  54. package/examples/system-ecommerce/modules/payments.aps.md +68 -0
  55. package/examples/system-ecommerce/modules/products.aps.md +53 -0
  56. package/package.json +34 -0
  57. package/project.json +37 -0
  58. package/scripts/generate-templates.js +33 -0
  59. package/src/filter/context-bundle.ts +312 -0
  60. package/src/filter/filter.test.ts +317 -0
  61. package/src/filter/index.ts +249 -0
  62. package/src/index.ts +16 -0
  63. package/src/loader/index.ts +364 -0
  64. package/src/loader/loader.test.ts +224 -0
  65. package/src/parser/__fixtures__/invalid-task-id-not-padded.aps.md +7 -0
  66. package/src/parser/__fixtures__/invalid-task-id.aps.md +8 -0
  67. package/src/parser/__fixtures__/minimal-task.aps.md +7 -0
  68. package/src/parser/__fixtures__/non-scope-hyphenated.aps.md +10 -0
  69. package/src/parser/__fixtures__/simple-index.aps.md +35 -0
  70. package/src/parser/__fixtures__/simple-plan.aps.md +19 -0
  71. package/src/parser/index.ts +30 -0
  72. package/src/parser/parse-document.test.ts +603 -0
  73. package/src/parser/parse-document.ts +262 -0
  74. package/src/parser/parse-index.test.ts +316 -0
  75. package/src/parser/parse-index.ts +298 -0
  76. package/src/parser/parse-task.test.ts +476 -0
  77. package/src/parser/parse-task.ts +325 -0
  78. package/src/state/__fixtures__/invalid-plan.aps.md +9 -0
  79. package/src/state/__fixtures__/test-plan.aps.md +20 -0
  80. package/src/state/index.ts +879 -0
  81. package/src/state/state.test.ts +645 -0
  82. package/src/templates/generator.test.ts +378 -0
  83. package/src/templates/generator.ts +776 -0
  84. package/src/templates/index.ts +5 -0
  85. package/src/types/index.ts +168 -0
  86. package/src/validator/__fixtures__/broken-links.aps.md +10 -0
  87. package/src/validator/__fixtures__/circular-deps-index.aps.md +26 -0
  88. package/src/validator/__fixtures__/circular-modules/module-a.aps.md +9 -0
  89. package/src/validator/__fixtures__/circular-modules/module-b.aps.md +9 -0
  90. package/src/validator/__fixtures__/circular-modules/module-c.aps.md +9 -0
  91. package/src/validator/__fixtures__/dup-modules/module-a.aps.md +9 -0
  92. package/src/validator/__fixtures__/dup-modules/module-b.aps.md +9 -0
  93. package/src/validator/__fixtures__/duplicate-ids-index.aps.md +15 -0
  94. package/src/validator/__fixtures__/invalid-task-id.aps.md +17 -0
  95. package/src/validator/__fixtures__/missing-confidence.aps.md +9 -0
  96. package/src/validator/__fixtures__/missing-h1.aps.md +5 -0
  97. package/src/validator/__fixtures__/missing-intent.aps.md +9 -0
  98. package/src/validator/__fixtures__/missing-modules-section.aps.md +7 -0
  99. package/src/validator/__fixtures__/missing-tasks-section.aps.md +7 -0
  100. package/src/validator/__fixtures__/modules/auth.aps.md +17 -0
  101. package/src/validator/__fixtures__/modules/payments.aps.md +13 -0
  102. package/src/validator/__fixtures__/scope-mismatch.aps.md +14 -0
  103. package/src/validator/__fixtures__/valid-index.aps.md +24 -0
  104. package/src/validator/__fixtures__/valid-leaf.aps.md +22 -0
  105. package/src/validator/index.ts +776 -0
  106. package/src/validator/validator.test.ts +269 -0
  107. package/templates/index-full.md +94 -0
  108. package/templates/index-minimal.md +16 -0
  109. package/templates/index-template.md +63 -0
  110. package/templates/leaf-full.md +76 -0
  111. package/templates/leaf-minimal.md +14 -0
  112. package/templates/leaf-template.md +55 -0
  113. package/templates/simple-full.md +56 -0
  114. package/templates/simple-minimal.md +14 -0
  115. package/templates/simple-template.md +30 -0
  116. package/tsconfig.json +19 -0
  117. package/tsconfig.lib.json +14 -0
  118. package/tsconfig.lib.tsbuildinfo +1 -0
  119. package/tsconfig.spec.json +9 -0
  120. package/tsconfig.tsbuildinfo +1 -0
  121. package/vitest.config.ts +15 -0
@@ -0,0 +1,269 @@
1
+ /**
2
+ * Validator module tests
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { join, dirname } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+ import { validatePlanningDoc, formatValidationIssues, type ValidationResult } from './index.js';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+ const fixturesDir = join(__dirname, '__fixtures__');
13
+
14
+ describe('validatePlanningDoc', () => {
15
+ describe('valid documents', () => {
16
+ it('should validate a valid leaf spec with no errors', async () => {
17
+ const result = await validatePlanningDoc(join(fixturesDir, 'valid-leaf.aps.md'));
18
+
19
+ expect(result.valid).toBe(true);
20
+ expect(result.errors).toHaveLength(0);
21
+ });
22
+
23
+ it('should validate a valid index file with linked modules', async () => {
24
+ const result = await validatePlanningDoc(join(fixturesDir, 'valid-index.aps.md'));
25
+
26
+ expect(result.valid).toBe(true);
27
+ expect(result.errors).toHaveLength(0);
28
+ });
29
+ });
30
+
31
+ describe('required-sections rule', () => {
32
+ it('should error when leaf spec is missing H1 title', async () => {
33
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-h1.aps.md'));
34
+
35
+ expect(result.valid).toBe(false);
36
+ expect(result.errors.some((e) => e.rule === 'required-sections')).toBe(true);
37
+ expect(result.errors.some((e) => e.message.includes('H1 title'))).toBe(true);
38
+ });
39
+
40
+ it('should error when leaf spec is missing ## Tasks section', async () => {
41
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-tasks-section.aps.md'));
42
+
43
+ expect(result.valid).toBe(false);
44
+ expect(result.errors.some((e) => e.rule === 'required-sections')).toBe(true);
45
+ expect(result.errors.some((e) => e.message.includes('## Tasks'))).toBe(true);
46
+ });
47
+
48
+ it('should error when index file is missing ## Modules section', async () => {
49
+ // This file doesn't have ## Modules, so it's treated as a leaf spec
50
+ // and will error because it's missing ## Tasks
51
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-modules-section.aps.md'));
52
+
53
+ expect(result.valid).toBe(false);
54
+ expect(result.errors.some((e) => e.rule === 'required-sections')).toBe(true);
55
+ });
56
+ });
57
+
58
+ describe('task-format rule', () => {
59
+ it('should error when task heading has invalid format', async () => {
60
+ const result = await validatePlanningDoc(join(fixturesDir, 'invalid-task-id.aps.md'));
61
+
62
+ expect(result.valid).toBe(false);
63
+ expect(result.errors.some((e) => e.rule === 'task-format')).toBe(true);
64
+ });
65
+
66
+ it('should error on lowercase task IDs', async () => {
67
+ const result = await validatePlanningDoc(join(fixturesDir, 'invalid-task-id.aps.md'));
68
+
69
+ const formatErrors = result.errors.filter((e) => e.rule === 'task-format');
70
+ expect(formatErrors.some((e) => e.message.includes('test-001'))).toBe(true);
71
+ });
72
+
73
+ it('should error on task IDs with non-3-digit numbers', async () => {
74
+ const result = await validatePlanningDoc(join(fixturesDir, 'invalid-task-id.aps.md'));
75
+
76
+ const formatErrors = result.errors.filter((e) => e.rule === 'task-format');
77
+ expect(formatErrors.some((e) => e.message.includes('T-1'))).toBe(true);
78
+ });
79
+
80
+ it('should error on task IDs with scope longer than 10 characters', async () => {
81
+ const result = await validatePlanningDoc(join(fixturesDir, 'invalid-task-id.aps.md'));
82
+
83
+ const formatErrors = result.errors.filter((e) => e.rule === 'task-format');
84
+ expect(formatErrors.some((e) => e.message.includes('VERYLONGSCOPE123-001'))).toBe(true);
85
+ });
86
+ });
87
+
88
+ describe('task-intent rule', () => {
89
+ it('should error when task is missing Intent field', async () => {
90
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-intent.aps.md'));
91
+
92
+ expect(result.valid).toBe(false);
93
+ expect(result.errors.some((e) => e.rule === 'task-intent')).toBe(true);
94
+ expect(result.errors.some((e) => e.message.includes('Intent'))).toBe(true);
95
+ });
96
+ });
97
+
98
+ describe('missing-confidence rule (warning)', () => {
99
+ it('should warn when task is missing Confidence field', async () => {
100
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-confidence.aps.md'));
101
+
102
+ // Should be valid (warnings don't invalidate)
103
+ expect(result.valid).toBe(true);
104
+ expect(result.warnings.some((w) => w.rule === 'missing-confidence')).toBe(true);
105
+ expect(result.warnings.some((w) => w.message.includes('Confidence'))).toBe(true);
106
+ });
107
+ });
108
+
109
+ describe('broken-links rule', () => {
110
+ it('should error when module link points to non-existent file', async () => {
111
+ const result = await validatePlanningDoc(join(fixturesDir, 'broken-links.aps.md'));
112
+
113
+ expect(result.valid).toBe(false);
114
+ expect(result.errors.some((e) => e.rule === 'broken-links')).toBe(true);
115
+ expect(result.errors.some((e) => e.message.includes('nonexistent'))).toBe(true);
116
+ });
117
+ });
118
+
119
+ describe('duplicate-ids rule', () => {
120
+ it('should error when same task ID appears in multiple modules', async () => {
121
+ const result = await validatePlanningDoc(join(fixturesDir, 'duplicate-ids-index.aps.md'));
122
+
123
+ expect(result.valid).toBe(false);
124
+ expect(result.errors.some((e) => e.rule === 'duplicate-ids')).toBe(true);
125
+ expect(result.errors.some((e) => e.message.includes('DUP-001'))).toBe(true);
126
+ });
127
+ });
128
+
129
+ describe('circular-dependencies rule', () => {
130
+ it('should error when modules have circular dependencies', async () => {
131
+ const result = await validatePlanningDoc(join(fixturesDir, 'circular-deps-index.aps.md'));
132
+
133
+ expect(result.valid).toBe(false);
134
+ expect(result.errors.some((e) => e.rule === 'circular-dependencies')).toBe(true);
135
+ expect(result.errors.some((e) => e.message.includes('Circular dependency'))).toBe(true);
136
+ });
137
+ });
138
+
139
+ describe('scope-mismatch rule (warning)', () => {
140
+ it('should warn when task ID scope prefix does not match module scope', async () => {
141
+ const result = await validatePlanningDoc(join(fixturesDir, 'scope-mismatch.aps.md'));
142
+
143
+ // Should be valid (warnings don't invalidate)
144
+ expect(result.valid).toBe(true);
145
+ expect(result.warnings.some((w) => w.rule === 'scope-mismatch')).toBe(true);
146
+ expect(result.warnings.some((w) => w.message.includes('AUTH-001'))).toBe(true);
147
+ expect(result.warnings.some((w) => w.message.includes('TEST'))).toBe(true);
148
+ });
149
+
150
+ it('should not warn when task ID scope prefix matches module scope', async () => {
151
+ const result = await validatePlanningDoc(join(fixturesDir, 'scope-mismatch.aps.md'));
152
+
153
+ // TEST-001 should not generate a scope-mismatch warning
154
+ const testScopeWarnings = result.warnings.filter(
155
+ (w) => w.rule === 'scope-mismatch' && w.message.includes('TEST-001')
156
+ );
157
+ expect(testScopeWarnings).toHaveLength(0);
158
+ });
159
+ });
160
+
161
+ describe('skipRules option', () => {
162
+ it('should skip specified rules', async () => {
163
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-intent.aps.md'), {
164
+ skipRules: ['task-intent', 'missing-confidence'],
165
+ });
166
+
167
+ expect(result.errors.some((e) => e.rule === 'task-intent')).toBe(false);
168
+ expect(result.warnings.some((w) => w.rule === 'missing-confidence')).toBe(false);
169
+ });
170
+ });
171
+
172
+ describe('recursive option', () => {
173
+ it('should not validate linked modules when recursive is false', async () => {
174
+ const result = await validatePlanningDoc(join(fixturesDir, 'broken-links.aps.md'), {
175
+ recursive: false,
176
+ });
177
+
178
+ // When not recursive, broken links are not checked
179
+ expect(result.errors.some((e) => e.rule === 'broken-links')).toBe(false);
180
+ });
181
+ });
182
+
183
+ describe('file-readable rule', () => {
184
+ it('should error when file does not exist', async () => {
185
+ const result = await validatePlanningDoc('/nonexistent/path/file.aps.md');
186
+
187
+ expect(result.valid).toBe(false);
188
+ expect(result.errors.some((e) => e.rule === 'file-readable')).toBe(true);
189
+ });
190
+ });
191
+ });
192
+
193
+ describe('formatValidationIssues', () => {
194
+ it('should format issues for display', async () => {
195
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-intent.aps.md'));
196
+ const formatted = formatValidationIssues(result);
197
+
198
+ expect(formatted).toContain('[ERROR]');
199
+ expect(formatted).toContain('Intent');
200
+ expect(formatted).toContain('error(s)');
201
+ expect(formatted).toContain('warning(s)');
202
+ });
203
+
204
+ it('should return "No issues found" for valid documents', async () => {
205
+ const result = await validatePlanningDoc(join(fixturesDir, 'valid-leaf.aps.md'));
206
+ // Valid leaf spec might still have warnings (e.g., missing confidence)
207
+ // so we test the "no issues" case with a mock
208
+ expect(result.valid).toBe(true);
209
+
210
+ // Test the "no issues" case with a mock result
211
+ const mockResult: ValidationResult = {
212
+ valid: true,
213
+ issues: [],
214
+ errors: [],
215
+ warnings: [],
216
+ };
217
+ const mockFormatted = formatValidationIssues(mockResult);
218
+ expect(mockFormatted).toBe('No issues found.');
219
+ });
220
+
221
+ it('should include context in formatted output', async () => {
222
+ const result = await validatePlanningDoc(join(fixturesDir, 'scope-mismatch.aps.md'));
223
+ const output = formatValidationIssues(result);
224
+
225
+ expect(output).toContain('Module scope:');
226
+ });
227
+
228
+ it('should include line numbers when available', async () => {
229
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-intent.aps.md'));
230
+ const formatted = formatValidationIssues(result);
231
+
232
+ // Should contain file path with line number
233
+ expect(formatted).toMatch(/\.aps\.md:\d+/);
234
+ });
235
+ });
236
+
237
+ describe('ValidationResult structure', () => {
238
+ it('should separate errors and warnings', async () => {
239
+ const result = await validatePlanningDoc(join(fixturesDir, 'scope-mismatch.aps.md'));
240
+
241
+ expect(result.issues.length).toBe(result.errors.length + result.warnings.length);
242
+ expect(result.errors.every((e) => e.severity === 'error')).toBe(true);
243
+ expect(result.warnings.every((w) => w.severity === 'warning')).toBe(true);
244
+ });
245
+
246
+ it('should mark as valid when only warnings exist', async () => {
247
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-confidence.aps.md'));
248
+
249
+ expect(result.valid).toBe(true);
250
+ expect(result.warnings.length).toBeGreaterThan(0);
251
+ expect(result.errors).toHaveLength(0);
252
+ });
253
+
254
+ it('should mark as invalid when errors exist', async () => {
255
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-intent.aps.md'));
256
+
257
+ expect(result.valid).toBe(false);
258
+ expect(result.errors.length).toBeGreaterThan(0);
259
+ });
260
+
261
+ it('should include path and lineNumber in issues where applicable', async () => {
262
+ const result = await validatePlanningDoc(join(fixturesDir, 'missing-intent.aps.md'));
263
+
264
+ const issueWithLocation = result.issues.find((i) => i.path && i.lineNumber);
265
+ expect(issueWithLocation).toBeDefined();
266
+ expect(issueWithLocation?.path).toContain('missing-intent.aps.md');
267
+ expect(typeof issueWithLocation?.lineNumber).toBe('number');
268
+ });
269
+ });
@@ -0,0 +1,94 @@
1
+ # APS Index — [Project Name]
2
+
3
+ ## Problem & Success Criteria
4
+
5
+ **Problem:** [What problem are we solving? Why does this work matter?]
6
+
7
+ **Success Criteria:**
8
+
9
+ - [ ] [Measurable outcome 1]
10
+ - [ ] [Measurable outcome 2]
11
+ - [ ] [How we know we're done]
12
+
13
+ ## Scope
14
+
15
+ **In Scope:**
16
+
17
+ - [What this plan covers]
18
+ - [Boundaries of work]
19
+
20
+ **Out of Scope:**
21
+
22
+ - [What this plan explicitly excludes]
23
+ - [Things deferred to future work]
24
+
25
+ ## System Map
26
+
27
+ [High-level view of modules and their relationships]
28
+
29
+ ```
30
+ [Module A] ──→ [Module B] ──→ [Module C]
31
+ ↑ ↓
32
+ [External Service] [Database]
33
+ ```
34
+
35
+ ## Milestones
36
+
37
+ ### M1: [Milestone Name]
38
+
39
+ - [What's included]
40
+ - Modules: [module-a, module-b]
41
+ - Target: [date]
42
+
43
+ ### M2: [Milestone Name]
44
+
45
+ - [What's included]
46
+ - Modules: [module-c]
47
+ - Target: [date]
48
+
49
+ ## Modules
50
+
51
+ ### [module-id]
52
+
53
+ - **Path:** [./modules/[module-name].aps.md](./modules/[module-name].aps.md)
54
+ - **Scope:** [SCOPE]
55
+ - **Owner:** @[username]
56
+ - **Status:** Draft
57
+ - **Priority:** [low|medium|high]
58
+ - **Tags:** [tag1, tag2]
59
+ - **Dependencies:** [other-module-id]
60
+
61
+ ### [another-module-id]
62
+
63
+ - **Path:**
64
+ [./modules/[another-module].aps.md](./modules/[another-module].aps.md)
65
+ - **Scope:** [SCOPE2]
66
+ - **Owner:** @[username]
67
+ - **Status:** Draft
68
+ - **Priority:** [low|medium|high]
69
+ - **Tags:** [tag1, tag2]
70
+ - **Dependencies:** (none)
71
+
72
+ ## Epics
73
+
74
+ ### [epic-id]
75
+
76
+ - **Path:** [./epics/[epic-name].aps.md](./epics/[epic-name].aps.md)
77
+ - **Owner:** @[username]
78
+ - **Modules:** [module-id-1, module-id-2]
79
+ - **Milestone:** M1
80
+
81
+ ## Decisions
82
+
83
+ - **D-001:** [Short decision] — [rationale] ([ADR-001](./decisions/ADR-001.md))
84
+ - **D-002:** [Another decision] — [rationale]
85
+
86
+ ## Risks
87
+
88
+ - **R-001:** [Risk description] — Mitigation: [approach]
89
+ - **R-002:** [Risk description] — Mitigation: [approach]
90
+
91
+ ## Open Questions
92
+
93
+ - [Unresolved question 1]
94
+ - [Unresolved question 2]
@@ -0,0 +1,16 @@
1
+ # [Plan Title]
2
+
3
+ ## Modules
4
+
5
+ ### [module-id]
6
+
7
+ - **Path:** [./modules/[module-name].aps.md](./modules/[module-name].aps.md)
8
+ - **Scope:** [SCOPE]
9
+ - **Owner:** @[username]
10
+
11
+ ### [another-module-id]
12
+
13
+ - **Path:**
14
+ [./modules/[another-module].aps.md](./modules/[another-module].aps.md)
15
+ - **Scope:** [SCOPE2]
16
+ - **Owner:** @[username]
@@ -0,0 +1,63 @@
1
+ # [Plan Title]
2
+
3
+ ## Problem & Success Criteria
4
+
5
+ **Problem:** [What problem are we solving? Why does this work matter?]
6
+
7
+ **Success Criteria:**
8
+
9
+ - [ ] [Measurable outcome 1]
10
+ - [ ] [Measurable outcome 2]
11
+ - [ ] [How we know we're done]
12
+
13
+ ## System Map
14
+
15
+ [High-level view of modules and their relationships]
16
+
17
+ - **[module-a]** → depends on → **[module-b]**
18
+ - **[module-c]** — standalone
19
+
20
+ ## Milestones
21
+
22
+ ### M1: [Milestone Name]
23
+
24
+ - [What's included]
25
+ - Target: [date or modules/features]
26
+
27
+ ### M2: [Milestone Name]
28
+
29
+ - [What's included]
30
+ - Target: [date or modules/features]
31
+
32
+ ## Modules
33
+
34
+ ### [module-id]
35
+
36
+ - **Path:** [./modules/[module-name].aps.md](./modules/[module-name].aps.md)
37
+ - **Scope:** [SCOPE]
38
+ - **Owner:** @[username]
39
+ - **Status:** Draft
40
+ - **Priority:** [low|medium|high]
41
+ - **Tags:** [tag1, tag2]
42
+ - **Dependencies:** [other-module-id]
43
+
44
+ ### [another-module-id]
45
+
46
+ - **Path:**
47
+ [./modules/[another-module].aps.md](./modules/[another-module].aps.md)
48
+ - **Scope:** [SCOPE2]
49
+ - **Owner:** @[username]
50
+ - **Status:** Draft
51
+ - **Priority:** [low|medium|high]
52
+ - **Tags:** [tag1, tag2]
53
+ - **Dependencies:** (none)
54
+
55
+ ## Decisions
56
+
57
+ - **D-001:** [Short decision] — [rationale] ([ADR-001](./decisions/ADR-001.md))
58
+ - **D-002:** [Another decision] — [rationale]
59
+
60
+ ## Open Questions
61
+
62
+ - [Unresolved question 1]
63
+ - [Unresolved question 2]
@@ -0,0 +1,76 @@
1
+ # Module APS — [Module Name]
2
+
3
+ **Scope:** [SCOPE] **Owner:** @[username] **Priority:** [low|medium|high]
4
+
5
+ ## Purpose
6
+
7
+ [Why this module exists and what problem it solves. The "why" behind this work.]
8
+
9
+ ## In Scope / Out of Scope
10
+
11
+ **In Scope:**
12
+
13
+ - [What this module WILL do]
14
+ - [Boundaries of responsibility]
15
+ - [Features included]
16
+
17
+ **Out of Scope:**
18
+
19
+ - [What this module will NOT do]
20
+ - [Things that belong elsewhere]
21
+ - [Explicit exclusions]
22
+
23
+ ## Assumptions
24
+
25
+ - [Assumption 1] — Confidence: [low|medium|high]
26
+ - [Assumption 2] — Confidence: [low|medium|high]
27
+
28
+ ## Interfaces
29
+
30
+ **Depends on:**
31
+
32
+ - [Service/Module name] — [what we need from it]
33
+ - [External API] — [what we consume]
34
+
35
+ **Exposes:**
36
+
37
+ - [Endpoint/API] — [what others can use]
38
+ - [Event/Hook] — [what we publish]
39
+
40
+ ## Tasks
41
+
42
+ ### [SCOPE]-001: [Task title]
43
+
44
+ **Intent:** [Clear statement of what this task aims to achieve] **Expected
45
+ Outcome:** [What success looks like] **Confidence:** [low|medium|high] **Link:**
46
+ [PROJ-123](https://jira.example.com/browse/PROJ-123) **Scopes:** [SCOPE1,
47
+ SCOPE2] **Tags:** [tag1, tag2, tag3] **Dependencies:** [SCOPE-XXX, OTHER-YYY]
48
+ **Inputs:**
49
+
50
+ - [Required input 1]
51
+ - [Required input 2]
52
+
53
+ ### [SCOPE]-002: [Another task]
54
+
55
+ **Intent:** [What this task does] **Expected Outcome:** [Success criteria]
56
+ **Confidence:** [low|medium|high] **Link:**
57
+ [PROJ-124](https://jira.example.com/browse/PROJ-124) **Scopes:** [SCOPE]
58
+ **Dependencies:** [SCOPE]-001
59
+
60
+ ## Decisions
61
+
62
+ - **D-001:** [Short decision] — [rationale] ([ADR-001](../decisions/ADR-001.md))
63
+ - **D-002:** [Another decision] — [rationale]
64
+
65
+ ## Risks
66
+
67
+ - **R-001:** [Risk description] — Mitigation: [approach]
68
+
69
+ ## Open Questions
70
+
71
+ - [Unresolved question about this module]
72
+
73
+ ## Notes
74
+
75
+ - [Additional context or considerations]
76
+ - [Links to relevant resources]
@@ -0,0 +1,14 @@
1
+ # [Module Title]
2
+
3
+ **Scope:** [SCOPE] **Owner:** @[username]
4
+
5
+ ## Tasks
6
+
7
+ ### [SCOPE]-001: [Task title]
8
+
9
+ **Intent:** [What this task aims to achieve] **Confidence:** [low|medium|high]
10
+
11
+ ### [SCOPE]-002: [Another task]
12
+
13
+ **Intent:** [What this task does] **Confidence:** [low|medium|high]
14
+ **Dependencies:** [SCOPE]-001
@@ -0,0 +1,55 @@
1
+ # [Module Title]
2
+
3
+ **Scope:** [SCOPE] **Owner:** @[username] **Priority:** [low|medium|high]
4
+
5
+ ## Purpose
6
+
7
+ [Why this module exists and what problem it solves]
8
+
9
+ ## In Scope / Out of Scope
10
+
11
+ **In Scope:**
12
+
13
+ - [What this module WILL do]
14
+ - [Boundaries of responsibility]
15
+
16
+ **Out of Scope:**
17
+
18
+ - [What this module will NOT do]
19
+ - [Things that belong elsewhere]
20
+
21
+ ## Interfaces
22
+
23
+ **Depends on:**
24
+
25
+ - [Service/Module name] — [what we need from it]
26
+
27
+ **Exposes:**
28
+
29
+ - [Endpoint/API] — [what others can use]
30
+
31
+ ## Tasks
32
+
33
+ ### [SCOPE]-001: [Task title]
34
+
35
+ **Intent:** [Clear statement of what this task aims to achieve] **Expected
36
+ Outcome:** [What success looks like] **Confidence:** [low|medium|high] **Link:**
37
+ [PROJ-123](https://jira.example.com/browse/PROJ-123) **Scopes:** [SCOPE1,
38
+ SCOPE2] **Tags:** [tag1, tag2, tag3] **Dependencies:** [SCOPE-XXX, OTHER-YYY]
39
+ **Inputs:**
40
+
41
+ - [Required input 1]
42
+ - [Required input 2]
43
+
44
+ ### [SCOPE]-002: [Another task]
45
+
46
+ **Intent:** [What this task does] **Confidence:** [low|medium|high] **Scopes:**
47
+ [SCOPE] **Dependencies:** [SCOPE]-001
48
+
49
+ ## Decisions
50
+
51
+ - **D-001:** [Short decision] — [rationale]
52
+
53
+ ## Notes
54
+
55
+ - [Additional context or considerations]
@@ -0,0 +1,56 @@
1
+ # Feature: [Feature Name]
2
+
3
+ **Scope:** [SCOPE] **Owner:** @[username] **Priority:** [low|medium|high]
4
+
5
+ ## Purpose
6
+
7
+ [Why we're building this feature and what problem it solves]
8
+
9
+ ## Success Criteria
10
+
11
+ - [ ] [Measurable outcome 1]
12
+ - [ ] [Measurable outcome 2]
13
+ - [ ] [How we know we're done]
14
+
15
+ ## In Scope / Out of Scope
16
+
17
+ **In Scope:**
18
+
19
+ - [What this feature WILL do]
20
+
21
+ **Out of Scope:**
22
+
23
+ - [What this feature will NOT do]
24
+
25
+ ## Assumptions
26
+
27
+ - [Assumption 1] — Confidence: [low|medium|high]
28
+
29
+ ## Tasks
30
+
31
+ ### [SCOPE]-001: [First task]
32
+
33
+ **Intent:** [What this task achieves] **Expected Outcome:** [Success criteria]
34
+ **Confidence:** [low|medium|high] **Link:**
35
+ [PROJ-123](https://jira.example.com/browse/PROJ-123) **Scopes:** [SCOPE]
36
+ **Tags:** [tag1, tag2] **Inputs:**
37
+
38
+ - [Required input 1]
39
+
40
+ ### [SCOPE]-002: [Second task]
41
+
42
+ **Intent:** [What this task achieves] **Expected Outcome:** [Success criteria]
43
+ **Confidence:** [low|medium|high] **Scopes:** [SCOPE] **Dependencies:**
44
+ [SCOPE]-001
45
+
46
+ ## Decisions
47
+
48
+ - **D-001:** [Decision] — [rationale]
49
+
50
+ ## Open Questions
51
+
52
+ - [Unresolved question]
53
+
54
+ ## Notes
55
+
56
+ - [Additional notes or considerations]
@@ -0,0 +1,14 @@
1
+ # [Feature Name]
2
+
3
+ **Scope:** [SCOPE] **Owner:** @[username]
4
+
5
+ ## Tasks
6
+
7
+ ### [SCOPE]-001: [First task]
8
+
9
+ **Intent:** [What this task achieves] **Confidence:** [low|medium|high]
10
+
11
+ ### [SCOPE]-002: [Second task]
12
+
13
+ **Intent:** [What this task achieves] **Confidence:** [low|medium|high]
14
+ **Dependencies:** [SCOPE]-001
@@ -0,0 +1,30 @@
1
+ # Feature: [Feature Name]
2
+
3
+ **Scope:** [SCOPE] **Owner:** @[username] **Priority:** [low|medium|high]
4
+
5
+ ## Purpose
6
+
7
+ [Why we're building this feature and what problem it solves]
8
+
9
+ ## Success Criteria
10
+
11
+ - [ ] [Measurable outcome 1]
12
+ - [ ] [Measurable outcome 2]
13
+
14
+ ## Tasks
15
+
16
+ ### [SCOPE]-001: [First task]
17
+
18
+ **Intent:** [What this task achieves] **Expected Outcome:** [Success criteria]
19
+ **Confidence:** [low|medium|high] **Link:**
20
+ [PROJ-123](https://jira.example.com/browse/PROJ-123) **Scopes:** [SCOPE]
21
+ **Tags:** [tag1, tag2]
22
+
23
+ ### [SCOPE]-002: [Second task]
24
+
25
+ **Intent:** [What this task achieves] **Confidence:** [low|medium|high]
26
+ **Scopes:** [SCOPE] **Dependencies:** [SCOPE]-001
27
+
28
+ ## Notes
29
+
30
+ - [Additional notes or considerations]