dna-sdk 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.
- package/LICENSE +21 -0
- package/README.md +58 -0
- package/dist/adapters/embedding/onnx.d.ts +32 -0
- package/dist/adapters/embedding/onnx.js +51 -0
- package/dist/adapters/filesystem/cache.d.ts +16 -0
- package/dist/adapters/filesystem/cache.js +113 -0
- package/dist/adapters/filesystem/index.d.ts +2 -0
- package/dist/adapters/filesystem/index.js +2 -0
- package/dist/adapters/filesystem/source.d.ts +82 -0
- package/dist/adapters/filesystem/source.js +357 -0
- package/dist/adapters/postgres/index.d.ts +2 -0
- package/dist/adapters/postgres/index.js +2 -0
- package/dist/adapters/postgres/migrations.d.ts +14 -0
- package/dist/adapters/postgres/migrations.js +88 -0
- package/dist/adapters/postgres/source.d.ts +172 -0
- package/dist/adapters/postgres/source.js +569 -0
- package/dist/adapters/resolvers/github.d.ts +12 -0
- package/dist/adapters/resolvers/github.js +47 -0
- package/dist/adapters/resolvers/http.d.ts +22 -0
- package/dist/adapters/resolvers/http.js +145 -0
- package/dist/adapters/resolvers/index.d.ts +3 -0
- package/dist/adapters/resolvers/index.js +3 -0
- package/dist/adapters/resolvers/local.d.ts +24 -0
- package/dist/adapters/resolvers/local.js +132 -0
- package/dist/adapters/search/driver.d.ts +45 -0
- package/dist/adapters/search/driver.js +138 -0
- package/dist/adapters/search/migrations.d.ts +26 -0
- package/dist/adapters/search/migrations.js +58 -0
- package/dist/adapters/search/rrf.d.ts +23 -0
- package/dist/adapters/search/rrf.js +42 -0
- package/dist/adapters/search/sqlite-vec.d.ts +66 -0
- package/dist/adapters/search/sqlite-vec.js +294 -0
- package/dist/bootstrap.d.ts +45 -0
- package/dist/bootstrap.js +134 -0
- package/dist/extensions/agentskills/DOCS.md +18 -0
- package/dist/extensions/agentskills.d.ts +28 -0
- package/dist/extensions/agentskills.js +252 -0
- package/dist/extensions/agentsmd/DOCS.md +18 -0
- package/dist/extensions/agentsmd.d.ts +22 -0
- package/dist/extensions/agentsmd.js +170 -0
- package/dist/extensions/audit/kinds/audit-log.kind.yaml +148 -0
- package/dist/extensions/audit.d.ts +25 -0
- package/dist/extensions/audit.js +62 -0
- package/dist/extensions/automation/kinds/automation.kind.yaml +296 -0
- package/dist/extensions/automation/query.d.ts +49 -0
- package/dist/extensions/automation/query.js +77 -0
- package/dist/extensions/automation/write-guards.d.ts +59 -0
- package/dist/extensions/automation/write-guards.js +153 -0
- package/dist/extensions/automation.d.ts +41 -0
- package/dist/extensions/automation.js +54 -0
- package/dist/extensions/collab.d.ts +16 -0
- package/dist/extensions/collab.js +79 -0
- package/dist/extensions/doc/kinds/doc.kind.yaml +118 -0
- package/dist/extensions/doc.d.ts +25 -0
- package/dist/extensions/doc.js +10 -0
- package/dist/extensions/eval/kinds/eval-baseline.kind.yaml +65 -0
- package/dist/extensions/eval/kinds/eval-case.kind.yaml +140 -0
- package/dist/extensions/eval/kinds/eval-run.kind.yaml +134 -0
- package/dist/extensions/eval/kinds/eval-suite.kind.yaml +87 -0
- package/dist/extensions/eval.d.ts +24 -0
- package/dist/extensions/eval.js +31 -0
- package/dist/extensions/evidence/kinds/evidence.kind.yaml +92 -0
- package/dist/extensions/evidence.d.ts +17 -0
- package/dist/extensions/evidence.js +94 -0
- package/dist/extensions/federation.d.ts +22 -0
- package/dist/extensions/federation.js +173 -0
- package/dist/extensions/guardrails/DOCS.md +20 -0
- package/dist/extensions/guardrails.d.ts +31 -0
- package/dist/extensions/guardrails.js +228 -0
- package/dist/extensions/helix/DOCS-Actor.md +20 -0
- package/dist/extensions/helix/DOCS-Agent.md +20 -0
- package/dist/extensions/helix/DOCS-UseCase.md +20 -0
- package/dist/extensions/helix/layers.d.ts +12 -0
- package/dist/extensions/helix/layers.js +18 -0
- package/dist/extensions/helix/write-guards.d.ts +70 -0
- package/dist/extensions/helix/write-guards.js +172 -0
- package/dist/extensions/helix.d.ts +29 -0
- package/dist/extensions/helix.js +963 -0
- package/dist/extensions/helix_extras.d.ts +124 -0
- package/dist/extensions/helix_extras.js +328 -0
- package/dist/extensions/hooks.d.ts +31 -0
- package/dist/extensions/hooks.js +247 -0
- package/dist/extensions/kinddef/DOCS.md +24 -0
- package/dist/extensions/kinddef.d.ts +40 -0
- package/dist/extensions/kinddef.js +171 -0
- package/dist/extensions/lesson.d.ts +6 -0
- package/dist/extensions/lesson.js +194 -0
- package/dist/extensions/modelreg/kinds/model-profile.kind.yaml +155 -0
- package/dist/extensions/modelreg.d.ts +30 -0
- package/dist/extensions/modelreg.js +38 -0
- package/dist/extensions/recognizer.d.ts +34 -0
- package/dist/extensions/recognizer.js +272 -0
- package/dist/extensions/research.d.ts +32 -0
- package/dist/extensions/research.js +292 -0
- package/dist/extensions/safety.d.ts +33 -0
- package/dist/extensions/safety.js +230 -0
- package/dist/extensions/sdlc/kinds/adr.kind.yaml +136 -0
- package/dist/extensions/sdlc/kinds/archive-proposal.kind.yaml +107 -0
- package/dist/extensions/sdlc/kinds/changelog.kind.yaml +108 -0
- package/dist/extensions/sdlc/kinds/cognitive-policy.kind.yaml +470 -0
- package/dist/extensions/sdlc/kinds/forecast.kind.yaml +125 -0
- package/dist/extensions/sdlc/kinds/insight.kind.yaml +87 -0
- package/dist/extensions/sdlc/kinds/kaizen.kind.yaml +106 -0
- package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +270 -0
- package/dist/extensions/sdlc/kinds/narrative.kind.yaml +189 -0
- package/dist/extensions/sdlc/kinds/pattern-insight.kind.yaml +204 -0
- package/dist/extensions/sdlc/kinds/postmortem.kind.yaml +168 -0
- package/dist/extensions/sdlc/kinds/pre-mortem.kind.yaml +145 -0
- package/dist/extensions/sdlc/kinds/prompt-template.kind.yaml +109 -0
- package/dist/extensions/sdlc/kinds/retrospective.kind.yaml +186 -0
- package/dist/extensions/sdlc/kinds/risk-register.kind.yaml +178 -0
- package/dist/extensions/sdlc/kinds/saved-view.kind.yaml +127 -0
- package/dist/extensions/sdlc/kinds/status-report.kind.yaml +129 -0
- package/dist/extensions/sdlc/kinds/synthesis-run.kind.yaml +279 -0
- package/dist/extensions/sdlc/kinds/synthesizer-state.kind.yaml +196 -0
- package/dist/extensions/sdlc/kinds/workflow-event.kind.yaml +195 -0
- package/dist/extensions/sdlc.d.ts +32 -0
- package/dist/extensions/sdlc.js +1318 -0
- package/dist/extensions/soulspec/DOCS.md +19 -0
- package/dist/extensions/soulspec.d.ts +22 -0
- package/dist/extensions/soulspec.js +287 -0
- package/dist/extensions/tenant.d.ts +6 -0
- package/dist/extensions/tenant.js +291 -0
- package/dist/extensions/testkit.d.ts +24 -0
- package/dist/extensions/testkit.js +211 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +102 -0
- package/dist/kernel/_text.d.ts +28 -0
- package/dist/kernel/_text.js +42 -0
- package/dist/kernel/bundle-handle.d.ts +83 -0
- package/dist/kernel/bundle-handle.js +184 -0
- package/dist/kernel/capabilities.d.ts +157 -0
- package/dist/kernel/capabilities.js +109 -0
- package/dist/kernel/collaborator-ports.d.ts +91 -0
- package/dist/kernel/collaborator-ports.js +1 -0
- package/dist/kernel/composition-resolver.d.ts +215 -0
- package/dist/kernel/composition-resolver.js +558 -0
- package/dist/kernel/composition.d.ts +10 -0
- package/dist/kernel/composition.js +1 -0
- package/dist/kernel/descriptor-loader.d.ts +13 -0
- package/dist/kernel/descriptor-loader.js +48 -0
- package/dist/kernel/document.d.ts +43 -0
- package/dist/kernel/document.js +71 -0
- package/dist/kernel/embedding.d.ts +28 -0
- package/dist/kernel/embedding.js +78 -0
- package/dist/kernel/errors.d.ts +62 -0
- package/dist/kernel/errors.js +82 -0
- package/dist/kernel/events.d.ts +6 -0
- package/dist/kernel/events.js +21 -0
- package/dist/kernel/evidence-capture.d.ts +69 -0
- package/dist/kernel/evidence-capture.js +138 -0
- package/dist/kernel/fs.d.ts +20 -0
- package/dist/kernel/fs.js +128 -0
- package/dist/kernel/generic-rw.d.ts +33 -0
- package/dist/kernel/generic-rw.js +235 -0
- package/dist/kernel/hooks.d.ts +172 -0
- package/dist/kernel/hooks.js +256 -0
- package/dist/kernel/index.d.ts +624 -0
- package/dist/kernel/index.js +1638 -0
- package/dist/kernel/instance.d.ts +211 -0
- package/dist/kernel/instance.js +487 -0
- package/dist/kernel/kind-registry.d.ts +183 -0
- package/dist/kernel/kind-registry.js +710 -0
- package/dist/kernel/kind_base.d.ts +106 -0
- package/dist/kernel/kind_base.js +244 -0
- package/dist/kernel/layer-resolver.d.ts +48 -0
- package/dist/kernel/layer-resolver.js +284 -0
- package/dist/kernel/lock-manager.d.ts +21 -0
- package/dist/kernel/lock-manager.js +43 -0
- package/dist/kernel/lock.d.ts +38 -0
- package/dist/kernel/lock.js +115 -0
- package/dist/kernel/meta.d.ts +135 -0
- package/dist/kernel/meta.js +817 -0
- package/dist/kernel/models.d.ts +3089 -0
- package/dist/kernel/models.js +764 -0
- package/dist/kernel/navigator.d.ts +36 -0
- package/dist/kernel/navigator.js +132 -0
- package/dist/kernel/port-surface.d.ts +33 -0
- package/dist/kernel/port-surface.js +180 -0
- package/dist/kernel/preview.d.ts +60 -0
- package/dist/kernel/preview.js +54 -0
- package/dist/kernel/prompt-budget.d.ts +39 -0
- package/dist/kernel/prompt-budget.js +43 -0
- package/dist/kernel/prompt-builder.d.ts +25 -0
- package/dist/kernel/prompt-builder.js +237 -0
- package/dist/kernel/protocols.d.ts +751 -0
- package/dist/kernel/protocols.js +493 -0
- package/dist/kernel/reports.d.ts +26 -0
- package/dist/kernel/reports.js +215 -0
- package/dist/kernel/resolver.d.ts +146 -0
- package/dist/kernel/resolver.js +232 -0
- package/dist/kernel/resource.d.ts +57 -0
- package/dist/kernel/resource.js +97 -0
- package/dist/kernel/runtime.d.ts +16 -0
- package/dist/kernel/runtime.js +18 -0
- package/dist/kernel/safety-scanner.d.ts +38 -0
- package/dist/kernel/safety-scanner.js +228 -0
- package/dist/kernel/serialize-to-files.d.ts +29 -0
- package/dist/kernel/serialize-to-files.js +23 -0
- package/dist/kernel/spec-access.d.ts +23 -0
- package/dist/kernel/spec-access.js +40 -0
- package/dist/kernel/studio_ui.d.ts +88 -0
- package/dist/kernel/studio_ui.js +135 -0
- package/dist/kernel/templates.d.ts +77 -0
- package/dist/kernel/templates.js +116 -0
- package/dist/kernel/tool-registry.d.ts +52 -0
- package/dist/kernel/tool-registry.js +89 -0
- package/dist/kernel/write-pipeline.d.ts +65 -0
- package/dist/kernel/write-pipeline.js +271 -0
- package/dist/kernel/writer-helpers.d.ts +24 -0
- package/dist/kernel/writer-helpers.js +42 -0
- package/dist/memory/decay.d.ts +31 -0
- package/dist/memory/decay.js +97 -0
- package/dist/memory/ecphory.d.ts +25 -0
- package/dist/memory/ecphory.js +178 -0
- package/dist/memory/encodingContext.d.ts +21 -0
- package/dist/memory/encodingContext.js +62 -0
- package/dist/memory/index.d.ts +23 -0
- package/dist/memory/index.js +23 -0
- package/dist/memory/memoryType.d.ts +7 -0
- package/dist/memory/memoryType.js +24 -0
- package/dist/memory/policy.d.ts +35 -0
- package/dist/memory/policy.js +35 -0
- package/dist/memory/retrieval.d.ts +21 -0
- package/dist/memory/retrieval.js +145 -0
- package/dist/testing/index.d.ts +8 -0
- package/dist/testing/index.js +8 -0
- package/dist/testing/recordSearchConformance.d.ts +43 -0
- package/dist/testing/recordSearchConformance.js +147 -0
- package/dist/viz/ascii.d.ts +7 -0
- package/dist/viz/ascii.js +60 -0
- package/dist/viz/health.d.ts +8 -0
- package/dist/viz/health.js +161 -0
- package/dist/viz/index.d.ts +10 -0
- package/dist/viz/index.js +10 -0
- package/dist/viz/matrix.d.ts +8 -0
- package/dist/viz/matrix.js +84 -0
- package/dist/viz/mermaid.d.ts +38 -0
- package/dist/viz/mermaid.js +706 -0
- package/package.json +82 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ResearchExtension — curated research syntheses with evidence ratings (TS twin).
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with python/dna/extensions/research/__init__.py.
|
|
5
|
+
*
|
|
6
|
+
* Storage layout per doc:
|
|
7
|
+
*
|
|
8
|
+
* .dna/<scope>/research/<name>/
|
|
9
|
+
* RESEARCH.md ← marker (frontmatter = spec)
|
|
10
|
+
*
|
|
11
|
+
* A Research synthesizes N Reference docs with objective, methodology,
|
|
12
|
+
* evidence-rated findings, and priority recommendations — agent-facing
|
|
13
|
+
* knowledge WITH provenance (the declarative counter to LLM-generated
|
|
14
|
+
* repo-wiki prose).
|
|
15
|
+
*
|
|
16
|
+
* Tenancy: PERMISSIVE — no `scope` attribute declared, so the write
|
|
17
|
+
* pipeline treats it as permissive (base writes with or without a
|
|
18
|
+
* tenant). Research is repo-authored knowledge, not per-client data.
|
|
19
|
+
*
|
|
20
|
+
* The Reference companion Kind is provided by the sdlc extension
|
|
21
|
+
* (alias sdlc-reference); it is NOT registered here.
|
|
22
|
+
*/
|
|
23
|
+
import yaml from "js-yaml";
|
|
24
|
+
import { KindBase } from "../kernel/kind_base.js";
|
|
25
|
+
import { SD } from "../kernel/protocols.js";
|
|
26
|
+
import { writeEntriesToHandle } from "../kernel/writer-helpers.js";
|
|
27
|
+
const API_VERSION = "github.com/ruinosus/dna/research/v1";
|
|
28
|
+
export const METHODOLOGIES = [
|
|
29
|
+
"web-search-curated",
|
|
30
|
+
"literature-review",
|
|
31
|
+
"interview",
|
|
32
|
+
"field-study",
|
|
33
|
+
"experiment",
|
|
34
|
+
"synthesis",
|
|
35
|
+
"other",
|
|
36
|
+
];
|
|
37
|
+
export const EVIDENCE_RATINGS = ["evidence-based", "opinion-practice", "anecdotal"];
|
|
38
|
+
export const STATUSES = ["brief", "ready", "draft", "published", "superseded", "retracted"];
|
|
39
|
+
export const VISIBILITY = ["scope-private", "shared"];
|
|
40
|
+
function parseFrontmatter(text) {
|
|
41
|
+
const m = text.match(/^---\n([\s\S]*?)---\n?([\s\S]*)$/);
|
|
42
|
+
if (!m)
|
|
43
|
+
return { fm: {}, body: text };
|
|
44
|
+
try {
|
|
45
|
+
const parsed = yaml.load(m[1]);
|
|
46
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
47
|
+
return { fm: parsed, body: (m[2] ?? "").replace(/^\n+/, "") };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// fall through
|
|
52
|
+
}
|
|
53
|
+
return { fm: {}, body: text };
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Research Kind
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
class ResearchKind extends KindBase {
|
|
59
|
+
apiVersion = API_VERSION;
|
|
60
|
+
kind = "Research";
|
|
61
|
+
// alias generated as <owner>-<kebab(kind)> = "research-research" (owner =
|
|
62
|
+
// extension name). New Kinds must not TYPE an explicit alias (guard:
|
|
63
|
+
// tests/alias-generation.test.ts, EXPLICIT_ALIAS_ALLOWLIST). Empty here
|
|
64
|
+
// keeps the KindPort shape while triggering generation at register time.
|
|
65
|
+
alias = "";
|
|
66
|
+
aliasOwner = "research";
|
|
67
|
+
origin = "github.com/ruinosus/dna/research";
|
|
68
|
+
// PERMISSIVE tenancy — NO `scope` declared (base writes with or without tenant).
|
|
69
|
+
isPromptTarget = false;
|
|
70
|
+
promptTargetPriority = 0;
|
|
71
|
+
flattenInContext = false;
|
|
72
|
+
storage = SD.bundle("research", "RESEARCH.md");
|
|
73
|
+
graphStyle = { fill: "#7C3AED", stroke: "#5B21B6", textColor: "#fff" };
|
|
74
|
+
asciiIcon = "🔬";
|
|
75
|
+
displayLabel = "Research";
|
|
76
|
+
docs = "A Research is a curated synthesis of N external sources " +
|
|
77
|
+
"(Reference docs) with objective, methodology, evidence-rated " +
|
|
78
|
+
"findings, and priority recommendations. Designed for " +
|
|
79
|
+
"auditability + agent consumption.";
|
|
80
|
+
dependencies() { return null; }
|
|
81
|
+
schema() {
|
|
82
|
+
return {
|
|
83
|
+
type: "object",
|
|
84
|
+
required: ["title", "objective", "methodology", "status"],
|
|
85
|
+
additionalProperties: true,
|
|
86
|
+
properties: {
|
|
87
|
+
title: { type: "string" },
|
|
88
|
+
objective: { type: "string" },
|
|
89
|
+
executive_summary: { type: "string" },
|
|
90
|
+
key_takeaways: { type: "array", items: { type: "string" }, default: [] },
|
|
91
|
+
overall_confidence: { type: "string", enum: ["high", "moderate", "low", "very-low"] },
|
|
92
|
+
last_reviewed_at: { type: "string", format: "date-time" },
|
|
93
|
+
next_review_due: { type: "string", format: "date-time" },
|
|
94
|
+
methodology: { type: "string", enum: [...METHODOLOGIES], default: "web-search-curated" },
|
|
95
|
+
conducted_by: { type: "string" },
|
|
96
|
+
conducted_at: { type: "string", format: "date-time" },
|
|
97
|
+
scope_ref: { type: "string" },
|
|
98
|
+
visibility: { type: "string", enum: [...VISIBILITY], default: "scope-private" },
|
|
99
|
+
sources: {
|
|
100
|
+
type: "array",
|
|
101
|
+
items: { type: "string" },
|
|
102
|
+
default: [],
|
|
103
|
+
},
|
|
104
|
+
findings: {
|
|
105
|
+
type: "array",
|
|
106
|
+
items: {
|
|
107
|
+
type: "object",
|
|
108
|
+
required: ["id", "title", "evidence_rating"],
|
|
109
|
+
additionalProperties: true,
|
|
110
|
+
properties: {
|
|
111
|
+
id: { type: "string", pattern: "^f-[a-z0-9-]+$" },
|
|
112
|
+
title: { type: "string" },
|
|
113
|
+
summary: { type: "string" },
|
|
114
|
+
evidence_rating: { type: "string", enum: [...EVIDENCE_RATINGS] },
|
|
115
|
+
source_refs: { type: "array", items: { type: "string" }, default: [] },
|
|
116
|
+
tags: { type: "array", items: { type: "string" }, default: [] },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
default: [],
|
|
120
|
+
},
|
|
121
|
+
recommendations: {
|
|
122
|
+
type: "array",
|
|
123
|
+
items: {
|
|
124
|
+
type: "object",
|
|
125
|
+
required: ["id", "priority", "summary"],
|
|
126
|
+
additionalProperties: true,
|
|
127
|
+
properties: {
|
|
128
|
+
id: { type: "string", pattern: "^rec-[a-z0-9-]+$" },
|
|
129
|
+
priority: { type: "string", enum: ["high", "medium", "low"] },
|
|
130
|
+
summary: { type: "string" },
|
|
131
|
+
effort_hours: { type: "number" },
|
|
132
|
+
clinical_decision: { type: "boolean", default: false },
|
|
133
|
+
depends_on: { type: "array", items: { type: "string" }, default: [] },
|
|
134
|
+
backed_by_findings: { type: "array", items: { type: "string" }, default: [] },
|
|
135
|
+
status: {
|
|
136
|
+
type: "string",
|
|
137
|
+
enum: ["proposed", "accepted", "rejected", "implemented", "blocked"],
|
|
138
|
+
default: "proposed",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
default: [],
|
|
143
|
+
},
|
|
144
|
+
status: { type: "string", enum: [...STATUSES], default: "draft" },
|
|
145
|
+
superseded_by: { type: "string" },
|
|
146
|
+
retracted_reason: { type: "string" },
|
|
147
|
+
audience_context: { type: "string" },
|
|
148
|
+
research_blocks: {
|
|
149
|
+
type: "array",
|
|
150
|
+
items: {
|
|
151
|
+
type: "object",
|
|
152
|
+
additionalProperties: true,
|
|
153
|
+
properties: {
|
|
154
|
+
title: { type: "string" },
|
|
155
|
+
questions: { type: "array", items: { type: "string" } },
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
default: [],
|
|
159
|
+
},
|
|
160
|
+
output_constraints: { type: "array", items: { type: "string" }, default: [] },
|
|
161
|
+
reference_baselines: { type: "array", items: { type: "string" }, default: [] },
|
|
162
|
+
brief_notes: { type: "string" },
|
|
163
|
+
tags: { type: "array", items: { type: "string" }, default: [] },
|
|
164
|
+
owner: { type: "string" },
|
|
165
|
+
created_at: { type: "string", format: "date-time" },
|
|
166
|
+
updated_at: { type: "string", format: "date-time" },
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
parse(raw) {
|
|
171
|
+
return raw;
|
|
172
|
+
}
|
|
173
|
+
describe(doc) {
|
|
174
|
+
const spec = (doc.spec ?? {});
|
|
175
|
+
const title = spec["title"] ?? "?";
|
|
176
|
+
const status = spec["status"] ?? "draft";
|
|
177
|
+
return `${title} [${status}]`;
|
|
178
|
+
}
|
|
179
|
+
summary(doc) {
|
|
180
|
+
const spec = (doc.spec ?? {});
|
|
181
|
+
const findings = spec["findings"] ?? [];
|
|
182
|
+
const recs = spec["recommendations"] ?? [];
|
|
183
|
+
const evFindings = findings.filter((f) => f.evidence_rating === "evidence-based").length;
|
|
184
|
+
return {
|
|
185
|
+
title: spec["title"] ?? "",
|
|
186
|
+
methodology: spec["methodology"] ?? "",
|
|
187
|
+
status: spec["status"] ?? "draft",
|
|
188
|
+
sources_count: (spec["sources"] ?? []).length,
|
|
189
|
+
findings_count: findings.length,
|
|
190
|
+
evidence_based_count: evFindings,
|
|
191
|
+
recommendations_count: recs.length,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
promptTemplate() { return null; }
|
|
195
|
+
}
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// Reader / Writer
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
class ResearchReader {
|
|
200
|
+
async detect(bundle) {
|
|
201
|
+
return bundle.exists("RESEARCH.md");
|
|
202
|
+
}
|
|
203
|
+
async read(bundle) {
|
|
204
|
+
const text = await bundle.readText("RESEARCH.md");
|
|
205
|
+
const { fm } = parseFrontmatter(text);
|
|
206
|
+
if (fm &&
|
|
207
|
+
typeof fm === "object" &&
|
|
208
|
+
"spec" in fm &&
|
|
209
|
+
fm["spec"] &&
|
|
210
|
+
typeof fm["spec"] === "object") {
|
|
211
|
+
const metadata = fm["metadata"] ?? {};
|
|
212
|
+
if (!metadata["name"])
|
|
213
|
+
metadata["name"] = bundle.name;
|
|
214
|
+
return {
|
|
215
|
+
apiVersion: fm["apiVersion"] ?? API_VERSION,
|
|
216
|
+
kind: fm["kind"] ?? "Research",
|
|
217
|
+
metadata,
|
|
218
|
+
spec: fm["spec"],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
apiVersion: API_VERSION,
|
|
223
|
+
kind: "Research",
|
|
224
|
+
metadata: { name: bundle.name },
|
|
225
|
+
spec: fm,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
class ResearchWriter {
|
|
230
|
+
canWrite(raw) {
|
|
231
|
+
return raw["kind"] === "Research";
|
|
232
|
+
}
|
|
233
|
+
serialize(raw) {
|
|
234
|
+
const spec = raw["spec"] ?? {};
|
|
235
|
+
const meta = raw["metadata"] ?? {};
|
|
236
|
+
const cleanSpec = {};
|
|
237
|
+
for (const [k, v] of Object.entries(spec)) {
|
|
238
|
+
if (v === null || v === undefined || v === "")
|
|
239
|
+
continue;
|
|
240
|
+
if (Array.isArray(v) && v.length === 0)
|
|
241
|
+
continue;
|
|
242
|
+
if (typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0)
|
|
243
|
+
continue;
|
|
244
|
+
cleanSpec[k] = v;
|
|
245
|
+
}
|
|
246
|
+
const cleanMeta = {};
|
|
247
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
248
|
+
if (v !== null && v !== undefined)
|
|
249
|
+
cleanMeta[k] = v;
|
|
250
|
+
}
|
|
251
|
+
const envelope = {
|
|
252
|
+
apiVersion: raw["apiVersion"] ?? API_VERSION,
|
|
253
|
+
kind: raw["kind"] ?? "Research",
|
|
254
|
+
metadata: cleanMeta,
|
|
255
|
+
spec: cleanSpec,
|
|
256
|
+
};
|
|
257
|
+
const fmYaml = yaml.dump(envelope, {
|
|
258
|
+
sortKeys: false,
|
|
259
|
+
lineWidth: 100,
|
|
260
|
+
noRefs: true,
|
|
261
|
+
noCompatMode: true,
|
|
262
|
+
}).replace(/\n+$/, "");
|
|
263
|
+
const title = cleanSpec["title"] ?? "?";
|
|
264
|
+
const method = cleanSpec["methodology"] ?? "synthesis";
|
|
265
|
+
const nFindings = (cleanSpec["findings"] ?? []).length;
|
|
266
|
+
const nSources = (cleanSpec["sources"] ?? []).length;
|
|
267
|
+
const body = `# Research — ${title}\n\n` +
|
|
268
|
+
`Methodology: ${method} · ${nSources} sources · ${nFindings} findings.\n\n` +
|
|
269
|
+
`This file's spec (frontmatter above) is the authoritative ` +
|
|
270
|
+
`data. The prose below is for human reading and is regenerated ` +
|
|
271
|
+
`on each write.\n`;
|
|
272
|
+
return [
|
|
273
|
+
{ relativePath: "RESEARCH.md", content: `---\n${fmYaml}\n---\n\n${body}` },
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
async write(bundle, raw) {
|
|
277
|
+
await writeEntriesToHandle(bundle, this.serialize(raw));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
// Extension
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
export class ResearchExtension {
|
|
284
|
+
name = "research";
|
|
285
|
+
version = "1.2.0";
|
|
286
|
+
register(kernel) {
|
|
287
|
+
kernel.kind(new ResearchKind());
|
|
288
|
+
// Reference companion is provided by the sdlc extension — not here.
|
|
289
|
+
kernel.reader(new ResearchReader());
|
|
290
|
+
kernel.writer(new ResearchWriter());
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SafetyPolicyExtension — SafetyPolicy kind + SafetyPolicyReader + SafetyPolicyWriter.
|
|
3
|
+
*
|
|
4
|
+
* Declarative safety enforcement rules. Tier 1 (regex) is built-in;
|
|
5
|
+
* heavier tiers (ml, api, llm_judge) are opt-in.
|
|
6
|
+
*
|
|
7
|
+
* 1:1 parity with Python dna.v3.extensions.safety.
|
|
8
|
+
*/
|
|
9
|
+
import type { BundleHandle } from "../kernel/bundle-handle.js";
|
|
10
|
+
import type { FSLike } from "../kernel/fs.js";
|
|
11
|
+
import type { ExtensionHost, Extension, ReaderPort, SerializedFile, WriterPort } from "../kernel/protocols.js";
|
|
12
|
+
export declare class SafetyPolicyReader implements ReaderPort {
|
|
13
|
+
private fs;
|
|
14
|
+
readonly _marker = "SAFETYPOLICY.md";
|
|
15
|
+
constructor(fs?: FSLike);
|
|
16
|
+
detect(bundle: BundleHandle): boolean;
|
|
17
|
+
read(bundle: BundleHandle): Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export declare class SafetyPolicyWriter implements WriterPort {
|
|
20
|
+
private fs;
|
|
21
|
+
readonly _kind = "SafetyPolicy";
|
|
22
|
+
constructor(fs?: FSLike);
|
|
23
|
+
canWrite(raw: Record<string, unknown>): boolean;
|
|
24
|
+
write(bundle: BundleHandle, raw: Record<string, unknown>): void;
|
|
25
|
+
serialize(raw: Record<string, unknown>): SerializedFile[];
|
|
26
|
+
}
|
|
27
|
+
export declare class SafetyPolicyExtension implements Extension {
|
|
28
|
+
private fs;
|
|
29
|
+
readonly name = "safety";
|
|
30
|
+
readonly version = "1.0.0";
|
|
31
|
+
constructor(fs?: FSLike);
|
|
32
|
+
register(kernel: ExtensionHost): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SafetyPolicyExtension — SafetyPolicy kind + SafetyPolicyReader + SafetyPolicyWriter.
|
|
3
|
+
*
|
|
4
|
+
* Declarative safety enforcement rules. Tier 1 (regex) is built-in;
|
|
5
|
+
* heavier tiers (ml, api, llm_judge) are opt-in.
|
|
6
|
+
*
|
|
7
|
+
* 1:1 parity with Python dna.v3.extensions.safety.
|
|
8
|
+
*/
|
|
9
|
+
import yaml from "js-yaml";
|
|
10
|
+
import { nodeFS } from "../kernel/fs.js";
|
|
11
|
+
import { KindBase } from "../kernel/kind_base.js";
|
|
12
|
+
import { SafetyPolicySchema, SafetyPolicySpecSchema, zodSpecToJsonSchema } from "../kernel/models.js";
|
|
13
|
+
import { SD } from "../kernel/protocols.js";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// SafetyPolicyKind
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
class SafetyPolicyKind extends KindBase {
|
|
18
|
+
apiVersion = "github.com/ruinosus/dna/v1";
|
|
19
|
+
kind = "SafetyPolicy";
|
|
20
|
+
alias = "helix-safety-policy";
|
|
21
|
+
isSchemaAffecting = true;
|
|
22
|
+
origin = "github.com/ruinosus/dna";
|
|
23
|
+
isPromptTarget = false;
|
|
24
|
+
promptTargetPriority = 0;
|
|
25
|
+
flattenInContext = false;
|
|
26
|
+
descriptionFallbackField = "instruction";
|
|
27
|
+
storage = SD.bundle("safety", "SAFETYPOLICY.md", "list", "rules");
|
|
28
|
+
graphStyle = { fill: "#DC2626", stroke: "#B91C1C", textColor: "#fff" };
|
|
29
|
+
asciiIcon = "🔒";
|
|
30
|
+
displayLabel = "Safety Policies";
|
|
31
|
+
_sourceUrl = import.meta.url;
|
|
32
|
+
docs = "A SafetyPolicy declares runtime enforcement rules for PII masking, " +
|
|
33
|
+
"content safety, topic restriction, prompt injection detection, and " +
|
|
34
|
+
"custom regex patterns. Rules are tiered: regex (built-in), ml, api, " +
|
|
35
|
+
"llm_judge. Stored as a bundle rooted on SAFETYPOLICY.md.";
|
|
36
|
+
uiSchema = {
|
|
37
|
+
scope: {
|
|
38
|
+
widget: "select",
|
|
39
|
+
label: "Scope",
|
|
40
|
+
help: "input guards the user prompt; output guards the agent response; both runs on each side.",
|
|
41
|
+
options: ["input", "output", "both"],
|
|
42
|
+
order: 1,
|
|
43
|
+
},
|
|
44
|
+
action: {
|
|
45
|
+
widget: "select",
|
|
46
|
+
label: "Action",
|
|
47
|
+
help: "mask: redact PII inline; block: reject the message; log: pass through with violation metadata.",
|
|
48
|
+
options: ["mask", "block", "log"],
|
|
49
|
+
order: 2,
|
|
50
|
+
},
|
|
51
|
+
severity: {
|
|
52
|
+
widget: "select",
|
|
53
|
+
label: "Severity",
|
|
54
|
+
help: "error fails the turn; warn lets the turn continue.",
|
|
55
|
+
options: ["error", "warn"],
|
|
56
|
+
order: 3,
|
|
57
|
+
},
|
|
58
|
+
rules: {
|
|
59
|
+
widget: "code",
|
|
60
|
+
language: "yaml",
|
|
61
|
+
label: "Rules",
|
|
62
|
+
help: "YAML list of safety rules (type, tier, entities, patterns, etc.)",
|
|
63
|
+
height: 300,
|
|
64
|
+
order: 4,
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
depFilters() { return { recognizers: "presidio-recognizer" }; }
|
|
68
|
+
schema() { return zodSpecToJsonSchema(SafetyPolicySpecSchema); }
|
|
69
|
+
parse(raw) {
|
|
70
|
+
return SafetyPolicySchema.parse(raw);
|
|
71
|
+
}
|
|
72
|
+
describe(doc) {
|
|
73
|
+
const spec = (doc.spec ?? {});
|
|
74
|
+
return `SafetyPolicy: ${doc.name} (scope=${spec.scope}, action=${spec.action})`;
|
|
75
|
+
}
|
|
76
|
+
summary(doc) {
|
|
77
|
+
const spec = (doc.spec ?? {});
|
|
78
|
+
return {
|
|
79
|
+
scope: typeof spec.scope === "string" ? spec.scope : "both",
|
|
80
|
+
action: typeof spec.action === "string" ? spec.action : "mask",
|
|
81
|
+
severity: typeof spec.severity === "string" ? spec.severity : "error",
|
|
82
|
+
rules: Array.isArray(spec.rules) ? spec.rules.length : 0,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
preview(doc) {
|
|
86
|
+
const spec = (doc.spec ?? {});
|
|
87
|
+
const blocks = [];
|
|
88
|
+
// Policy fields
|
|
89
|
+
const meta = [];
|
|
90
|
+
if (typeof spec.scope === "string")
|
|
91
|
+
meta.push({ label: "Scope", value: spec.scope });
|
|
92
|
+
if (typeof spec.action === "string")
|
|
93
|
+
meta.push({ label: "Action", value: spec.action });
|
|
94
|
+
if (typeof spec.severity === "string")
|
|
95
|
+
meta.push({ label: "Severity", value: spec.severity });
|
|
96
|
+
if (meta.length > 0) {
|
|
97
|
+
blocks.push({ kind: "fields", title: "Policy", fields: meta });
|
|
98
|
+
}
|
|
99
|
+
// Rules as YAML code block
|
|
100
|
+
const rules = spec.rules;
|
|
101
|
+
if (Array.isArray(rules) && rules.length > 0) {
|
|
102
|
+
blocks.push({
|
|
103
|
+
kind: "code",
|
|
104
|
+
title: "Rules",
|
|
105
|
+
body: yaml.dump(rules, { flowLevel: 3 }).trim(),
|
|
106
|
+
language: "yaml",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (blocks.length === 0) {
|
|
110
|
+
return [{ kind: "empty", title: "SafetyPolicy (empty)" }];
|
|
111
|
+
}
|
|
112
|
+
return blocks;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// SafetyPolicyReader
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
function parseFrontmatter(text) {
|
|
119
|
+
const match = text.match(/^---\n([\s\S]*?)---\n?/);
|
|
120
|
+
if (!match)
|
|
121
|
+
return [{}, text];
|
|
122
|
+
let fm = {};
|
|
123
|
+
try {
|
|
124
|
+
const parsed = yaml.load(match[1]);
|
|
125
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
126
|
+
fm = parsed;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch { /* ignore */ }
|
|
130
|
+
const body = text.slice(match[0].length);
|
|
131
|
+
return [fm, body];
|
|
132
|
+
}
|
|
133
|
+
export class SafetyPolicyReader {
|
|
134
|
+
fs;
|
|
135
|
+
_marker = "SAFETYPOLICY.md";
|
|
136
|
+
constructor(fs = nodeFS) {
|
|
137
|
+
this.fs = fs;
|
|
138
|
+
}
|
|
139
|
+
detect(bundle) {
|
|
140
|
+
const path = bundle.path ?? "";
|
|
141
|
+
return this.fs.exists(`${path}/SAFETYPOLICY.md`);
|
|
142
|
+
}
|
|
143
|
+
read(bundle) {
|
|
144
|
+
const path = bundle.path ?? "";
|
|
145
|
+
const text = this.fs.readFile(`${path}/SAFETYPOLICY.md`);
|
|
146
|
+
const [fm, body] = parseFrontmatter(text);
|
|
147
|
+
const name = String(fm.name || "") || path.split("/").pop() || "";
|
|
148
|
+
const description = String(fm.description || "");
|
|
149
|
+
const scope = String(fm.scope || "both");
|
|
150
|
+
const action = String(fm.action || "mask");
|
|
151
|
+
const severity = String(fm.severity || "error");
|
|
152
|
+
// Parse body as YAML list of rules
|
|
153
|
+
let rules = [];
|
|
154
|
+
const trimmedBody = body.trim();
|
|
155
|
+
if (trimmedBody) {
|
|
156
|
+
try {
|
|
157
|
+
const parsed = yaml.load(trimmedBody);
|
|
158
|
+
if (Array.isArray(parsed)) {
|
|
159
|
+
rules = parsed;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch { /* ignore invalid YAML */ }
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
apiVersion: "github.com/ruinosus/dna/v1",
|
|
166
|
+
kind: "SafetyPolicy",
|
|
167
|
+
metadata: { name, description },
|
|
168
|
+
spec: { scope, action, severity, rules },
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// SafetyPolicyWriter
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
export class SafetyPolicyWriter {
|
|
176
|
+
fs;
|
|
177
|
+
_kind = "SafetyPolicy";
|
|
178
|
+
constructor(fs = nodeFS) {
|
|
179
|
+
this.fs = fs;
|
|
180
|
+
}
|
|
181
|
+
canWrite(raw) {
|
|
182
|
+
return raw.kind === "SafetyPolicy";
|
|
183
|
+
}
|
|
184
|
+
write(bundle, raw) {
|
|
185
|
+
const path = bundle.path ?? "";
|
|
186
|
+
this.fs.mkdir(path);
|
|
187
|
+
for (const f of this.serialize(raw)) {
|
|
188
|
+
this.fs.writeFile(`${path}/${f.relativePath}`, f.content ?? "");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
serialize(raw) {
|
|
192
|
+
const meta = (raw.metadata ?? {});
|
|
193
|
+
const spec = (raw.spec ?? {});
|
|
194
|
+
const fmParts = [];
|
|
195
|
+
if (meta.name)
|
|
196
|
+
fmParts.push(`name: ${meta.name}`);
|
|
197
|
+
if (meta.description)
|
|
198
|
+
fmParts.push(`description: ${meta.description}`);
|
|
199
|
+
fmParts.push(`scope: ${spec.scope ?? "both"}`);
|
|
200
|
+
fmParts.push(`action: ${spec.action ?? "mask"}`);
|
|
201
|
+
fmParts.push(`severity: ${spec.severity ?? "error"}`);
|
|
202
|
+
const frontmatter = fmParts.join("\n");
|
|
203
|
+
// Serialize rules as YAML list
|
|
204
|
+
const rules = spec.rules ?? [];
|
|
205
|
+
let body = "";
|
|
206
|
+
if (rules.length > 0) {
|
|
207
|
+
body = yaml.dump(rules, { flowLevel: 3 }).trim();
|
|
208
|
+
}
|
|
209
|
+
const content = body
|
|
210
|
+
? `---\n${frontmatter}\n---\n\n${body}\n`
|
|
211
|
+
: `---\n${frontmatter}\n---\n`;
|
|
212
|
+
return [{ relativePath: "SAFETYPOLICY.md", content }];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Extension
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
export class SafetyPolicyExtension {
|
|
219
|
+
fs;
|
|
220
|
+
name = "safety";
|
|
221
|
+
version = "1.0.0";
|
|
222
|
+
constructor(fs = nodeFS) {
|
|
223
|
+
this.fs = fs;
|
|
224
|
+
}
|
|
225
|
+
register(kernel) {
|
|
226
|
+
kernel.kind(new SafetyPolicyKind());
|
|
227
|
+
kernel.reader(new SafetyPolicyReader(this.fs));
|
|
228
|
+
kernel.writer(new SafetyPolicyWriter(this.fs));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# ADR — Architecture Decision Record (Nygard 2011 / MADR), record plane.
|
|
2
|
+
# One ratified architectural decision per doc: context/decision/consequences.
|
|
3
|
+
# Split out from Narrative.decisions[] (Phase 2.2 vocabulary sanitization).
|
|
4
|
+
#
|
|
5
|
+
# F3 lote-2 (spec 2026-06-10-kinds-descriptor-f3): synthesized from this
|
|
6
|
+
# descriptor; the hand-written class twins were deleted. PARITY-CRITICAL
|
|
7
|
+
# package data — byte-identical mirror under packages/sdk-{py,ts};
|
|
8
|
+
# tests/test_descriptor_hash_parity.py enforces. Equivalence vs the extinct
|
|
9
|
+
# class frozen in tests/test_lote2_descriptor_equivalence.py (golden:
|
|
10
|
+
# tests/goldens/lote2/ADR.golden.json). Deltas intencionais (semântica
|
|
11
|
+
# canônica = port): summary curado substitui o eco whole-spec do KindBase
|
|
12
|
+
# default (list endpoints não usam kp.summary()); parse valida contra o
|
|
13
|
+
# schema (classe era pass-through); to_card morto não migrado.
|
|
14
|
+
apiVersion: github.com/ruinosus/dna/core/v1
|
|
15
|
+
kind: KindDefinition
|
|
16
|
+
metadata:
|
|
17
|
+
name: adr
|
|
18
|
+
spec:
|
|
19
|
+
target_api_version: github.com/ruinosus/dna/sdlc/v1
|
|
20
|
+
target_kind: ADR
|
|
21
|
+
alias: sdlc-adr
|
|
22
|
+
origin: github.com/ruinosus/dna/sdlc
|
|
23
|
+
tenant_scope: global
|
|
24
|
+
plane: record
|
|
25
|
+
prompt_target: false
|
|
26
|
+
flatten_in_context: false
|
|
27
|
+
prompt_target_priority: 0
|
|
28
|
+
storage:
|
|
29
|
+
type: bundle
|
|
30
|
+
container: adrs
|
|
31
|
+
marker: ADR.md
|
|
32
|
+
body_field: body
|
|
33
|
+
dep_filters:
|
|
34
|
+
superseded_by: sdlc-adr
|
|
35
|
+
supersedes: sdlc-adr
|
|
36
|
+
covers_features: sdlc-feature
|
|
37
|
+
schema:
|
|
38
|
+
type: object
|
|
39
|
+
required:
|
|
40
|
+
- title
|
|
41
|
+
- status
|
|
42
|
+
- context
|
|
43
|
+
- decision
|
|
44
|
+
properties:
|
|
45
|
+
title:
|
|
46
|
+
type: string
|
|
47
|
+
description: Decision headline — start with imperative verb.
|
|
48
|
+
status:
|
|
49
|
+
type: string
|
|
50
|
+
enum:
|
|
51
|
+
- proposed
|
|
52
|
+
- accepted
|
|
53
|
+
- deprecated
|
|
54
|
+
- superseded
|
|
55
|
+
description: 'Lifecycle: proposed → accepted → deprecated|superseded. Use
|
|
56
|
+
`superseded` (not deprecated) when a newer ADR replaces this one — link
|
|
57
|
+
via `superseded_by`.'
|
|
58
|
+
context:
|
|
59
|
+
type: string
|
|
60
|
+
description: WHY we needed to decide. What forces are in play? What constraints
|
|
61
|
+
(technical, business, team) shape the choice? 1-3 paragraphs.
|
|
62
|
+
decision:
|
|
63
|
+
type: string
|
|
64
|
+
description: 'WHAT we decided. Active voice: ''We will X'' or ''We chose X
|
|
65
|
+
over Y''. 1-2 paragraphs.'
|
|
66
|
+
consequences:
|
|
67
|
+
type: string
|
|
68
|
+
description: What follows from this decision — positive AND negative. Future
|
|
69
|
+
readers need to see the trade-offs, not just the wins.
|
|
70
|
+
alternatives_considered:
|
|
71
|
+
type: array
|
|
72
|
+
items:
|
|
73
|
+
type: string
|
|
74
|
+
description: Other options weighed and rejected, with brief why-not.
|
|
75
|
+
deciders:
|
|
76
|
+
type: array
|
|
77
|
+
items:
|
|
78
|
+
type: string
|
|
79
|
+
description: Actor names who participated in the decision.
|
|
80
|
+
date:
|
|
81
|
+
type: string
|
|
82
|
+
format: date
|
|
83
|
+
description: Date the decision was accepted (ISO-8601).
|
|
84
|
+
superseded_by:
|
|
85
|
+
type: string
|
|
86
|
+
description: ADR slug that supersedes this one (when status=superseded).
|
|
87
|
+
supersedes:
|
|
88
|
+
type: array
|
|
89
|
+
items:
|
|
90
|
+
type: string
|
|
91
|
+
description: ADR slugs this one replaces.
|
|
92
|
+
covers_features:
|
|
93
|
+
type: array
|
|
94
|
+
items:
|
|
95
|
+
type: string
|
|
96
|
+
description: Feature names this decision affects.
|
|
97
|
+
narrative_origin:
|
|
98
|
+
type: string
|
|
99
|
+
description: When extracted from a Narrative.decisions[] entry during Phase
|
|
100
|
+
2.2 migration, this points to the source Narrative slug for provenance.
|
|
101
|
+
tags:
|
|
102
|
+
type: array
|
|
103
|
+
items:
|
|
104
|
+
type: string
|
|
105
|
+
description: Free-form tags (e.g. 'persistence', 'auth', 'ui').
|
|
106
|
+
body:
|
|
107
|
+
type: string
|
|
108
|
+
description: Optional full markdown body (ADR.md). When present, takes precedence
|
|
109
|
+
over the structured fields above for rendering. Useful when the ADR predates
|
|
110
|
+
this schema.
|
|
111
|
+
created_at:
|
|
112
|
+
type: string
|
|
113
|
+
format: date-time
|
|
114
|
+
updated_at:
|
|
115
|
+
type: string
|
|
116
|
+
format: date-time
|
|
117
|
+
summary:
|
|
118
|
+
title: ''
|
|
119
|
+
status: ''
|
|
120
|
+
date: ''
|
|
121
|
+
superseded_by: ''
|
|
122
|
+
embed:
|
|
123
|
+
- title
|
|
124
|
+
- context
|
|
125
|
+
- decision
|
|
126
|
+
graph_style:
|
|
127
|
+
fill: '#10B981'
|
|
128
|
+
stroke: '#047857'
|
|
129
|
+
text_color: '#fff'
|
|
130
|
+
ascii_icon: 📐
|
|
131
|
+
display_label: ADRs
|
|
132
|
+
docs: 'An ADR captures ONE architectural decision with its context, rationale, and
|
|
133
|
+
consequences. Convention: one ADR per file, immutable once accepted (subsequent
|
|
134
|
+
decisions supersede). Follows Nygard / MADR template — Adopt on ThoughtWorks Tech
|
|
135
|
+
Radar. Studio renders these as the decision log of the project; PMs/architects
|
|
136
|
+
can scan rationale without reading code.'
|