@shahmarasy/prodo 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 (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/bin/prodo.cjs +6 -0
  4. package/dist/agent-command-installer.d.ts +4 -0
  5. package/dist/agent-command-installer.js +158 -0
  6. package/dist/agents.d.ts +15 -0
  7. package/dist/agents.js +47 -0
  8. package/dist/artifact-registry.d.ts +11 -0
  9. package/dist/artifact-registry.js +49 -0
  10. package/dist/artifacts.d.ts +9 -0
  11. package/dist/artifacts.js +514 -0
  12. package/dist/cli.d.ts +9 -0
  13. package/dist/cli.js +305 -0
  14. package/dist/consistency.d.ts +8 -0
  15. package/dist/consistency.js +268 -0
  16. package/dist/constants.d.ts +7 -0
  17. package/dist/constants.js +64 -0
  18. package/dist/doctor.d.ts +1 -0
  19. package/dist/doctor.js +123 -0
  20. package/dist/errors.d.ts +3 -0
  21. package/dist/errors.js +10 -0
  22. package/dist/hook-executor.d.ts +1 -0
  23. package/dist/hook-executor.js +175 -0
  24. package/dist/init-tui.d.ts +21 -0
  25. package/dist/init-tui.js +161 -0
  26. package/dist/init.d.ts +10 -0
  27. package/dist/init.js +307 -0
  28. package/dist/markdown.d.ts +11 -0
  29. package/dist/markdown.js +66 -0
  30. package/dist/normalize.d.ts +7 -0
  31. package/dist/normalize.js +73 -0
  32. package/dist/normalized-brief.d.ts +39 -0
  33. package/dist/normalized-brief.js +170 -0
  34. package/dist/output-index.d.ts +13 -0
  35. package/dist/output-index.js +55 -0
  36. package/dist/paths.d.ts +16 -0
  37. package/dist/paths.js +76 -0
  38. package/dist/preset-loader.d.ts +4 -0
  39. package/dist/preset-loader.js +210 -0
  40. package/dist/project-config.d.ts +14 -0
  41. package/dist/project-config.js +69 -0
  42. package/dist/providers/index.d.ts +2 -0
  43. package/dist/providers/index.js +12 -0
  44. package/dist/providers/mock-provider.d.ts +7 -0
  45. package/dist/providers/mock-provider.js +168 -0
  46. package/dist/providers/openai-provider.d.ts +11 -0
  47. package/dist/providers/openai-provider.js +69 -0
  48. package/dist/registry.d.ts +13 -0
  49. package/dist/registry.js +115 -0
  50. package/dist/settings.d.ts +6 -0
  51. package/dist/settings.js +34 -0
  52. package/dist/template-resolver.d.ts +11 -0
  53. package/dist/template-resolver.js +28 -0
  54. package/dist/templates.d.ts +33 -0
  55. package/dist/templates.js +428 -0
  56. package/dist/types.d.ts +35 -0
  57. package/dist/types.js +5 -0
  58. package/dist/utils.d.ts +6 -0
  59. package/dist/utils.js +53 -0
  60. package/dist/validate.d.ts +9 -0
  61. package/dist/validate.js +226 -0
  62. package/dist/validator.d.ts +5 -0
  63. package/dist/validator.js +80 -0
  64. package/dist/version.d.ts +1 -0
  65. package/dist/version.js +30 -0
  66. package/dist/workflow-commands.d.ts +7 -0
  67. package/dist/workflow-commands.js +28 -0
  68. package/package.json +45 -0
  69. package/presets/fintech/preset.json +1 -0
  70. package/presets/fintech/prompts/prd.md +3 -0
  71. package/presets/marketplace/preset.json +1 -0
  72. package/presets/marketplace/prompts/prd.md +3 -0
  73. package/presets/saas/preset.json +1 -0
  74. package/presets/saas/prompts/prd.md +3 -0
  75. package/src/agent-command-installer.ts +174 -0
  76. package/src/agents.ts +56 -0
  77. package/src/artifact-registry.ts +69 -0
  78. package/src/artifacts.ts +606 -0
  79. package/src/cli.ts +322 -0
  80. package/src/consistency.ts +303 -0
  81. package/src/constants.ts +72 -0
  82. package/src/doctor.ts +137 -0
  83. package/src/errors.ts +7 -0
  84. package/src/hook-executor.ts +196 -0
  85. package/src/init-tui.ts +193 -0
  86. package/src/init.ts +375 -0
  87. package/src/markdown.ts +73 -0
  88. package/src/normalize.ts +89 -0
  89. package/src/normalized-brief.ts +206 -0
  90. package/src/output-index.ts +59 -0
  91. package/src/paths.ts +72 -0
  92. package/src/preset-loader.ts +237 -0
  93. package/src/project-config.ts +78 -0
  94. package/src/providers/index.ts +12 -0
  95. package/src/providers/mock-provider.ts +188 -0
  96. package/src/providers/openai-provider.ts +87 -0
  97. package/src/registry.ts +119 -0
  98. package/src/settings.ts +34 -0
  99. package/src/template-resolver.ts +33 -0
  100. package/src/templates.ts +440 -0
  101. package/src/types.ts +46 -0
  102. package/src/utils.ts +50 -0
  103. package/src/validate.ts +246 -0
  104. package/src/validator.ts +96 -0
  105. package/src/version.ts +24 -0
  106. package/src/workflow-commands.ts +31 -0
  107. package/templates/artifacts/prd.md +219 -0
  108. package/templates/artifacts/stories.md +49 -0
  109. package/templates/artifacts/techspec.md +42 -0
  110. package/templates/artifacts/wireframe.html +260 -0
  111. package/templates/artifacts/wireframe.md +22 -0
  112. package/templates/artifacts/workflow.md +22 -0
  113. package/templates/artifacts/workflow.mmd +6 -0
  114. package/templates/commands/prodo-normalize.md +24 -0
  115. package/templates/commands/prodo-prd.md +24 -0
  116. package/templates/commands/prodo-stories.md +24 -0
  117. package/templates/commands/prodo-techspec.md +24 -0
  118. package/templates/commands/prodo-validate.md +24 -0
  119. package/templates/commands/prodo-wireframe.md +24 -0
  120. package/templates/commands/prodo-workflow.md +24 -0
@@ -0,0 +1,246 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import matter from "gray-matter";
4
+ import { listArtifactDefinitions } from "./artifact-registry";
5
+ import { checkConsistency } from "./consistency";
6
+ import { UserError } from "./errors";
7
+ import { getActiveArtifactPath } from "./output-index";
8
+ import { normalizedBriefPath, outputDirPath, reportPath } from "./paths";
9
+ import { extractRequiredHeadingsFromTemplate, resolveTemplate } from "./template-resolver";
10
+ import type { ArtifactDoc, ArtifactType, ValidationIssue } from "./types";
11
+ import { ensureDir, fileExists, isPathInside, listFilesSortedByMtime, readJsonFile } from "./utils";
12
+ import { validateSchema } from "./validator";
13
+
14
+ type LoadedArtifact = {
15
+ type: ArtifactType;
16
+ file: string;
17
+ doc: ArtifactDoc;
18
+ };
19
+
20
+ function sidecarPath(filePath: string): string {
21
+ const parsed = path.parse(filePath);
22
+ return path.join(parsed.dir, `${parsed.name}.artifact.json`);
23
+ }
24
+
25
+ async function loadArtifactDoc(filePath: string): Promise<ArtifactDoc> {
26
+ const sidecar = sidecarPath(filePath);
27
+ if (await fileExists(sidecar)) {
28
+ const payload = await readJsonFile<Record<string, unknown>>(sidecar);
29
+ return {
30
+ frontmatter: (payload.frontmatter as Record<string, unknown>) ?? {},
31
+ body: typeof payload.body === "string" ? payload.body : ""
32
+ };
33
+ }
34
+ const raw = await fs.readFile(filePath, "utf8");
35
+ const parsed = matter(raw);
36
+ return {
37
+ frontmatter: parsed.data as Record<string, unknown>,
38
+ body: parsed.content
39
+ };
40
+ }
41
+
42
+ async function loadLatestArtifacts(cwd: string): Promise<LoadedArtifact[]> {
43
+ const defs = await listArtifactDefinitions(cwd);
44
+ const loaded: LoadedArtifact[] = [];
45
+ for (const def of defs) {
46
+ const type = def.name;
47
+ const active = await getActiveArtifactPath(cwd, type);
48
+ const fallback = async (): Promise<string | undefined> => {
49
+ const files = await listFilesSortedByMtime(outputDirPath(cwd, type, def.output_dir));
50
+ return files[0];
51
+ };
52
+ const latest = active ?? (await fallback());
53
+ if (!latest) continue;
54
+ const parsed = await loadArtifactDoc(latest);
55
+ loaded.push({
56
+ type,
57
+ file: latest,
58
+ doc: parsed
59
+ });
60
+ }
61
+ return loaded;
62
+ }
63
+
64
+ async function listHtmlFiles(dir: string): Promise<string[]> {
65
+ if (!(await fileExists(dir))) return [];
66
+ const entries = await fs.readdir(dir, { withFileTypes: true });
67
+ return entries
68
+ .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".html"))
69
+ .map((entry) => path.join(dir, entry.name));
70
+ }
71
+
72
+ function formatIssue(issue: ValidationIssue): string {
73
+ const location = issue.file ? ` (${issue.file})` : "";
74
+ const field = issue.field ? ` [${issue.field}]` : "";
75
+ const check = issue.check ? ` [${issue.check}]` : "";
76
+ const fix = issue.suggestion ? `\n Suggestion: ${issue.suggestion}` : "";
77
+ return `- [${issue.level.toUpperCase()}] ${issue.code}${check}${field}: ${issue.message}${location}${fix}`;
78
+ }
79
+
80
+ async function writeReport(targetPath: string, issues: ValidationIssue[]): Promise<void> {
81
+ const status = issues.some((issue) => issue.level === "error") ? "FAIL" : "PASS";
82
+ const schemaIssues = issues.filter((issue) => issue.check === "schema");
83
+ const coverageIssues = issues.filter((issue) => issue.check === "tag_coverage");
84
+ const relevanceIssues = issues.filter((issue) => issue.check === "contract_relevance");
85
+ const semanticIssues = issues.filter((issue) => issue.check === "semantic_consistency");
86
+ const gate = (set: ValidationIssue[]) => (set.some((issue) => issue.level === "error") ? "FAIL" : "PASS");
87
+ const content = [
88
+ "# Prodo Validation Report",
89
+ "",
90
+ `Status: **${status}**`,
91
+ `Generated at: ${new Date().toISOString()}`,
92
+ "",
93
+ "## Gate Results",
94
+ `- Schema pass: ${gate(schemaIssues)}`,
95
+ `- Tag coverage pass: ${gate(coverageIssues)}`,
96
+ `- Contract relevance pass: ${gate(relevanceIssues)}`,
97
+ `- Semantic consistency pass: ${gate(semanticIssues)}`,
98
+ "",
99
+ "## Findings",
100
+ issues.length === 0 ? "- No issues found." : issues.map(formatIssue).join("\n"),
101
+ ""
102
+ ].join("\n");
103
+
104
+ await ensureDir(path.dirname(targetPath));
105
+ await fs.writeFile(targetPath, content, "utf8");
106
+ }
107
+
108
+ export async function runValidate(
109
+ cwd: string,
110
+ options: { strict?: boolean; report?: string }
111
+ ): Promise<{ pass: boolean; reportPath: string; issues: ValidationIssue[] }> {
112
+ const normalizedPath = normalizedBriefPath(cwd);
113
+ if (!(await fileExists(normalizedPath))) {
114
+ throw new UserError("Missing `.prodo/briefs/normalized-brief.json`. Run `prodo-init` and create it first.");
115
+ }
116
+
117
+ const normalizedBrief = await readJsonFile<Record<string, unknown>>(normalizedPath);
118
+ const loaded = await loadLatestArtifacts(cwd);
119
+ const issues: ValidationIssue[] = [];
120
+
121
+ for (const artifact of loaded) {
122
+ const template = await resolveTemplate({
123
+ cwd,
124
+ artifactType: artifact.type
125
+ });
126
+ const headings = template ? extractRequiredHeadingsFromTemplate(template.content) : [];
127
+ const schemaCheck = await validateSchema(
128
+ cwd,
129
+ artifact.type,
130
+ artifact.doc,
131
+ headings
132
+ );
133
+ issues.push(...schemaCheck.issues.map((issue) => ({ ...issue, file: artifact.file })));
134
+
135
+ if (artifact.type === "workflow") {
136
+ const ext = path.extname(artifact.file).toLowerCase();
137
+ if (ext !== ".md") {
138
+ issues.push({
139
+ level: "error",
140
+ code: "workflow_markdown_missing",
141
+ check: "schema",
142
+ artifactType: artifact.type,
143
+ file: artifact.file,
144
+ message: "Workflow explanation artifact must be Markdown (.md).",
145
+ suggestion: "Regenerate workflow so explanation is written to .md."
146
+ });
147
+ }
148
+ const mmdPath = path.join(path.dirname(artifact.file), `${path.parse(artifact.file).name}.mmd`);
149
+ if (!(await fileExists(mmdPath))) {
150
+ issues.push({
151
+ level: "error",
152
+ code: "workflow_mermaid_missing",
153
+ check: "schema",
154
+ artifactType: artifact.type,
155
+ file: artifact.file,
156
+ message: "Workflow Mermaid companion file (.mmd) is missing.",
157
+ suggestion: "Regenerate workflow so markdown and .mmd are produced as a pair."
158
+ });
159
+ } else {
160
+ const mmdRaw = await fs.readFile(mmdPath, "utf8");
161
+ const mermaidLike = /(^|\n)\s*flowchart\s+/i.test(mmdRaw) || /(^|\n)\s*graph\s+/i.test(mmdRaw);
162
+ if (!mermaidLike) {
163
+ issues.push({
164
+ level: "error",
165
+ code: "workflow_mermaid_invalid",
166
+ check: "schema",
167
+ artifactType: artifact.type,
168
+ file: mmdPath,
169
+ message: "Workflow Mermaid file is invalid or prose-only.",
170
+ suggestion: "Ensure .mmd file contains valid Mermaid diagram syntax."
171
+ });
172
+ }
173
+ }
174
+ }
175
+
176
+ if (artifact.type === "wireframe") {
177
+ const ext = path.extname(artifact.file).toLowerCase();
178
+ if (ext !== ".md") {
179
+ issues.push({
180
+ level: "error",
181
+ code: "wireframe_markdown_missing",
182
+ check: "schema",
183
+ artifactType: artifact.type,
184
+ file: artifact.file,
185
+ message: "Wireframe explanation artifact must be Markdown (.md).",
186
+ suggestion: "Regenerate wireframe so explanation is written to .md."
187
+ });
188
+ }
189
+
190
+ const htmlPath = path.join(path.dirname(artifact.file), `${path.parse(artifact.file).name}.html`);
191
+ if (!(await fileExists(htmlPath))) {
192
+ issues.push({
193
+ level: "error",
194
+ code: "wireframe_html_missing",
195
+ check: "schema",
196
+ artifactType: artifact.type,
197
+ file: artifact.file,
198
+ message: "Wireframe HTML companion file is missing.",
199
+ suggestion: "Regenerate wireframe so markdown and .html are produced as a pair."
200
+ });
201
+ } else {
202
+ const htmlRaw = await fs.readFile(htmlPath, "utf8");
203
+ const htmlLooksValid = /<!doctype html>/i.test(htmlRaw) || /<html[\s>]/i.test(htmlRaw);
204
+ if (!htmlLooksValid) {
205
+ issues.push({
206
+ level: "error",
207
+ code: "wireframe_html_invalid",
208
+ check: "schema",
209
+ artifactType: artifact.type,
210
+ file: htmlPath,
211
+ message: "Wireframe output is not valid HTML content.",
212
+ suggestion: "Ensure wireframe companion HTML contains a valid document structure."
213
+ });
214
+ }
215
+ }
216
+
217
+ const htmlFiles = await listHtmlFiles(path.dirname(artifact.file));
218
+ if (htmlFiles.length < 1) {
219
+ issues.push({
220
+ level: "error",
221
+ code: "wireframe_screens_missing",
222
+ check: "schema",
223
+ artifactType: artifact.type,
224
+ file: artifact.file,
225
+ message: "Wireframe must include at least one HTML screen artifact.",
226
+ suggestion: "Regenerate wireframe to create paired .md and .html screen files."
227
+ });
228
+ }
229
+ }
230
+ }
231
+
232
+ issues.push(...(await checkConsistency(cwd, loaded, normalizedBrief)));
233
+ if (options.strict) {
234
+ for (const issue of issues) {
235
+ if (issue.level === "warning") issue.level = "error";
236
+ }
237
+ }
238
+
239
+ const finalReportPath = options.report ? path.resolve(cwd, options.report) : reportPath(cwd);
240
+ if (!isPathInside(path.join(cwd, "product-docs"), finalReportPath)) {
241
+ throw new UserError("Validation report must be inside `product-docs/`.");
242
+ }
243
+ await writeReport(finalReportPath, issues);
244
+ const pass = !issues.some((issue) => issue.level === "error");
245
+ return { pass, reportPath: finalReportPath, issues };
246
+ }
@@ -0,0 +1,96 @@
1
+ import fs from "node:fs/promises";
2
+ import Ajv2020 from "ajv/dist/2020";
3
+ import addFormats from "ajv-formats";
4
+ import yaml from "js-yaml";
5
+ import { sectionTextMap } from "./markdown";
6
+ import type { ArtifactDoc, ArtifactType, ValidationIssue } from "./types";
7
+ import { schemaPath } from "./paths";
8
+
9
+ type SchemaWithHeading = Record<string, unknown> & {
10
+ x_required_headings?: unknown;
11
+ };
12
+
13
+ const ajv = new Ajv2020({ allErrors: true, strict: false });
14
+ addFormats(ajv);
15
+
16
+ async function resolveSchema(
17
+ cwd: string,
18
+ artifactType: ArtifactType
19
+ ): Promise<SchemaWithHeading> {
20
+ const raw = await fs.readFile(schemaPath(cwd, artifactType), "utf8");
21
+ return yaml.load(raw) as SchemaWithHeading;
22
+ }
23
+
24
+ function requiredHeadingsFromSchema(schema: SchemaWithHeading): string[] {
25
+ if (!Array.isArray(schema.x_required_headings)) return [];
26
+ return schema.x_required_headings.filter((item): item is string => typeof item === "string");
27
+ }
28
+
29
+ export async function validateSchema(
30
+ cwd: string,
31
+ artifactType: ArtifactType,
32
+ doc: ArtifactDoc,
33
+ requiredHeadingsOverride?: string[]
34
+ ): Promise<{ issues: ValidationIssue[]; requiredHeadings: string[] }> {
35
+ const schema = await resolveSchema(cwd, artifactType);
36
+ const requiredHeadings =
37
+ requiredHeadingsOverride && requiredHeadingsOverride.length > 0
38
+ ? requiredHeadingsOverride
39
+ : requiredHeadingsFromSchema(schema);
40
+ const workingSchema: Record<string, unknown> = { ...schema };
41
+ delete workingSchema.x_required_headings;
42
+
43
+ const validate = ajv.compile(workingSchema);
44
+ const valid = validate(doc);
45
+ const issues: ValidationIssue[] = [];
46
+
47
+ if (!valid && validate.errors) {
48
+ for (const err of validate.errors) {
49
+ issues.push({
50
+ level: "error",
51
+ code: "schema_validation_failed",
52
+ check: "schema",
53
+ artifactType,
54
+ field: err.instancePath || err.schemaPath,
55
+ message: `Schema validation error: ${err.message ?? "unknown error"}`,
56
+ suggestion: "Adjust the generated content to satisfy schema requirements."
57
+ });
58
+ }
59
+ }
60
+
61
+ const sections = sectionTextMap(doc.body);
62
+ const trMode = String((doc.frontmatter as Record<string, unknown>).language ?? "").toLowerCase().startsWith("tr");
63
+ if (trMode) {
64
+ return { issues, requiredHeadings: [] };
65
+ }
66
+ for (const heading of requiredHeadings) {
67
+ if (!doc.body.includes(heading)) {
68
+ issues.push({
69
+ level: "error",
70
+ code: "missing_required_heading",
71
+ check: "schema",
72
+ artifactType,
73
+ field: heading,
74
+ message: `Required section missing: ${heading}`,
75
+ suggestion: "Regenerate or manually edit the artifact to include all required headings."
76
+ });
77
+ continue;
78
+ }
79
+
80
+ const content = sections.get(heading) ?? "";
81
+ const isPlaceholder = /(tbd|to be defined|i don't know|unknown|n\/a)/i.test(content);
82
+ if (content.trim().length < 20 || isPlaceholder) {
83
+ issues.push({
84
+ level: "error",
85
+ code: "weak_required_heading_content",
86
+ check: "schema",
87
+ artifactType,
88
+ field: heading,
89
+ message: `Section has weak or placeholder content: ${heading}`,
90
+ suggestion: "Replace placeholders with concrete, actionable details."
91
+ });
92
+ }
93
+ }
94
+
95
+ return { issues, requiredHeadings };
96
+ }
package/src/version.ts ADDED
@@ -0,0 +1,24 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { fileExists } from "./utils";
4
+
5
+ export async function readCliVersion(cwd: string): Promise<string> {
6
+ const candidates = [
7
+ path.join(cwd, "package.json"),
8
+ path.resolve(__dirname, "..", "package.json")
9
+ ];
10
+ for (const candidate of candidates) {
11
+ if (!(await fileExists(candidate))) continue;
12
+ try {
13
+ const raw = await fs.readFile(candidate, "utf8");
14
+ const parsed = JSON.parse(raw) as { version?: unknown };
15
+ if (typeof parsed.version === "string" && parsed.version.trim().length > 0) {
16
+ return parsed.version.trim();
17
+ }
18
+ } catch {
19
+ // ignore and continue
20
+ }
21
+ }
22
+ return "0.0.0";
23
+ }
24
+
@@ -0,0 +1,31 @@
1
+ export type WorkflowCommand = {
2
+ name: string;
3
+ cliSubcommand: string;
4
+ description: string;
5
+ };
6
+
7
+ const BASE_WORKFLOW_COMMANDS: WorkflowCommand[] = [
8
+ { name: "prodo-normalize", cliSubcommand: "normalize", description: "Normalize start brief into normalized brief JSON." },
9
+ { name: "prodo-prd", cliSubcommand: "prd", description: "Generate PRD artifact from normalized brief." },
10
+ { name: "prodo-workflow", cliSubcommand: "workflow", description: "Generate workflow artifact." },
11
+ { name: "prodo-wireframe", cliSubcommand: "wireframe", description: "Generate wireframe artifact." },
12
+ { name: "prodo-stories", cliSubcommand: "stories", description: "Generate stories artifact." },
13
+ { name: "prodo-techspec", cliSubcommand: "techspec", description: "Generate technical specification artifact." },
14
+ { name: "prodo-validate", cliSubcommand: "validate", description: "Run schema and cross-artifact consistency validation." }
15
+ ];
16
+
17
+ export const WORKFLOW_COMMANDS: WorkflowCommand[] = BASE_WORKFLOW_COMMANDS;
18
+
19
+ export function buildWorkflowCommands(artifactTypes: string[]): WorkflowCommand[] {
20
+ const commandByName = new Map<string, WorkflowCommand>(BASE_WORKFLOW_COMMANDS.map((item) => [item.name, item]));
21
+ for (const type of artifactTypes) {
22
+ const name = `prodo-${type}`;
23
+ if (commandByName.has(name)) continue;
24
+ commandByName.set(name, {
25
+ name,
26
+ cliSubcommand: type,
27
+ description: `Generate ${type} artifact from normalized brief.`
28
+ });
29
+ }
30
+ return Array.from(commandByName.values());
31
+ }
@@ -0,0 +1,219 @@
1
+ # Product Requirements Document (PRD)
2
+
3
+ ---
4
+
5
+ ## 1. Document Control
6
+
7
+ | Version | Date | Author | Description |
8
+ |--------|------|--------|-------------|
9
+ | v1.0 | {{date}} | {{author}} | Initial version |
10
+
11
+ ---
12
+
13
+ ## 2. Product Overview
14
+
15
+ ### 2.1 Product Summary
16
+
17
+ {{Provide a concise description of the product, what it does, and its core value proposition.}}
18
+
19
+ ### 2.2 Problem Statement
20
+
21
+ {{Describe the core problem being solved. Who is affected and why it matters.}}
22
+
23
+ ### 2.3 Goals & Objectives
24
+
25
+ - **G1:** {{Primary business/product goal}}
26
+ - **G2:** {{Secondary goal}}
27
+ - **G3:** {{Optional}}
28
+
29
+ ---
30
+
31
+ ## 3. Functional Requirements
32
+
33
+ ### 3.1 Core System Logic
34
+
35
+ #### Primary Features
36
+
37
+ - **FR-XX-01:** {{System must...}}
38
+ - **FR-XX-02:** {{System must...}}
39
+ - **FR-XX-03:** {{System must...}}
40
+
41
+ #### Advanced Rules / Variations
42
+
43
+ - **FR-XX-04:** {{Conditional logic / variations}}
44
+ - **FR-XX-05:** {{Behavior-based rules}}
45
+ - **FR-XX-06:** {{Special cases}}
46
+
47
+ ---
48
+
49
+ ### 3.2 Constraints & Limits
50
+
51
+ - **FR-XX-07:** {{Max limits}}
52
+ - **FR-XX-08:** {{Time-based limits}}
53
+ - **FR-XX-09:** {{Usage restrictions}}
54
+
55
+ ---
56
+
57
+ ### 3.3 Wallet / Balance / State Management (if applicable)
58
+
59
+ - **FR-STATE-01:** {{State update logic}}
60
+ - **FR-STATE-02:** {{User visibility}}
61
+ - **FR-STATE-03:** {{Separation of balances/categories}}
62
+
63
+ ---
64
+
65
+ ### 3.4 Exception & Recovery Handling
66
+
67
+ - **FR-ERR-01:** {{Full rollback / reversal logic}}
68
+ - **FR-ERR-02:** {{Partial correction logic}}
69
+ - **FR-ERR-03:** {{Negative state prevention}}
70
+ - **FR-ERR-04:** {{User notification for corrections}}
71
+
72
+ ---
73
+
74
+ ### 3.5 Notifications
75
+
76
+ - **FR-NOTIF-01:** {{Real-time notification}}
77
+ - **FR-NOTIF-02:** {{Special case notification}}
78
+ - **FR-NOTIF-03:** {{Periodic summary}}
79
+ - **FR-NOTIF-04:** {{Threshold/limit warnings}}
80
+
81
+ ---
82
+
83
+ ### 3.6 Admin / Backoffice
84
+
85
+ - **FR-ADMIN-01:** {{Rule management}}
86
+ - **FR-ADMIN-02:** {{Configuration control}}
87
+ - **FR-ADMIN-03:** {{Fraud/manual intervention}}
88
+ - **FR-ADMIN-04:** {{Audit & monitoring}}
89
+
90
+ ---
91
+
92
+ ## 4. Non-Functional Requirements
93
+
94
+ ### 4.1 Performance
95
+
96
+ - **NFR-PERF-01:** {{Latency requirements}}
97
+ - **NFR-PERF-02:** {{Throughput requirements}}
98
+ - **NFR-PERF-03:** {{API performance}}
99
+
100
+ ---
101
+
102
+ ### 4.2 Security
103
+
104
+ - **NFR-SEC-01:** {{Fraud detection / abuse prevention}}
105
+ - **NFR-SEC-02:** {{Auditability}}
106
+ - **NFR-SEC-03:** {{Encryption / data protection}}
107
+
108
+ ---
109
+
110
+ ### 4.3 Reliability
111
+
112
+ - **NFR-REL-01:** {{Uptime}}
113
+ - **NFR-REL-02:** {{Failure handling}}
114
+ - **NFR-REL-03:** {{Graceful degradation}}
115
+
116
+ ---
117
+
118
+ ### 4.4 Scalability
119
+
120
+ - **NFR-SCAL-01:** {{Horizontal scalability}}
121
+ - **NFR-SCAL-02:** {{Traffic spikes}}
122
+
123
+ ---
124
+
125
+ ## 5. Data Requirements
126
+
127
+ ### 5.1 Data Entities
128
+
129
+ - {{Entity 1}}
130
+ - {{Entity 2}}
131
+ - {{Entity 3}}
132
+
133
+ ---
134
+
135
+ ### 5.2 Data Retention
136
+
137
+ - {{Retention rule 1}}
138
+ - {{Retention rule 2}}
139
+
140
+ ---
141
+
142
+ ## 6. Integrations & Constraints
143
+
144
+ ### 6.1 External Integrations
145
+
146
+ - {{Integration 1}}
147
+ - {{Integration 2}}
148
+ - {{Integration 3}}
149
+
150
+ ---
151
+
152
+ ### 6.2 Technical Constraints
153
+
154
+ - {{Platform constraints}}
155
+ - {{Database constraints}}
156
+ - {{Compliance constraints}}
157
+
158
+ ---
159
+
160
+ ### 6.3 Performance Targets
161
+
162
+ | Metric | Target | Measurement |
163
+ |--------|--------|-------------|
164
+ | {{metric}} | {{target}} | {{method}} |
165
+
166
+ ---
167
+
168
+ ## 7. User Flows
169
+
170
+ ### Flow 1: {{Flow Name}}
171
+
172
+ 1. {{Step}}
173
+ 2. {{Step}}
174
+ 3. {{Step}}
175
+
176
+ ---
177
+
178
+ ### Flow 2: {{Flow Name}}
179
+
180
+ 1. {{Step}}
181
+ 2. {{Step}}
182
+
183
+ ---
184
+
185
+ ## 8. Compliance & Data Protection (if applicable)
186
+
187
+ ### 8.1 Consent Mechanism
188
+
189
+ {{Describe user consent flow}}
190
+
191
+ ---
192
+
193
+ ### 8.2 Legal Notes
194
+
195
+ {{Regulatory requirements and compliance context}}
196
+
197
+ ---
198
+
199
+ ## 9. Success Metrics
200
+
201
+ ### 9.1 Product KPIs
202
+
203
+ - {{KPI 1}}
204
+ - {{KPI 2}}
205
+ - {{KPI 3}}
206
+
207
+ ---
208
+
209
+ ### 9.2 Operational Metrics
210
+
211
+ - {{Metric 1}}
212
+ - {{Metric 2}}
213
+
214
+ ---
215
+
216
+ ## Notes
217
+
218
+ - This document defines product requirements only
219
+ - Technical implementation must be defined in a separate TechSpec
@@ -0,0 +1,49 @@
1
+ # User Stories & Acceptance Criteria
2
+
3
+ ---
4
+
5
+ ## 1. Legend
6
+
7
+ - **Priority:** P0 (Critical), P1 (High), P2 (Medium), P3 (Low)
8
+ - **Estimate:** Story Points (Fibonacci: 1, 2, 3, 5, 8, 13)
9
+
10
+ ---
11
+
12
+ ## 2. Epic Overview
13
+
14
+ - **EPIC-01:** {{Epic Name}}
15
+ - **EPIC-02:** {{Epic Name}}
16
+ - **EPIC-03:** {{Epic Name}}
17
+
18
+ ---
19
+
20
+ ## 3. Detailed User Stories
21
+
22
+ ---
23
+
24
+ ### EPIC-XX: {{Epic Name}}
25
+
26
+ ---
27
+
28
+ #### US-XX: {{Short Title}}
29
+
30
+ **As a** {{user type}},
31
+ **I want** {{desired action}},
32
+ **So that** {{expected outcome/value}}.
33
+
34
+ - **Priority:** P0 | P1 | P2 | P3
35
+ - **Estimate:** {{story points}} SP
36
+
37
+ ---
38
+
39
+ ### Acceptance Criteria
40
+
41
+ ```gherkin
42
+ Scenario: {{Scenario name}}
43
+
44
+ Given {{initial state}}
45
+ When {{action happens}}
46
+ Then {{expected result}}
47
+
48
+ And {{additional condition}}
49
+ And {{additional validation}}
@@ -0,0 +1,42 @@
1
+ # Technical Specification (TechSpec)
2
+
3
+ ---
4
+
5
+ ## 1. Document Control
6
+
7
+ | Version | Date | Author | Description |
8
+ |--------|------|--------|-------------|
9
+ | v1.0 | {{date}} | {{author}} | Initial version |
10
+
11
+ ---
12
+
13
+ ## 2. Overview
14
+
15
+ ### 2.1 Purpose
16
+
17
+ {{Describe what this system/component does in technical terms}}
18
+
19
+ ### 2.2 Scope
20
+
21
+ {{Define boundaries of the system. What is included / excluded}}
22
+
23
+ ### 2.3 Related Documents
24
+
25
+ - PRD: {{link}}
26
+ - User Stories: {{link}}
27
+
28
+ ---
29
+
30
+ ## 3. System Architecture
31
+
32
+ ### 3.1 High-Level Architecture
33
+
34
+ {{Describe system components and interactions}}
35
+
36
+ Optional (Mermaid):
37
+
38
+ ```mermaid
39
+ flowchart TD
40
+ A[Client] --> B[API Gateway]
41
+ B --> C[Service Layer]
42
+ C --> D[Database]