@grainulation/silo 1.0.0 → 1.0.1
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/CODE_OF_CONDUCT.md +25 -0
- package/CONTRIBUTING.md +103 -0
- package/README.md +67 -59
- package/bin/silo.js +212 -86
- package/lib/analytics.js +26 -11
- package/lib/confluence.js +343 -0
- package/lib/graph.js +414 -0
- package/lib/import-export.js +29 -24
- package/lib/index.js +15 -9
- package/lib/packs.js +60 -36
- package/lib/search.js +24 -16
- package/lib/serve-mcp.js +391 -95
- package/lib/server.js +205 -110
- package/lib/store.js +34 -18
- package/lib/templates.js +28 -17
- package/package.json +7 -3
- package/packs/adr.json +219 -0
- package/packs/api-design.json +67 -14
- package/packs/architecture-decision.json +152 -0
- package/packs/architecture.json +45 -9
- package/packs/ci-cd.json +51 -11
- package/packs/compliance.json +70 -14
- package/packs/data-engineering.json +57 -12
- package/packs/frontend.json +56 -12
- package/packs/hackathon-best-ai.json +179 -0
- package/packs/hackathon-business-impact.json +180 -0
- package/packs/hackathon-innovation.json +210 -0
- package/packs/hackathon-most-innovative.json +179 -0
- package/packs/hackathon-most-rigorous.json +179 -0
- package/packs/hackathon-sprint-boost.json +173 -0
- package/packs/incident-postmortem.json +219 -0
- package/packs/migration.json +45 -9
- package/packs/observability.json +57 -12
- package/packs/security.json +61 -13
- package/packs/team-process.json +64 -13
- package/packs/testing.json +20 -4
- package/packs/vendor-eval.json +219 -0
- package/packs/vendor-evaluation.json +148 -0
package/lib/templates.js
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
* A template contains: question, audience, constraints, and starter claims.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const fs = require(
|
|
9
|
-
const path = require(
|
|
10
|
-
const { Store } = require(
|
|
8
|
+
const fs = require("node:fs");
|
|
9
|
+
const path = require("node:path");
|
|
10
|
+
const { Store } = require("./store.js");
|
|
11
11
|
|
|
12
12
|
class Templates {
|
|
13
13
|
constructor(store) {
|
|
@@ -35,14 +35,14 @@ class Templates {
|
|
|
35
35
|
savedAt: new Date().toISOString(),
|
|
36
36
|
};
|
|
37
37
|
const filePath = path.join(this.store.templatesDir, `${id}.json`);
|
|
38
|
-
const tmp1 = filePath +
|
|
39
|
-
fs.writeFileSync(tmp1, JSON.stringify(entry, null, 2) +
|
|
38
|
+
const tmp1 = filePath + ".tmp." + process.pid;
|
|
39
|
+
fs.writeFileSync(tmp1, JSON.stringify(entry, null, 2) + "\n", "utf-8");
|
|
40
40
|
fs.renameSync(tmp1, filePath);
|
|
41
41
|
|
|
42
42
|
this.store._addToIndex({
|
|
43
43
|
id,
|
|
44
44
|
name,
|
|
45
|
-
type:
|
|
45
|
+
type: "template",
|
|
46
46
|
claimCount: (template.seedClaims || []).length,
|
|
47
47
|
storedAt: entry.savedAt,
|
|
48
48
|
});
|
|
@@ -55,7 +55,7 @@ class Templates {
|
|
|
55
55
|
const id = this._slugify(nameOrId);
|
|
56
56
|
const filePath = path.join(this.store.templatesDir, `${id}.json`);
|
|
57
57
|
if (!fs.existsSync(filePath)) return null;
|
|
58
|
-
return JSON.parse(fs.readFileSync(filePath,
|
|
58
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/** List all saved templates. */
|
|
@@ -66,10 +66,10 @@ class Templates {
|
|
|
66
66
|
|
|
67
67
|
return fs
|
|
68
68
|
.readdirSync(dir)
|
|
69
|
-
.filter((f) => f.endsWith(
|
|
69
|
+
.filter((f) => f.endsWith(".json"))
|
|
70
70
|
.map((f) => {
|
|
71
71
|
try {
|
|
72
|
-
const data = JSON.parse(fs.readFileSync(path.join(dir, f),
|
|
72
|
+
const data = JSON.parse(fs.readFileSync(path.join(dir, f), "utf-8"));
|
|
73
73
|
return {
|
|
74
74
|
id: data.id,
|
|
75
75
|
name: data.name,
|
|
@@ -102,17 +102,21 @@ class Templates {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// Write seed claims
|
|
105
|
-
const claimsPath = path.join(targetDir,
|
|
105
|
+
const claimsPath = path.join(targetDir, "claims.json");
|
|
106
106
|
const claims = (template.seedClaims || []).map((c, i) => ({
|
|
107
107
|
...c,
|
|
108
|
-
id: c.id || `d${String(i + 1).padStart(3,
|
|
108
|
+
id: c.id || `d${String(i + 1).padStart(3, "0")}`,
|
|
109
109
|
}));
|
|
110
|
-
const tmpClaims = claimsPath +
|
|
111
|
-
fs.writeFileSync(
|
|
110
|
+
const tmpClaims = claimsPath + ".tmp." + process.pid;
|
|
111
|
+
fs.writeFileSync(
|
|
112
|
+
tmpClaims,
|
|
113
|
+
JSON.stringify(claims, null, 2) + "\n",
|
|
114
|
+
"utf-8",
|
|
115
|
+
);
|
|
112
116
|
fs.renameSync(tmpClaims, claimsPath);
|
|
113
117
|
|
|
114
118
|
// Write sprint config stub
|
|
115
|
-
const configPath = path.join(targetDir,
|
|
119
|
+
const configPath = path.join(targetDir, "sprint.json");
|
|
116
120
|
const config = {
|
|
117
121
|
question: template.question,
|
|
118
122
|
audience: template.audience,
|
|
@@ -120,8 +124,12 @@ class Templates {
|
|
|
120
124
|
fromTemplate: template.id,
|
|
121
125
|
createdAt: new Date().toISOString(),
|
|
122
126
|
};
|
|
123
|
-
const tmpConfig = configPath +
|
|
124
|
-
fs.writeFileSync(
|
|
127
|
+
const tmpConfig = configPath + ".tmp." + process.pid;
|
|
128
|
+
fs.writeFileSync(
|
|
129
|
+
tmpConfig,
|
|
130
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
131
|
+
"utf-8",
|
|
132
|
+
);
|
|
125
133
|
fs.renameSync(tmpConfig, configPath);
|
|
126
134
|
|
|
127
135
|
return {
|
|
@@ -132,7 +140,10 @@ class Templates {
|
|
|
132
140
|
}
|
|
133
141
|
|
|
134
142
|
_slugify(str) {
|
|
135
|
-
return str
|
|
143
|
+
return str
|
|
144
|
+
.toLowerCase()
|
|
145
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
146
|
+
.replace(/^-|-$/g, "");
|
|
136
147
|
}
|
|
137
148
|
}
|
|
138
149
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grainulation/silo",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Reusable knowledge for research sprints -- shared claim libraries, templates, and knowledge packs",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -27,12 +27,13 @@
|
|
|
27
27
|
],
|
|
28
28
|
"author": "grainulation contributors",
|
|
29
29
|
"license": "MIT",
|
|
30
|
+
"type": "module",
|
|
30
31
|
"repository": {
|
|
31
32
|
"type": "git",
|
|
32
33
|
"url": "git+https://github.com/grainulation/silo.git"
|
|
33
34
|
},
|
|
34
35
|
"engines": {
|
|
35
|
-
"node": ">=
|
|
36
|
+
"node": ">=20"
|
|
36
37
|
},
|
|
37
38
|
"bugs": {
|
|
38
39
|
"url": "https://github.com/grainulation/silo/issues"
|
|
@@ -43,6 +44,9 @@
|
|
|
43
44
|
"lib/",
|
|
44
45
|
"packs/",
|
|
45
46
|
"public/",
|
|
46
|
-
"CHANGELOG.md"
|
|
47
|
+
"CHANGELOG.md",
|
|
48
|
+
"LICENSE",
|
|
49
|
+
"CODE_OF_CONDUCT.md",
|
|
50
|
+
"CONTRIBUTING.md"
|
|
47
51
|
]
|
|
48
52
|
}
|
package/packs/adr.json
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Architecture Decision Records",
|
|
3
|
+
"description": "Enterprise ADR framework with evidence tiers. Structured constraints and risk claims for common architecture decisions — use as seed claims when evaluating build-vs-buy, migration, scaling, or technology choices.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"claims": [
|
|
6
|
+
{
|
|
7
|
+
"id": "adr-001",
|
|
8
|
+
"type": "constraint",
|
|
9
|
+
"topic": "ADR decision structure",
|
|
10
|
+
"content": "Every ADR must state: (1) Decision context — what problem are we solving? (2) Decision drivers — what constraints and goals matter most? (3) Options considered — at least 2 alternatives with evidence. (4) Decision outcome — which option and why. (5) Consequences — accepted trade-offs.",
|
|
11
|
+
"source": {
|
|
12
|
+
"origin": "best-practice",
|
|
13
|
+
"artifact": "https://adr.github.io",
|
|
14
|
+
"connector": null
|
|
15
|
+
},
|
|
16
|
+
"evidence": "documented",
|
|
17
|
+
"status": "active",
|
|
18
|
+
"phase_added": "define",
|
|
19
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
20
|
+
"conflicts_with": [],
|
|
21
|
+
"resolved_by": null,
|
|
22
|
+
"tags": ["adr", "structure", "decision-record", "enterprise"]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "adr-002",
|
|
26
|
+
"type": "constraint",
|
|
27
|
+
"topic": "evidence-backed decisions",
|
|
28
|
+
"content": "Architecture decisions must be backed by evidence, not opinions. Each option should have claims at 'documented' tier or higher. 'We chose X because the team prefers it' is not an ADR — 'We chose X because benchmarks show 3x throughput under our load profile' is.",
|
|
29
|
+
"source": {
|
|
30
|
+
"origin": "best-practice",
|
|
31
|
+
"artifact": null,
|
|
32
|
+
"connector": null
|
|
33
|
+
},
|
|
34
|
+
"evidence": "documented",
|
|
35
|
+
"status": "active",
|
|
36
|
+
"phase_added": "define",
|
|
37
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
38
|
+
"conflicts_with": [],
|
|
39
|
+
"resolved_by": null,
|
|
40
|
+
"tags": ["adr", "evidence", "decision-quality", "enterprise"]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "adr-003",
|
|
44
|
+
"type": "constraint",
|
|
45
|
+
"topic": "reversibility assessment",
|
|
46
|
+
"content": "Every architecture decision must classify its reversibility: Type 1 (irreversible — database engine, programming language, cloud provider) requires heavyweight evidence. Type 2 (reversible — framework, library, API design) can proceed with lighter evidence.",
|
|
47
|
+
"source": {
|
|
48
|
+
"origin": "best-practice",
|
|
49
|
+
"artifact": null,
|
|
50
|
+
"connector": null
|
|
51
|
+
},
|
|
52
|
+
"evidence": "documented",
|
|
53
|
+
"status": "active",
|
|
54
|
+
"phase_added": "define",
|
|
55
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
56
|
+
"conflicts_with": [],
|
|
57
|
+
"resolved_by": null,
|
|
58
|
+
"tags": ["adr", "reversibility", "type-1-type-2", "risk"]
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "adr-004",
|
|
62
|
+
"type": "risk",
|
|
63
|
+
"topic": "ADR decay",
|
|
64
|
+
"content": "ADRs that are not linked to code become stale and misleading. Risk: team makes decisions contradicting existing ADRs because they can't find them. Mitigation: store ADRs alongside code (docs/adr/), reference in relevant source files, review annually.",
|
|
65
|
+
"source": {
|
|
66
|
+
"origin": "best-practice",
|
|
67
|
+
"artifact": null,
|
|
68
|
+
"connector": null
|
|
69
|
+
},
|
|
70
|
+
"evidence": "documented",
|
|
71
|
+
"status": "active",
|
|
72
|
+
"phase_added": "define",
|
|
73
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
74
|
+
"conflicts_with": [],
|
|
75
|
+
"resolved_by": null,
|
|
76
|
+
"tags": ["adr", "decay", "maintenance", "risk"]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "adr-005",
|
|
80
|
+
"type": "recommendation",
|
|
81
|
+
"topic": "wheat-powered ADR workflow",
|
|
82
|
+
"content": "Use wheat sprint as ADR research phase: (1) Define decision question as sprint question. (2) Add constraints from stakeholders. (3) Research options — each generates factual claims with evidence tiers. (4) Challenge phase creates conflict claims between options. (5) Compile — the brief IS the ADR.",
|
|
83
|
+
"source": {
|
|
84
|
+
"origin": "best-practice",
|
|
85
|
+
"artifact": null,
|
|
86
|
+
"connector": null
|
|
87
|
+
},
|
|
88
|
+
"evidence": "stated",
|
|
89
|
+
"status": "active",
|
|
90
|
+
"phase_added": "define",
|
|
91
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
92
|
+
"conflicts_with": [],
|
|
93
|
+
"resolved_by": null,
|
|
94
|
+
"tags": ["adr", "wheat", "workflow", "recommendation"]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"id": "adr-006",
|
|
98
|
+
"type": "factual",
|
|
99
|
+
"topic": "build vs buy framework",
|
|
100
|
+
"content": "Build-vs-buy evaluation dimensions: (1) Total cost of ownership over 3 years (build: salaries + infra; buy: license + integration). (2) Time to value (build: months; buy: weeks). (3) Customization requirements. (4) Strategic differentiation — is this your core competency? (5) Maintenance burden.",
|
|
101
|
+
"source": {
|
|
102
|
+
"origin": "best-practice",
|
|
103
|
+
"artifact": null,
|
|
104
|
+
"connector": null
|
|
105
|
+
},
|
|
106
|
+
"evidence": "documented",
|
|
107
|
+
"status": "active",
|
|
108
|
+
"phase_added": "define",
|
|
109
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
110
|
+
"conflicts_with": [],
|
|
111
|
+
"resolved_by": null,
|
|
112
|
+
"tags": ["adr", "build-vs-buy", "framework", "tco"]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "adr-007",
|
|
116
|
+
"type": "factual",
|
|
117
|
+
"topic": "migration decision framework",
|
|
118
|
+
"content": "Migration decision framework: (1) Is the current system at a hard limit (scale, compliance, EOL)? (2) What's the migration risk (data loss, downtime, rollback complexity)? (3) Can you migrate incrementally (strangler fig) or must it be big-bang? (4) What's the team's experience with the target?",
|
|
119
|
+
"source": {
|
|
120
|
+
"origin": "best-practice",
|
|
121
|
+
"artifact": null,
|
|
122
|
+
"connector": null
|
|
123
|
+
},
|
|
124
|
+
"evidence": "documented",
|
|
125
|
+
"status": "active",
|
|
126
|
+
"phase_added": "define",
|
|
127
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
128
|
+
"conflicts_with": [],
|
|
129
|
+
"resolved_by": null,
|
|
130
|
+
"tags": ["adr", "migration", "framework", "risk-assessment"]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "adr-008",
|
|
134
|
+
"type": "risk",
|
|
135
|
+
"topic": "consensus-driven decisions",
|
|
136
|
+
"content": "Risk: architecture by committee produces mediocre compromises. ADRs should identify a single decision owner (usually the tech lead or architect) who makes the call after reviewing evidence. The ADR documents the rationale for future teams.",
|
|
137
|
+
"source": {
|
|
138
|
+
"origin": "best-practice",
|
|
139
|
+
"artifact": null,
|
|
140
|
+
"connector": null
|
|
141
|
+
},
|
|
142
|
+
"evidence": "documented",
|
|
143
|
+
"status": "active",
|
|
144
|
+
"phase_added": "define",
|
|
145
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
146
|
+
"conflicts_with": [],
|
|
147
|
+
"resolved_by": null,
|
|
148
|
+
"tags": ["adr", "decision-owner", "governance", "risk"]
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "adr-009",
|
|
152
|
+
"type": "constraint",
|
|
153
|
+
"topic": "ADR status lifecycle",
|
|
154
|
+
"content": "ADR statuses: Proposed (under research), Accepted (decision made), Deprecated (superseded by newer ADR), Superseded (link to replacement). Never delete ADRs — they document why past decisions were made, even if reversed.",
|
|
155
|
+
"source": {
|
|
156
|
+
"origin": "best-practice",
|
|
157
|
+
"artifact": "https://adr.github.io",
|
|
158
|
+
"connector": null
|
|
159
|
+
},
|
|
160
|
+
"evidence": "documented",
|
|
161
|
+
"status": "active",
|
|
162
|
+
"phase_added": "define",
|
|
163
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
164
|
+
"conflicts_with": [],
|
|
165
|
+
"resolved_by": null,
|
|
166
|
+
"tags": ["adr", "lifecycle", "status", "governance"]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "adr-010",
|
|
170
|
+
"type": "recommendation",
|
|
171
|
+
"topic": "ADR audit trail",
|
|
172
|
+
"content": "For enterprise compliance: ADRs created via wheat produce an immutable audit trail. The claims.json is version-controlled, the compilation certificate references a content hash, and git log shows who contributed what. This satisfies SOC 2 change management evidence requirements.",
|
|
173
|
+
"source": {
|
|
174
|
+
"origin": "best-practice",
|
|
175
|
+
"artifact": null,
|
|
176
|
+
"connector": null
|
|
177
|
+
},
|
|
178
|
+
"evidence": "stated",
|
|
179
|
+
"status": "active",
|
|
180
|
+
"phase_added": "define",
|
|
181
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
182
|
+
"conflicts_with": [],
|
|
183
|
+
"resolved_by": null,
|
|
184
|
+
"tags": ["adr", "audit-trail", "compliance", "soc2", "enterprise"]
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"id": "adr-011",
|
|
188
|
+
"type": "estimate",
|
|
189
|
+
"topic": "ADR time savings",
|
|
190
|
+
"content": "Traditional ADR process: 1-2 weeks of stakeholder meetings, document drafting, and review cycles. Wheat-powered ADR: 10-15 minute research sprint produces a compiled brief with evidence tiers, conflict resolution, and structured recommendations — then a 30-minute review meeting to accept.",
|
|
191
|
+
"source": { "origin": "research", "artifact": null, "connector": null },
|
|
192
|
+
"evidence": "stated",
|
|
193
|
+
"status": "active",
|
|
194
|
+
"phase_added": "research",
|
|
195
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
196
|
+
"conflicts_with": [],
|
|
197
|
+
"resolved_by": null,
|
|
198
|
+
"tags": ["adr", "time-savings", "roi", "enterprise"]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"id": "adr-012",
|
|
202
|
+
"type": "factual",
|
|
203
|
+
"topic": "scaling decision patterns",
|
|
204
|
+
"content": "Common scaling decision points: (1) Vertical before horizontal — scale up is simpler until hardware limits. (2) Read replicas before sharding — reduces complexity for read-heavy workloads. (3) Caching before rewriting — often 10x improvement for fraction of effort. (4) CDN before app optimization for static assets.",
|
|
205
|
+
"source": {
|
|
206
|
+
"origin": "best-practice",
|
|
207
|
+
"artifact": null,
|
|
208
|
+
"connector": null
|
|
209
|
+
},
|
|
210
|
+
"evidence": "documented",
|
|
211
|
+
"status": "active",
|
|
212
|
+
"phase_added": "define",
|
|
213
|
+
"timestamp": "2026-01-01T00:00:00.000Z",
|
|
214
|
+
"conflicts_with": [],
|
|
215
|
+
"resolved_by": null,
|
|
216
|
+
"tags": ["adr", "scaling", "patterns", "architecture"]
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
}
|
package/packs/api-design.json
CHANGED
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
"type": "constraint",
|
|
9
9
|
"topic": "HTTP method semantics",
|
|
10
10
|
"content": "GET must be safe and idempotent (no side effects). PUT must be idempotent (same result on repeat). POST is neither safe nor idempotent. DELETE should be idempotent (deleting an already-deleted resource returns 204 or 404, not an error). Violating these semantics breaks caches, retries, and client expectations.",
|
|
11
|
-
"source": {
|
|
11
|
+
"source": {
|
|
12
|
+
"origin": "best-practice",
|
|
13
|
+
"artifact": null,
|
|
14
|
+
"connector": null
|
|
15
|
+
},
|
|
12
16
|
"evidence": "documented",
|
|
13
17
|
"status": "active",
|
|
14
18
|
"phase_added": "define",
|
|
@@ -22,7 +26,11 @@
|
|
|
22
26
|
"type": "recommendation",
|
|
23
27
|
"topic": "error response format",
|
|
24
28
|
"content": "Use RFC 7807 (Problem Details) for error responses: {type, title, status, detail, instance}. Include a machine-readable error code field for client-side branching. Never expose stack traces, internal paths, or database errors in production responses.",
|
|
25
|
-
"source": {
|
|
29
|
+
"source": {
|
|
30
|
+
"origin": "best-practice",
|
|
31
|
+
"artifact": null,
|
|
32
|
+
"connector": null
|
|
33
|
+
},
|
|
26
34
|
"evidence": "documented",
|
|
27
35
|
"status": "active",
|
|
28
36
|
"phase_added": "define",
|
|
@@ -36,7 +44,11 @@
|
|
|
36
44
|
"type": "recommendation",
|
|
37
45
|
"topic": "cursor-based pagination",
|
|
38
46
|
"content": "Prefer cursor-based pagination over offset-based for datasets that change frequently. Offset pagination becomes inconsistent when rows are inserted or deleted between pages. Cursor pagination uses an opaque token (base64-encoded last-seen ID) and supports consistent forward iteration. Return {data, next_cursor, has_more} in responses.",
|
|
39
|
-
"source": {
|
|
47
|
+
"source": {
|
|
48
|
+
"origin": "best-practice",
|
|
49
|
+
"artifact": null,
|
|
50
|
+
"connector": null
|
|
51
|
+
},
|
|
40
52
|
"evidence": "production",
|
|
41
53
|
"status": "active",
|
|
42
54
|
"phase_added": "define",
|
|
@@ -50,7 +62,11 @@
|
|
|
50
62
|
"type": "risk",
|
|
51
63
|
"topic": "URL path versioning lock-in",
|
|
52
64
|
"content": "URL path versioning (/v1/users, /v2/users) is simple but creates permanent routing complexity. Every version doubles the endpoint surface. Prefer header-based versioning (Accept: application/vnd.api+json;version=2) or additive-only changes (no versioning needed if you never remove or rename fields).",
|
|
53
|
-
"source": {
|
|
65
|
+
"source": {
|
|
66
|
+
"origin": "best-practice",
|
|
67
|
+
"artifact": null,
|
|
68
|
+
"connector": null
|
|
69
|
+
},
|
|
54
70
|
"evidence": "web",
|
|
55
71
|
"status": "active",
|
|
56
72
|
"phase_added": "define",
|
|
@@ -64,21 +80,34 @@
|
|
|
64
80
|
"type": "constraint",
|
|
65
81
|
"topic": "backwards compatibility rules",
|
|
66
82
|
"content": "Breaking changes for public APIs: removing a field, renaming a field, changing a field type, changing a required/optional status, removing an endpoint, changing authentication. Non-breaking: adding optional fields, adding new endpoints, adding new enum values (if clients handle unknown values). Never make breaking changes without a version bump and deprecation period of at least 6 months.",
|
|
67
|
-
"source": {
|
|
83
|
+
"source": {
|
|
84
|
+
"origin": "best-practice",
|
|
85
|
+
"artifact": null,
|
|
86
|
+
"connector": null
|
|
87
|
+
},
|
|
68
88
|
"evidence": "documented",
|
|
69
89
|
"status": "active",
|
|
70
90
|
"phase_added": "define",
|
|
71
91
|
"timestamp": "2025-01-01T00:00:00.000Z",
|
|
72
92
|
"conflicts_with": [],
|
|
73
93
|
"resolved_by": null,
|
|
74
|
-
"tags": [
|
|
94
|
+
"tags": [
|
|
95
|
+
"api",
|
|
96
|
+
"backwards-compatibility",
|
|
97
|
+
"deprecation",
|
|
98
|
+
"breaking-changes"
|
|
99
|
+
]
|
|
75
100
|
},
|
|
76
101
|
{
|
|
77
102
|
"id": "api-006",
|
|
78
103
|
"type": "factual",
|
|
79
104
|
"topic": "GraphQL vs REST tradeoffs",
|
|
80
105
|
"content": "GraphQL eliminates over-fetching and under-fetching but introduces: query complexity analysis (must limit depth to 7-10 and breadth), N+1 data loader patterns, cache invalidation complexity (no HTTP caching by default), and a steeper client learning curve. REST is simpler for CRUD-heavy APIs with predictable access patterns.",
|
|
81
|
-
"source": {
|
|
106
|
+
"source": {
|
|
107
|
+
"origin": "best-practice",
|
|
108
|
+
"artifact": null,
|
|
109
|
+
"connector": null
|
|
110
|
+
},
|
|
82
111
|
"evidence": "documented",
|
|
83
112
|
"status": "active",
|
|
84
113
|
"phase_added": "define",
|
|
@@ -92,7 +121,11 @@
|
|
|
92
121
|
"type": "recommendation",
|
|
93
122
|
"topic": "rate limit headers",
|
|
94
123
|
"content": "Include rate limit headers in every response: X-RateLimit-Limit (max requests per window), X-RateLimit-Remaining (remaining in current window), X-RateLimit-Reset (Unix timestamp when window resets). Return 429 Too Many Requests with a Retry-After header when limit is exceeded. This lets well-behaved clients self-throttle.",
|
|
95
|
-
"source": {
|
|
124
|
+
"source": {
|
|
125
|
+
"origin": "best-practice",
|
|
126
|
+
"artifact": null,
|
|
127
|
+
"connector": null
|
|
128
|
+
},
|
|
96
129
|
"evidence": "documented",
|
|
97
130
|
"status": "active",
|
|
98
131
|
"phase_added": "define",
|
|
@@ -106,7 +139,11 @@
|
|
|
106
139
|
"type": "constraint",
|
|
107
140
|
"topic": "request payload size limits",
|
|
108
141
|
"content": "Set explicit request body size limits: 1 MB for standard JSON APIs, 10 MB for file uploads via multipart, and configurable limits for specific endpoints. Unbounded request bodies enable denial-of-service attacks. Return 413 Payload Too Large with a clear message stating the limit.",
|
|
109
|
-
"source": {
|
|
142
|
+
"source": {
|
|
143
|
+
"origin": "best-practice",
|
|
144
|
+
"artifact": null,
|
|
145
|
+
"connector": null
|
|
146
|
+
},
|
|
110
147
|
"evidence": "documented",
|
|
111
148
|
"status": "active",
|
|
112
149
|
"phase_added": "define",
|
|
@@ -120,7 +157,11 @@
|
|
|
120
157
|
"type": "recommendation",
|
|
121
158
|
"topic": "idempotency keys for mutations",
|
|
122
159
|
"content": "POST endpoints that create resources should accept an Idempotency-Key header. Store the key with the response for 24 hours. If a duplicate key arrives, return the stored response without re-executing. This prevents duplicate charges, orders, or records from network retries. Stripe, PayPal, and AWS all use this pattern.",
|
|
123
|
-
"source": {
|
|
160
|
+
"source": {
|
|
161
|
+
"origin": "best-practice",
|
|
162
|
+
"artifact": null,
|
|
163
|
+
"connector": null
|
|
164
|
+
},
|
|
124
165
|
"evidence": "production",
|
|
125
166
|
"status": "active",
|
|
126
167
|
"phase_added": "define",
|
|
@@ -134,7 +175,11 @@
|
|
|
134
175
|
"type": "risk",
|
|
135
176
|
"topic": "nested resource depth",
|
|
136
177
|
"content": "Deeply nested REST routes (/orgs/:id/teams/:id/members/:id/roles) become rigid and hard to evolve. Limit nesting to 2 levels maximum. Use flat routes with query filters for deeper relationships (/roles?member_id=X&team_id=Y). Deep nesting also makes URL construction fragile for API consumers.",
|
|
137
|
-
"source": {
|
|
178
|
+
"source": {
|
|
179
|
+
"origin": "best-practice",
|
|
180
|
+
"artifact": null,
|
|
181
|
+
"connector": null
|
|
182
|
+
},
|
|
138
183
|
"evidence": "web",
|
|
139
184
|
"status": "active",
|
|
140
185
|
"phase_added": "define",
|
|
@@ -148,7 +193,11 @@
|
|
|
148
193
|
"type": "factual",
|
|
149
194
|
"topic": "HTTP status code semantics",
|
|
150
195
|
"content": "Use status codes precisely: 200 (success with body), 201 (created, include Location header), 204 (success, no body), 400 (malformed request), 401 (unauthenticated), 403 (authenticated but unauthorized), 404 (not found or hidden), 409 (conflict/duplicate), 422 (valid syntax but semantic error), 429 (rate limited), 500 (server error). Avoid 200-wrapping errors.",
|
|
151
|
-
"source": {
|
|
196
|
+
"source": {
|
|
197
|
+
"origin": "best-practice",
|
|
198
|
+
"artifact": null,
|
|
199
|
+
"connector": null
|
|
200
|
+
},
|
|
152
201
|
"evidence": "documented",
|
|
153
202
|
"status": "active",
|
|
154
203
|
"phase_added": "define",
|
|
@@ -162,7 +211,11 @@
|
|
|
162
211
|
"type": "recommendation",
|
|
163
212
|
"topic": "API documentation contract testing",
|
|
164
213
|
"content": "Generate API documentation from code (OpenAPI/Swagger) rather than maintaining it manually. Use contract testing (Pact, Dredd, or OpenAPI-diff in CI) to verify that the implementation matches the spec. Manual docs drift within weeks; contract tests catch drift on every PR.",
|
|
165
|
-
"source": {
|
|
214
|
+
"source": {
|
|
215
|
+
"origin": "best-practice",
|
|
216
|
+
"artifact": null,
|
|
217
|
+
"connector": null
|
|
218
|
+
},
|
|
166
219
|
"evidence": "tested",
|
|
167
220
|
"status": "active",
|
|
168
221
|
"phase_added": "define",
|
|
@@ -186,4 +239,4 @@
|
|
|
186
239
|
"tags": ["api", "performance", "latency", "sla"]
|
|
187
240
|
}
|
|
188
241
|
]
|
|
189
|
-
}
|
|
242
|
+
}
|