refactor: make sdd-lab review-driven (WealthLedger/specs#66)
Replace executable schema checks with explicit review-driven readiness, add a requirement-writing profile, and incorporate review clarifications for source traceability and comparative validation.
This commit was merged in pull request #68.
This commit is contained in:
@@ -1,294 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
// Deterministic checks for wealth-ledger-sdd-lab changes. OpenSpec CLI 1.13.1 has no conditional
|
|
||||||
// artifacts, gate records or archive policy, so this file covers only what the CLI cannot.
|
|
||||||
// It is the single source for profile, ordering, gate and readiness rules; schema.yaml points here.
|
|
||||||
// Semantic quality (contradictions, invented facts, design fit) stays with reviewers.
|
|
||||||
//
|
|
||||||
// Usage: node check.cjs <plan|apply|archive> <change-name> [--root <openspec-root>]
|
|
||||||
// plan — artifact consistency while planning; missing gates are notes, not errors.
|
|
||||||
// apply — plan + every apply blocker is an error.
|
|
||||||
// archive — apply + all tasks done, G4 approved, lab retrospective.
|
|
||||||
// Exit: 0 ok, 1 violations, 2 invocation/environment error.
|
|
||||||
"use strict";
|
|
||||||
const fs = require("node:fs");
|
|
||||||
const path = require("node:path");
|
|
||||||
|
|
||||||
const SA_MODULES = ["data-mapping", "failure-model", "nfr", "observability"];
|
|
||||||
// Primary file of each conditional artifact, relative to the change directory.
|
|
||||||
const PRIMARY = {
|
|
||||||
research: "research.md",
|
|
||||||
"ux-design": "ux-design.md",
|
|
||||||
"system-analysis": "system-analysis/system-analysis.md",
|
|
||||||
"data-mapping": "system-analysis/data-mapping.md",
|
|
||||||
"failure-model": "system-analysis/failure-model.md",
|
|
||||||
nfr: "system-analysis/nfr.md",
|
|
||||||
observability: "system-analysis/observability.md",
|
|
||||||
"security-analysis": "security-analysis.md",
|
|
||||||
"test-cases": "test-cases",
|
|
||||||
"docs-impact": "docs-impact.md",
|
|
||||||
};
|
|
||||||
const CONDITIONAL = Object.keys(PRIMARY);
|
|
||||||
// A selected conditional input must exist before any of these files is written.
|
|
||||||
const BEFORE_SOLUTION = ["design.md", "qa-analysis.md", "tasks.md"];
|
|
||||||
const DOWNSTREAM = {
|
|
||||||
research: ["specs", ...BEFORE_SOLUTION],
|
|
||||||
"ux-design": BEFORE_SOLUTION,
|
|
||||||
"system-analysis": BEFORE_SOLUTION,
|
|
||||||
...Object.fromEntries(SA_MODULES.map((id) => [id, BEFORE_SOLUTION])),
|
|
||||||
"security-analysis": BEFORE_SOLUTION,
|
|
||||||
"test-cases": ["tasks.md"],
|
|
||||||
"docs-impact": ["tasks.md"],
|
|
||||||
};
|
|
||||||
// Gate that may approve each artifact. Complex changes approve system analysis at G2 (contract-first).
|
|
||||||
const SA_IDS = ["system-analysis", ...SA_MODULES];
|
|
||||||
const allowedGates = (id, complex, hasG2) => {
|
|
||||||
if (id === "proposal") return ["G1"];
|
|
||||||
if (id === "change-profile") return ["G1", "G2", "G3"]; // later profile changes need a recorded decision
|
|
||||||
if (id === "specs") return hasG2 ? ["G2"] : ["G3"];
|
|
||||||
if (SA_IDS.includes(id)) return complex ? ["G2"] : hasG2 ? ["G2", "G3"] : ["G3"];
|
|
||||||
return ["G3"];
|
|
||||||
};
|
|
||||||
const NOT_APPROVED = new Set(["research", "process-record"]); // input context / the log itself
|
|
||||||
const REF = /`([a-z0-9][a-z0-9/-]*)`\s*[—–-]\s*`([^`]+)`/g;
|
|
||||||
|
|
||||||
const fail = (msg) => { console.error(msg); process.exit(2); };
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
const [mode, changeName] = args;
|
|
||||||
if (!["plan", "apply", "archive"].includes(mode) || !changeName) {
|
|
||||||
fail("Usage: node check.cjs <plan|apply|archive> <change-name> [--root <openspec-root>]");
|
|
||||||
}
|
|
||||||
const rootIdx = args.indexOf("--root");
|
|
||||||
if (rootIdx > 0 && !args[rootIdx + 1]) fail("--root needs a path");
|
|
||||||
const root = path.resolve(rootIdx > 0 ? args[rootIdx + 1] : path.join(__dirname, "..", ".."));
|
|
||||||
const changeDir = path.join(root, "changes", changeName);
|
|
||||||
if (!fs.existsSync(changeDir)) fail(`Change directory not found: ${changeDir}`);
|
|
||||||
|
|
||||||
const errors = [];
|
|
||||||
const warnings = [];
|
|
||||||
const notes = [];
|
|
||||||
const rel = (p) => path.relative(changeDir, p);
|
|
||||||
const read = (p) => fs.readFileSync(p, "utf8");
|
|
||||||
const at = (p) => path.join(changeDir, p);
|
|
||||||
const walk = (dir) => fs.existsSync(dir)
|
|
||||||
? fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) =>
|
|
||||||
e.isDirectory() ? walk(path.join(dir, e.name)) : [path.join(dir, e.name)])
|
|
||||||
: [];
|
|
||||||
const present = (p) => p === "test-cases" || p === "specs"
|
|
||||||
? walk(at(p)).some((f) => f.endsWith(".md"))
|
|
||||||
: fs.existsSync(at(p));
|
|
||||||
const mdFiles = walk(changeDir).filter((f) => f.endsWith(".md"));
|
|
||||||
const strip = (s) => (s ?? "").replace(/`/g, "").trim();
|
|
||||||
// Columns: fixed count, the last cell keeps any `|` it contains.
|
|
||||||
const tableRows = (text, heading, columns) => {
|
|
||||||
const section = text.split(/^## /m).find((s) => s.startsWith(heading));
|
|
||||||
if (!section) return null;
|
|
||||||
return section.split("\n").filter((l) => /^\|/.test(l)).slice(2).map((l) => {
|
|
||||||
const parts = l.replace(/^\|/, "").replace(/\|\s*$/, "").split("|");
|
|
||||||
return [...parts.slice(0, columns - 1), parts.slice(columns - 1).join("|")].map((c) => c.trim());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
finishIfMissing();
|
|
||||||
const record = read(at("process-record.md"));
|
|
||||||
|
|
||||||
// 1. Unfilled templates.
|
|
||||||
for (const f of mdFiles) {
|
|
||||||
if (read(f).includes("<!-- guide:")) errors.push(`${rel(f)}: unfilled template guide comment remains`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Mode, class, Change Profile.
|
|
||||||
const labMode = (record.match(/\*\*Mode:\*\*\s*(lab|delivery)\b/i) || [])[1]?.toLowerCase();
|
|
||||||
if (!labMode) errors.push("process-record.md: **Mode:** must be `lab` or `delivery`");
|
|
||||||
const klass = (record.match(/\*\*Class:\*\*\s*(standard|complex)\b/i) || [])[1]?.toLowerCase();
|
|
||||||
if (!klass) errors.push("process-record.md: **Class:** must be `standard` or `complex`");
|
|
||||||
const complex = klass === "complex";
|
|
||||||
const hasG2 = labMode === "lab" || complex;
|
|
||||||
|
|
||||||
const profile = new Map();
|
|
||||||
for (const [rawId, selected, reason] of tableRows(record, "Change Profile", 3) ?? []) {
|
|
||||||
const id = strip(rawId);
|
|
||||||
if (!CONDITIONAL.includes(id)) { errors.push(`Change Profile: unknown artifact '${id}'`); continue; }
|
|
||||||
if (!/^(yes|no)$/i.test(strip(selected))) errors.push(`Change Profile: '${id}' Selected must be yes|no`);
|
|
||||||
if (!reason) errors.push(`Change Profile: '${id}' needs a trigger or reason`);
|
|
||||||
profile.set(id, /^yes$/i.test(strip(selected)));
|
|
||||||
}
|
|
||||||
const pending = [];
|
|
||||||
for (const id of CONDITIONAL) {
|
|
||||||
if (!profile.has(id)) { errors.push(`Change Profile: no decision for '${id}'`); continue; }
|
|
||||||
const has = present(PRIMARY[id]);
|
|
||||||
if (profile.get(id) && !has) {
|
|
||||||
const consumer = DOWNSTREAM[id].find(present);
|
|
||||||
if (consumer) errors.push(`'${id}' is selected but ${PRIMARY[id]} does not exist while ${consumer} was already written`);
|
|
||||||
else pending.push(id);
|
|
||||||
}
|
|
||||||
if (!profile.get(id) && has) errors.push(`'${id}' is not selected but ${PRIMARY[id]} exists; select it through a gate decision or remove the file`);
|
|
||||||
}
|
|
||||||
for (const id of SA_MODULES) {
|
|
||||||
if (profile.get(id) && !profile.get("system-analysis")) errors.push(`'${id}' requires system-analysis to be selected`);
|
|
||||||
}
|
|
||||||
if (fs.existsSync(at("system-analysis")) && !present(PRIMARY["system-analysis"])) {
|
|
||||||
errors.push("system-analysis/ exists without system-analysis/system-analysis.md");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Requirement references resolve; delta requirements are covered.
|
|
||||||
const skipSpecs = present(".openspec.yaml") && /^skip_specs:\s*true/m.test(read(at(".openspec.yaml")));
|
|
||||||
if (skipSpecs && present("specs")) errors.push("skip_specs: true but specs/ contains delta files");
|
|
||||||
const deltaReqs = [];
|
|
||||||
const known = new Map();
|
|
||||||
const removed = new Set();
|
|
||||||
const addKnown = (cap, h) => (known.get(cap) ?? known.set(cap, new Set()).get(cap)).add(h);
|
|
||||||
const key = (cap, req) => `${cap}\u0000${req}`;
|
|
||||||
for (const f of walk(at("specs")).filter((x) => x.endsWith("spec.md"))) {
|
|
||||||
const cap = path.relative(at("specs"), path.dirname(f)).split(path.sep).join("/");
|
|
||||||
let op = null;
|
|
||||||
const seen = new Set();
|
|
||||||
for (const line of read(f).split("\n")) {
|
|
||||||
op = (line.match(/^## (ADDED|MODIFIED|REMOVED|RENAMED) Requirements/) || [])[1] ?? op;
|
|
||||||
const req = (line.match(/^### Requirement:\s*(.+?)\s*$/) || [])[1];
|
|
||||||
if (req) {
|
|
||||||
if (seen.has(req)) errors.push(`specs/${cap}/spec.md: duplicate Requirement '${req}'`);
|
|
||||||
seen.add(req);
|
|
||||||
if (op === "REMOVED") removed.add(key(cap, req));
|
|
||||||
else addKnown(cap, req);
|
|
||||||
if (op === "ADDED" || op === "MODIFIED") deltaReqs.push(key(cap, req));
|
|
||||||
}
|
|
||||||
const to = (line.match(/^-?\s*TO:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/) || [])[1];
|
|
||||||
if (to) addKnown(cap, to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const f of walk(path.join(root, "specs")).filter((x) => x.endsWith("spec.md"))) {
|
|
||||||
const cap = path.relative(path.join(root, "specs"), path.dirname(f)).split(path.sep).join("/");
|
|
||||||
for (const m of read(f).matchAll(/^### Requirement:\s*(.+?)\s*$/gm)) {
|
|
||||||
if (!removed.has(key(cap, m[1]))) addKnown(cap, m[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const refs = (text) => [...text.matchAll(REF)].map((m) => ({ cap: m[1], req: m[2] }));
|
|
||||||
for (const f of mdFiles.filter((x) => !rel(x).startsWith(`specs${path.sep}`))) {
|
|
||||||
for (const { cap, req } of refs(read(f))) {
|
|
||||||
if (!known.get(cap)?.has(req)) errors.push(`${rel(f)}: unknown requirement reference \`${cap}\` — \`${req}\``);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const tasksPath = at("tasks.md");
|
|
||||||
const tasksText = fs.existsSync(tasksPath) ? read(tasksPath) : "";
|
|
||||||
const coversLines = tasksText.split("\n").filter((l) => /\*\*Covers:\*\*/.test(l)).join("\n");
|
|
||||||
const taskCover = new Set(refs(coversLines).map((r) => key(r.cap, r.req)));
|
|
||||||
const qaText = [at("qa-analysis.md"), ...walk(at("test-cases"))].filter(fs.existsSync).map(read).join("\n");
|
|
||||||
const qaCover = new Set(refs(qaText).map((r) => key(r.cap, r.req)));
|
|
||||||
for (const k of deltaReqs) {
|
|
||||||
const label = k.replace("\u0000", "` — `");
|
|
||||||
if (tasksText && !taskCover.has(k)) errors.push(`tasks.md: delta requirement \`${label}\` has no task Covers`);
|
|
||||||
if (present("qa-analysis.md") && !qaCover.has(k)) warnings.push(`qa-analysis: delta requirement \`${label}\` has no verification reference (allowed only with a stated risk reason)`);
|
|
||||||
}
|
|
||||||
if (!skipSpecs && present("specs") && deltaReqs.length === 0) warnings.push("no ADDED/MODIFIED requirements found in delta specs");
|
|
||||||
|
|
||||||
// 4. Test case identity and traceability.
|
|
||||||
const tcIds = new Set();
|
|
||||||
for (const f of walk(at("test-cases")).filter((x) => x.endsWith(".md"))) {
|
|
||||||
for (const block of read(f).split(/^###\s+(?=TC-)/m).slice(1)) {
|
|
||||||
const id = block.split(/\s/)[0];
|
|
||||||
if (tcIds.has(id)) errors.push(`${rel(f)}: duplicate test case id ${id}`);
|
|
||||||
tcIds.add(id);
|
|
||||||
if (!refs(block).length) errors.push(`${rel(f)}: test case ${id} has no requirement reference`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Contracts and diagrams are referenced, JSON parses, required task kinds exist.
|
|
||||||
const saText = walk(at("system-analysis")).filter((f) => f.endsWith(".md")).map(read).join("\n");
|
|
||||||
const contracts = walk(at("system-analysis/contracts"));
|
|
||||||
for (const f of [...contracts, ...walk(at("system-analysis/diagrams"))]) {
|
|
||||||
if (!saText.includes(path.basename(f))) errors.push(`${rel(f)}: not referenced from any system-analysis/*.md`);
|
|
||||||
if (f.endsWith(".json")) {
|
|
||||||
try { JSON.parse(read(f)); } catch (e) { errors.push(`${rel(f)}: invalid JSON (${e.message})`); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const taskLines = tasksText.split("\n").filter((l) => /^\s*[-*+]\s*\[.?\]/.test(l));
|
|
||||||
if (tasksText) {
|
|
||||||
if (!taskLines.some((l) => /Independent Review:/.test(l))) errors.push("tasks.md: no `Independent Review:` task");
|
|
||||||
if (contracts.length && !taskLines.some((l) => /Contract Parity:/.test(l))) errors.push("tasks.md: contracts exist but no `Contract Parity:` task");
|
|
||||||
if (profile.get("docs-impact") === false && !/^## Docs Impact/m.test(tasksText)) {
|
|
||||||
errors.push("tasks.md: docs-impact not selected, so a `## Docs Impact` section (changes or no-change reason) is required");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Gate Log: chronological; invalidating Gn withdraws Gn and every later gate.
|
|
||||||
const gates = new Map();
|
|
||||||
const order = [];
|
|
||||||
for (const [rawGate, , scope, validation, , decision] of tableRows(record, "Gate Log", 6) ?? []) {
|
|
||||||
const id = (strip(rawGate).match(/^G[1-4]\b/) || [])[0];
|
|
||||||
if (!id) continue;
|
|
||||||
if (/^invalidated\b/i.test(decision)) {
|
|
||||||
for (const g of [...gates.keys()]) if (g >= id) gates.delete(g);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!/^approved\b/i.test(decision)) continue;
|
|
||||||
if (!/"[^"]+"|«[^»]+»/.test(decision)) errors.push(`Gate ${id}: approval must quote the Change Owner decision`);
|
|
||||||
if (!validation) errors.push(`Gate ${id}: approval has no validation evidence`);
|
|
||||||
const prior = ["G1", "G2", "G3", "G4"].filter((g) => g < id && (g !== "G2" || hasG2));
|
|
||||||
if (prior.some((g) => !gates.has(g))) errors.push(`Gate ${id}: approved before ${prior.filter((g) => !gates.has(g)).join(", ")}`);
|
|
||||||
const ids = strip(scope).split(/[,\s]+/).filter(Boolean);
|
|
||||||
for (const a of ids) {
|
|
||||||
if (id !== "G4" && !allowedGates(a, complex, hasG2).includes(id)) errors.push(`Gate ${id}: '${a}' must be approved at ${allowedGates(a, complex, hasG2).join(" or ")}`);
|
|
||||||
}
|
|
||||||
gates.set(id, ids);
|
|
||||||
order.push(id);
|
|
||||||
}
|
|
||||||
if (gates.has("G1") && !gates.get("G1").includes("change-profile")) errors.push("Gate G1: scope must include change-profile");
|
|
||||||
|
|
||||||
// 7. Readiness.
|
|
||||||
const artifactPaths = [
|
|
||||||
["proposal", "proposal.md"], ["specs", "specs"], ["design", "design.md"],
|
|
||||||
["qa-analysis", "qa-analysis.md"], ["tasks", "tasks.md"],
|
|
||||||
...CONDITIONAL.map((id) => [id, PRIMARY[id]]),
|
|
||||||
];
|
|
||||||
const approvedScope = new Set([...gates.values()].flat());
|
|
||||||
const required = hasG2 ? ["G1", "G2", "G3"] : ["G1", "G3"];
|
|
||||||
const missingGates = required.filter((g) => !gates.has(g));
|
|
||||||
const unapproved = artifactPaths.filter(([id, p]) => !NOT_APPROVED.has(id) && present(p) && !approvedScope.has(id)).map(([id]) => id);
|
|
||||||
const blockers = [
|
|
||||||
...(tasksText ? [] : ["tasks.md missing"]),
|
|
||||||
...(missingGates.length ? [`missing gates: ${missingGates.join(", ")}`] : []),
|
|
||||||
...(unapproved.length ? [`not in any approved gate scope: ${unapproved.join(", ")}`] : []),
|
|
||||||
...(pending.length ? [`selected but missing: ${pending.join(", ")}`] : []),
|
|
||||||
];
|
|
||||||
notes.push(`apply-ready: ${blockers.length || errors.length ? `no (${[...blockers, ...(errors.length ? ["errors"] : [])].join("; ")})` : "yes"}`);
|
|
||||||
if (mode !== "plan") blockers.forEach((b) => errors.push(`apply blocker: ${b}`));
|
|
||||||
|
|
||||||
const sequence = ["proposal", "process-record", "research", "specs", "ux-design", "system-analysis",
|
|
||||||
...SA_MODULES, "security-analysis", "design", "qa-analysis", "test-cases", "docs-impact", "tasks"];
|
|
||||||
const pathOf = Object.fromEntries([...artifactPaths, ["process-record", "process-record.md"]]);
|
|
||||||
const next = sequence.find((id) => {
|
|
||||||
if (CONDITIONAL.includes(id) && !profile.get(id)) return false;
|
|
||||||
if (id === "specs" && skipSpecs) return false;
|
|
||||||
return !present(pathOf[id]);
|
|
||||||
});
|
|
||||||
notes.push(`next artifact: ${next ?? "none (planning complete for this profile)"}`);
|
|
||||||
|
|
||||||
// 8. Archive readiness. Native archive treats any non-[x] marker as open, so descoped work is
|
|
||||||
// removed from tasks.md by a gate decision and recorded in "Descoped Work".
|
|
||||||
if (mode === "archive") {
|
|
||||||
for (const l of taskLines) if (!/\[x\]/i.test(l)) errors.push(`tasks.md: open task: ${l.trim()}`);
|
|
||||||
for (const [task, reason, gate] of tableRows(record, "Descoped Work", 3) ?? []) {
|
|
||||||
if (task && (!reason || !/^G[1-4]$/.test(strip(gate)))) errors.push(`Descoped Work '${task}': needs reason and approving gate`);
|
|
||||||
}
|
|
||||||
if (!gates.has("G4")) errors.push("Gate G4 Delivery Acceptance is not approved");
|
|
||||||
if (labMode === "lab") {
|
|
||||||
const retro = record.split(/^## /m).find((s) => s.startsWith("Retrospective")) ?? "";
|
|
||||||
if (!retro.replace(/^Retrospective.*\n/, "").trim()) errors.push("process-record.md: Retrospective is empty (lab mode)");
|
|
||||||
if ((tableRows(record, "Lab Journal", 9) ?? []).length < gates.size) warnings.push("Lab Journal has fewer rows than approved gates");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
|
|
||||||
function finishIfMissing() {
|
|
||||||
if (fs.existsSync(at("process-record.md"))) return;
|
|
||||||
errors.push("process-record.md is missing: Change Profile and Gate Log are required");
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
function finish() {
|
|
||||||
for (const n of notes) console.log(`note: ${n}`);
|
|
||||||
for (const w of warnings) console.log(`warning: ${w}`);
|
|
||||||
for (const e of errors) console.log(`error: ${e}`);
|
|
||||||
console.log(errors.length ? `FAIL ${mode} ${changeName}: ${errors.length} error(s)` : `OK ${mode} ${changeName}`);
|
|
||||||
process.exit(errors.length ? 1 : 0);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,242 @@
|
|||||||
|
# Spec Writing Profile
|
||||||
|
|
||||||
|
Профиль написания и ревью требований `wealth-ledger-sdd-lab`. Цель — одинаковое
|
||||||
|
понимание поведения разными авторами, разработчиками и QA, а не дословно одинаковые
|
||||||
|
документы. Это адаптация инженерных практик, не заявление о соответствии стандартам.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Автор и reviewer читают профиль перед подготовкой или согласованием Specs. Правила
|
||||||
|
применяются к новым и изменяемым требованиям; весь canonical store ради стиля не
|
||||||
|
переписывается. Пути, delta-операции и gates задаёт `schema.yaml`.
|
||||||
|
|
||||||
|
Specs определяют наблюдаемое поведение. Обоснования и открытые решения — в Proposal,
|
||||||
|
межсистемные детали — в `analysis/`, механизмы — в design, способы доказательства —
|
||||||
|
в QA. Ни один downstream-артефакт не вводит новое поведение молча.
|
||||||
|
|
||||||
|
## Writing Rules
|
||||||
|
|
||||||
|
1. **Основание.** Каждое обязательство следует из согласованного намерения или
|
||||||
|
существующего контракта. Наблюдение в коде, гипотеза и пример — не решение
|
||||||
|
владельца. Неизвестное запиши в Open Decisions: вопрос, кто решает, зависимое
|
||||||
|
требование и что блокируется. Несогласованные числа и варианты не становятся нормой.
|
||||||
|
Для спорного или выведенного при анализе Requirement перед первым Scenario добавь
|
||||||
|
`**Источник (ненормативно):**` — ссылку на конкретное решение Change Owner в Issue
|
||||||
|
или пункт согласованной редакции контракта. Ссылка должна оставаться пригодной из
|
||||||
|
canonical spec после archive; относительной ссылки на активный Proposal недостаточно.
|
||||||
|
Источник объясняет происхождение, но не заменяет полную норму или approval;
|
||||||
|
обоснование остаётся в Proposal.
|
||||||
|
2. **Термины.** Используй названия из затронутых canonical specs и предоставленного
|
||||||
|
проектного глоссария. Один термин — одно значение в названном контексте. «Счёт»,
|
||||||
|
«брокерский счёт» и «учётная запись» не взаимозаменяемы. Новый или спорный термин
|
||||||
|
определи в теле владеющего им Requirement; остальные требования ссылаются на него.
|
||||||
|
Определение должно различать случаи, а не заменять слово синонимом. Общие термины
|
||||||
|
передаются в глоссарий через Docs Impact; новый файл словаря на каждый change не нужен.
|
||||||
|
3. **Атомарность.** Один Requirement — одно независимо принимаемое обязательство.
|
||||||
|
Если одну часть можно принять, а другую отклонить — раздели. Связанные условия,
|
||||||
|
сохранность данных при отказе и единый атомарный результат остаются вместе.
|
||||||
|
Число союзов или SHALL не определяет атомарность.
|
||||||
|
4. **Явная норма.** Назови субъект, условие применимости и наблюдаемый результат.
|
||||||
|
Для нового обязательства предпочитай SHALL / SHALL NOT; существующие MUST /
|
||||||
|
MUST NOT равнозначны и сохраняются. SHOULD / MAY не заменяют неопределённый исход:
|
||||||
|
допустимая вариативность и исключения должны быть согласованы и описаны.
|
||||||
|
«Корректно», «быстро», «при необходимости», «и/или», «и т. д.» без определения —
|
||||||
|
повод уточнить правило. Краткость не оправдывает потерю условий.
|
||||||
|
5. **Величины и границы.** Для расчёта задай входы, формулу, знак, валюту, точность
|
||||||
|
и момент округления, если они влияют на результат. Для периода — включительность
|
||||||
|
границ, дату/время учёта и часовой пояс; для порога — сравнение и точку отсчёта.
|
||||||
|
Различай ноль, отсутствие значения и неизвестный результат. Не добавляй допуск,
|
||||||
|
таймаут или округление без источника только ради измеримости.
|
||||||
|
6. **Свобода реализации.** Описывай «что», а не внутренние классы, библиотеки или
|
||||||
|
алгоритм. Если внутренний механизм можно заменить без изменения внешнего
|
||||||
|
обязательства, это design. Внешний формат или протокол допустим в Specs, когда
|
||||||
|
именно он является согласованным обязательством, а не удобством реализации.
|
||||||
|
|
||||||
|
## Requirement and Scenario
|
||||||
|
|
||||||
|
Порядок блока: `### Requirement:` с устойчивым именем → общая норма → нужные
|
||||||
|
определения/таблица → `#### Scenario:`. Для новых требований используй короткое имя
|
||||||
|
на русском; существующие headings сохраняй для native delta-сопоставления.
|
||||||
|
|
||||||
|
Выбирай форму по смыслу (адаптация EARS, не дополнительный синтаксис OpenSpec):
|
||||||
|
|
||||||
|
| Смысл | Каркас нормы |
|
||||||
|
| --- | --- |
|
||||||
|
| Постоянное свойство | Система SHALL … |
|
||||||
|
| Реакция на событие | При … система SHALL … |
|
||||||
|
| Зависимость от состояния | Пока … система SHALL … |
|
||||||
|
| Ошибка или исключение | Если … система SHALL … |
|
||||||
|
|
||||||
|
У ADDED/MODIFIED Requirement минимум один Scenario. GIVEN нужен, когда исходное
|
||||||
|
состояние влияет на результат; WHEN задаёт событие; THEN — наблюдаемый исход с
|
||||||
|
определённым ожидаемым значением или составом, не «работает правильно». AND продолжает
|
||||||
|
предыдущий тип шага: дополняет исходный контекст, событие или ожидаемый исход.
|
||||||
|
Шаги не описывают внутренние вызовы и селекторы UI.
|
||||||
|
|
||||||
|
Сначала общее правило, затем различающие примеры: основной путь, существенная
|
||||||
|
граница, иной исход, отказ, повтор или конкуренция — по применимости. Каждый сценарий
|
||||||
|
добавляет новый класс поведения. Не вводи правило только в Scenario и не переписывай
|
||||||
|
правило целиком для каждой комбинации данных. Сценарии показывают следствия нормы,
|
||||||
|
но несколько примеров не доказывают универсальный инвариант.
|
||||||
|
|
||||||
|
## Representation
|
||||||
|
|
||||||
|
| Когда прозы недостаточно | Представление и обязательные уточнения |
|
||||||
|
| --- | --- |
|
||||||
|
| Взаимодействуют несколько условий | Таблица решений: область входов, смысл «любое», политика совпадений (одна строка / первая / все), результат отсутствия совпадений или объяснение полноты |
|
||||||
|
| Результат зависит от истории | Переходы: исходное состояние, событие/условие, новое состояние, видимый эффект; существенные повторы и недопустимые переходы |
|
||||||
|
| Должно сохраняться свойство | Инвариант/формула с областью действия и единицами; примеры нуля, знака и округления по применимости |
|
||||||
|
|
||||||
|
Нормативная таблица находится внутри Requirement до первого Scenario и вводится
|
||||||
|
фразой с SHALL. Она — единственное определение правила, а не копия соседней прозы.
|
||||||
|
Неоднозначные пересечения строк — дефект, если явно не задан приоритет или сбор
|
||||||
|
результатов. Таблица безусловно не нужна каждому требованию. Внутренние состояния
|
||||||
|
реализации и межсистемные схемы остаются в analysis/design.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Учебные примеры формы, не продуктовые решения WealthLedger. В реальном change
|
||||||
|
периоды, состояния и исходы берутся из его согласованных входов.
|
||||||
|
|
||||||
|
### Condition and Boundary
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Отбор операций за календарный период
|
||||||
|
При формировании отчёта система SHALL включать все и только операции выбранного
|
||||||
|
счёта, дата учёта которых входит в выбранный период включительно по Europe/Moscow.
|
||||||
|
|
||||||
|
#### Scenario: Последний день периода и следующий день
|
||||||
|
- **GIVEN** выбран период 01.10.2026–31.10.2026 и счёт A
|
||||||
|
- **AND** на счёте A есть операции X с датой учёта 31.10.2026 и Y с датой 01.11.2026
|
||||||
|
- **WHEN** формируется отчёт
|
||||||
|
- **THEN** отчёт включает X и не включает Y
|
||||||
|
```
|
||||||
|
|
||||||
|
Плохо: «Система SHALL корректно отбирать операции до конца месяца» — не определены
|
||||||
|
месяц, границы, дата отбора и счёт. «THEN список корректен» не позволяет проверить исход.
|
||||||
|
|
||||||
|
### Decision Table
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Различимость неполного и пустого итога
|
||||||
|
Система SHALL представлять итог расчёта по таблице. «Полнота» означает получение
|
||||||
|
всех данных выбранной выборки; «есть записи» — наличие хотя бы одной записи.
|
||||||
|
Оба условия логические. Совпадает ровно одна строка; «любое» означает оба значения.
|
||||||
|
Покрыты все комбинации.
|
||||||
|
|
||||||
|
| Полнота подтверждена | Есть записи | Видимый итог |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Нет | Любое | Недоступен, без числового итога |
|
||||||
|
| Да | Нет | Ноль и пояснение пустого результата |
|
||||||
|
| Да | Да | Рассчитанный итог |
|
||||||
|
|
||||||
|
#### Scenario: Известная часть не становится окончательной суммой
|
||||||
|
- **GIVEN** известна одна запись на 100 RUB, но полнота выборки не подтверждена
|
||||||
|
- **WHEN** пользователь просматривает результат
|
||||||
|
- **THEN** итог обозначен как недоступный, не показан ни как 100 RUB, ни как ноль
|
||||||
|
```
|
||||||
|
|
||||||
|
Плохо: строки «нет данных → 0» и «данные не полны → недоступно» без определения
|
||||||
|
«нет данных» — одному входу могут соответствовать два разных результата.
|
||||||
|
|
||||||
|
### State Transition
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: Отмена формирования отчёта
|
||||||
|
При запросе отмены система SHALL изменять состояние формирования отчёта по таблице.
|
||||||
|
Область правила — состояния «Ожидает», «Отменён», «Готов»; начальное — «Ожидает».
|
||||||
|
Совпадает ровно одна строка. Таблица определяет только событие отмены.
|
||||||
|
|
||||||
|
| До | После | Результат отмены |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Ожидает | Отменён | Отмена подтверждена, отчёт не будет сформирован |
|
||||||
|
| Отменён | Отменён | Отмена подтверждена без повторного эффекта |
|
||||||
|
| Готов | Готов | Отмена отклонена, готовый отчёт сохранён |
|
||||||
|
|
||||||
|
#### Scenario: Повтор отмены
|
||||||
|
- **GIVEN** формирование отчёта уже отменено
|
||||||
|
- **WHEN** пользователь снова запрашивает отмену
|
||||||
|
- **THEN** отмена подтверждена, состояние остаётся «Отменён»
|
||||||
|
```
|
||||||
|
|
||||||
|
Плохо: «повтор ничего не делает» — не определён результат для пользователя.
|
||||||
|
|
||||||
|
## Review
|
||||||
|
|
||||||
|
Используй два прохода на существующем gate Specs (G2 или G3), не новый gate.
|
||||||
|
Автор выполняет самопроверку; назначенный по режиму reviewer перепроверяет независимо.
|
||||||
|
|
||||||
|
| Проход | Что проверить |
|
||||||
|
| --- | --- |
|
||||||
|
| Нормоконтроль | Native headings/операции; термины и единицы; явные субъект/условие/исход; атомарность; корректные ссылки; Scenario с проверяемым THEN |
|
||||||
|
| Смысл и набор | Необходимость и происхождение норм (у спорных/выведенных — источник действительно подтверждает норму, ссылка пригодна после archive); основной путь, альтернативы, отказы и границы; повторы/конкуренция/сохранность по риску; непротиворечивость таблиц и итогового baseline; отсутствие выдуманных решений |
|
||||||
|
|
||||||
|
Проверь не только delta, но и получающийся набор вместе с canonical specs:
|
||||||
|
сохранились ли действующие ограничения, исключения, инварианты и сценарии? MODIFIED
|
||||||
|
заменяет полный блок; редакторское улучшение не разрешает удалить прежний сценарий
|
||||||
|
или незаметно переименовать Requirement.
|
||||||
|
|
||||||
|
Профиль не отменяет ограничений установленного CLI. При предупреждении о длине
|
||||||
|
сократи повторения или раздели действительно независимые обязательства; не убирай
|
||||||
|
условия и не прячь норму в комментарий/код ради успешного `--strict`.
|
||||||
|
|
||||||
|
Контрольный вопрос: «Назови существенную ошибочную реализацию и наблюдение, которым
|
||||||
|
это требование отличит её от соответствующей». Если ответ зависит от догадки,
|
||||||
|
укажи точную неопределённость. Это эвристика ревью, не доказательство полноты.
|
||||||
|
|
||||||
|
Finding: **файл/heading + цитата → нарушенное правило → два возможных толкования
|
||||||
|
или недостающий исход → вопрос владельцу/адресат исправления**. Reviewer возвращает
|
||||||
|
находки, не переписывает текст по вкусу. Пример: «до конца месяца» → неясна правая
|
||||||
|
граница → операция в последний день включается или исключается → уточнить в Specs.
|
||||||
|
Нерешённый вопрос, меняющий требование или ожидаемый результат, блокирует его gate;
|
||||||
|
косметическое предпочтение не блокирует. Validation и Findings — в существующем
|
||||||
|
Gate Log, без нового отчёта на каждый Requirement.
|
||||||
|
|
||||||
|
### Trial
|
||||||
|
|
||||||
|
Эффект профиля проверяется сравнительным пилотом; до результатов он не подтверждён.
|
||||||
|
|
||||||
|
1. До запуска зафиксируй четыре одинаковых для обеих версий задания: простое правило,
|
||||||
|
ветвление, повтор/состояние и MODIFIED с исходной canonical spec. Для оценки отдельно
|
||||||
|
запиши согласованные исходы, намеренно оставленные неизвестные и контрольные вопросы.
|
||||||
|
2. Сравни прежнюю и изменённую инструкции минимум на двух авторах/моделях, каждый
|
||||||
|
выполняет обе версии. Сохрани входы, prompts, версии инструкций и моделей, доступные
|
||||||
|
настройки и результаты. Для LLM используй отдельные сессии без ответов другой версии;
|
||||||
|
для людей чередуй порядок версий, чтобы уменьшить эффект обучения.
|
||||||
|
3. Независимые читатели отвечают на одинаковые контрольные вопросы по полученным
|
||||||
|
спецификациям, не зная автора и версии инструкции. Ответы для оценки из шага 1
|
||||||
|
раскрываются после чтения; согласие читателей само по себе не означает правильность.
|
||||||
|
4. По каждому заданию сравни расхождения трактовок, ошибки относительно согласованных
|
||||||
|
исходов, пропущенные случаи, выдуманные нормы и время уточнения/ревью. Сохрани
|
||||||
|
конкретные примеры ошибок вместе с результатами в Issue пилота.
|
||||||
|
5. Критерий улучшения согласуй до запуска: меньше смысловых расхождений без роста
|
||||||
|
ошибок, пропусков и выдуманных норм; допустимое изменение времени определяет владелец.
|
||||||
|
Если различий нет или данных недостаточно, эффект не подтверждён. Вывод ограничен
|
||||||
|
проверенными заданиями и авторами/моделями. Совпадение формулировок и успешный CLI
|
||||||
|
не считаются доказательством одинакового понимания.
|
||||||
|
|
||||||
|
## Sources and Adaptation
|
||||||
|
|
||||||
|
Из девяти исследований `sdd-spec-quality/*.md` и трёх сводок `result/*.pdf` выбраны
|
||||||
|
правила с понятной пользой для ручного Markdown-процесса. Первоисточники проверены
|
||||||
|
30.10.2026; критерии адаптированы, а не перенесены как обязательная сертификация.
|
||||||
|
|
||||||
|
| Источник | Что взято и где предел |
|
||||||
|
| --- | --- |
|
||||||
|
| [NASA SE Handbook, Appendix C, C.2–C.4](https://www.nasa.gov/reference/appendix-c-how-to-write-a-good-requirement/) | Необходимость, одна мысль, «что, а не как», проверяемость и согласованность; не сертификационный комплект документов |
|
||||||
|
| [INCOSE GtWR v4 Summary, с. 2–3](https://www.incose.org/wp-content/uploads/legacy/working-groups/requirements-wg/guidetowritingrequirements/incose_rwg_gtwr_v4_summary_sheet.pdf) | C1/C3/C5/C7 для отдельного требования, C10–C14 для набора; R4/R7/R33–R36 — термины, величины, время. Полный платный Guide не заявлен как проверенный |
|
||||||
|
| [EARS, автор методики](https://alistairmavin.com/ears/) | Каркасы условия/события/состояния/ошибки; русский язык — адаптация, не буквальная английская грамматика |
|
||||||
|
| [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html), [8174 §2](https://www.rfc-editor.org/rfc/rfc8174.html#section-2) | Значения uppercase SHALL/MUST и запретов; наличие ключевого слова само по себе не гарантирует качество |
|
||||||
|
| [Cucumber, Given/When/Then](https://cucumber.io/docs/gherkin/reference/) | Исходное состояние, событие, наблюдаемый исход; Markdown Scenario не становится исполняемым Gherkin-тестом |
|
||||||
|
|
||||||
|
Из предложений не приняты: жёсткие лимиты слов/сценариев, подсчёт SHALL вместо
|
||||||
|
атомарности, обязательные REQ-ID и скрытые атрибуты, JSON-first, новые линтеры и gates.
|
||||||
|
Из нормоконтроля конструкторской документации заимствовано разделение проверки формы
|
||||||
|
и смысла; соответствие ГОСТ, DO-178C или ISO/IEC/IEEE 29148 не заявляется. Таблицы и
|
||||||
|
инварианты используются по риску; обязательная формальная модель каждому change не нужна.
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
# Data Mapping
|
# Data Mapping
|
||||||
|
|
||||||
<!-- guide: Field-level transformation between systems. Large mappings go to a
|
<!-- Преобразование данных между системами по полям. Большой mapping — машиночитаемый
|
||||||
machine-checkable file in contracts/ and are referenced here. Remove guides. -->
|
файл в contracts/ со ссылкой отсюда. -->
|
||||||
|
|
||||||
## Source and Target
|
## Source and Target
|
||||||
|
|
||||||
| Mapping | Source (owner) | Target (owner) | Direction | Trigger |
|
| Mapping | Источник (владелец) | Цель (владелец) | Направление | Trigger |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Field Mapping
|
## Field Mapping
|
||||||
|
|
||||||
| Target field | Source field(s) | Transformation | Unit / currency / scale | Nullability | Default | Validation | On failure |
|
| Поле цели | Поля источника | Преобразование | Единица / валюта / scale | Nullability | Default | Валидация | При ошибке |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Invariants
|
## Invariants
|
||||||
|
|
||||||
<!-- guide: Conservation rules that must hold after mapping, e.g. "total = sum of
|
<!-- Правила сохранения после mapping, например «итог = сумма позиций + кэш; каждая
|
||||||
positions + cash; each money unit counted once", sign conventions, rounding
|
денежная единица учтена один раз», знаки, точка округления. Каждый инвариант
|
||||||
point. Each invariant references the spec requirement it protects. -->
|
ссылается на защищаемый Requirement. -->
|
||||||
|
|
||||||
## Double-counting and Classification Risks
|
## Double-counting and Classification Risks
|
||||||
|
|
||||||
<!-- guide: Every source record type that could fall into two target aggregates,
|
<!-- Каждый тип исходной записи, который может попасть в два агрегата, и правило,
|
||||||
and the rule that assigns it to exactly one. -->
|
относящее его ровно к одному. -->
|
||||||
|
|
||||||
## Transformation Errors
|
## Transformation Errors
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
# Engineering Design
|
# Engineering Design
|
||||||
|
|
||||||
<!-- guide: How affected repositories implement approved specs and system
|
<!-- Как affected repositories реализуют утверждённые specs и системные обязательства.
|
||||||
obligations. Reference upstream ids; do not restate them. Remove guides. -->
|
Ссылайся на upstream ids, не пересказывай. -->
|
||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
<!-- guide: Exact upstream artifacts this design implements (specs, selected
|
<!-- Точные upstream-артефакты, которые реализует этот design (specs, выбранные
|
||||||
system-analysis modules, ux-design, security-analysis). -->
|
модули analysis/, ux-design, security-analysis). -->
|
||||||
|
|
||||||
## Architecture Drivers
|
## Architecture Drivers
|
||||||
|
|
||||||
<!-- guide: Requirements, NFR ids, constraints and risks that force decisions. -->
|
<!-- Requirements, ids NFR, ограничения и риски, которые вынуждают решения. -->
|
||||||
|
|
||||||
## Technical Goals / Non-Goals
|
## Technical Goals / Non-Goals
|
||||||
|
|
||||||
## Implementation Impact Map
|
## Implementation Impact Map
|
||||||
|
|
||||||
| Area | Affected | Outcome or N/A reason |
|
| Область | Затронута | Результат или причина N/A |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| main | | |
|
| main | | |
|
||||||
| frontend | | |
|
| frontend | | |
|
||||||
@@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
## Decisions
|
## Decisions
|
||||||
|
|
||||||
<!-- guide: Per decision: context, chosen option, rejected alternatives, driver it
|
<!-- На каждое решение: контекст, выбранный вариант, отвергнутые альтернативы, driver.
|
||||||
satisfies. Group by repository when that helps. -->
|
Группируй по repository, если это помогает. -->
|
||||||
|
|
||||||
## Components and Responsibilities
|
## Components and Responsibilities
|
||||||
|
|
||||||
@@ -33,16 +33,16 @@
|
|||||||
|
|
||||||
## Contract Implementation
|
## Contract Implementation
|
||||||
|
|
||||||
<!-- guide: How each contract is produced/consumed and how published OpenAPI is
|
<!-- Как каждый контракт производится/потребляется и как опубликованный OpenAPI
|
||||||
compared with the target contract. N/A when no contracts changed. -->
|
сравнивается с target-контрактом. N/A, если контракты не менялись. -->
|
||||||
|
|
||||||
## Cross-cutting Implementation
|
## Cross-cutting Implementation
|
||||||
|
|
||||||
## UI Implementation
|
## UI Implementation
|
||||||
|
|
||||||
<!-- guide: With ux-design.md: component composition, state, data fetching, how
|
<!-- С ux-design: композиция компонентов, state, data fetching, как получается каждое
|
||||||
each UI state is produced — no restated UX. Without ux-design.md: state
|
UI-состояние — без пересказа UX. Без ux-design: new/changed/reuse или N/A с
|
||||||
new/changed, reuse or N/A with reason, plus the needed screen/state detail. -->
|
причиной и нужные детали экранов/состояний. -->
|
||||||
|
|
||||||
## Developer Verification Strategy
|
## Developer Verification Strategy
|
||||||
|
|
||||||
@@ -50,8 +50,8 @@
|
|||||||
|
|
||||||
## Solution Assessment
|
## Solution Assessment
|
||||||
|
|
||||||
<!-- guide: Required for new auth, security, storage, integration, observability,
|
<!-- Обязателен для нового auth, security, storage, интеграции, observability,
|
||||||
caching, concurrency or shared tooling; otherwise N/A with reason. -->
|
кеширования, конкурентности или общего tooling; иначе N/A с причиной. -->
|
||||||
|
|
||||||
## Risks / Trade-offs
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Documentation Impact
|
# Documentation Impact
|
||||||
|
|
||||||
<!-- guide: Plan only; docs/ is edited by [docs] tasks. Remove guides. -->
|
<!-- Только план; docs/ правят tasks `[docs]`. -->
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
## Documentation Changes
|
## Documentation Changes
|
||||||
|
|
||||||
| Action | Target | Category | Resulting AS-IS | Source | Verification |
|
| Action | Target | Category | Итоговое AS-IS | Source | Verification |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Diagram Promotion
|
## Diagram Promotion
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
# Failure Model
|
# Failure Model
|
||||||
|
|
||||||
<!-- guide: Boundary-level failure semantics. Implementation of mechanisms (queues,
|
<!-- Семантика отказов на границах. Реализация механизмов (очереди, locks,
|
||||||
locks, libraries) belongs to design.md. Remove guides before G3. -->
|
библиотеки) — в design.md. -->
|
||||||
|
|
||||||
## Failure Scenarios
|
## Failure Scenarios
|
||||||
|
|
||||||
| ID | Boundary / step | Failure | Detection | Required system behavior | Observable result | Requirement |
|
| ID | Граница / шаг | Отказ | Обнаружение | Требуемое поведение системы | Наблюдаемый результат | Requirement |
|
||||||
| --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Timeouts and Retries
|
## Timeouts and Retries
|
||||||
|
|
||||||
## Idempotency and Duplicate Handling
|
## Idempotency and Duplicate Handling
|
||||||
|
|
||||||
<!-- guide: Idempotency key, scope, retention; what a duplicate request/event
|
<!-- Ключ идемпотентности, scope, срок хранения; что даёт повторный запрос/событие. -->
|
||||||
yields. -->
|
|
||||||
|
|
||||||
## Ordering and Concurrency
|
## Ordering and Concurrency
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
# Non-Functional Requirements
|
# Non-Functional Requirements
|
||||||
|
|
||||||
<!-- guide: Only measurable constraints that change design or verification.
|
<!-- Только измеримые ограничения, которые меняют design или проверку. У каждой
|
||||||
Each row needs a threshold and a way to measure it. Remove guides. -->
|
строки есть согласованный порог с единицами, условия/нагрузка и способ измерения.
|
||||||
|
Для времени укажи точки начала/конца, для статистики — окно и агрегат.
|
||||||
|
Не превращай пример числа из исследования в утверждённую цель. -->
|
||||||
|
|
||||||
| ID | Category | Requirement | Threshold | Measurement / evidence | Rationale / source |
|
| ID | Категория | Требование | Порог и единицы | Условия измерения | Измерение / evidence | Обоснование / источник |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Constraints Without Thresholds
|
## Constraints Without Thresholds
|
||||||
|
|
||||||
<!-- guide: Constraints that are real but not measurable yet; state why and who
|
<!-- Реальные, но пока неизмеримые ограничения: почему нет порога, кто решит и какое
|
||||||
decides the threshold. -->
|
решение/проверку это блокирует. Не выдавай неизвестное за согласованный NFR. -->
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
# Observability
|
# Observability
|
||||||
|
|
||||||
<!-- guide: What must be observable and why; not which SDK, exporter or logger
|
<!-- Что должно быть наблюдаемо и зачем; не какой SDK, exporter или logger (design.md). -->
|
||||||
(design.md). Remove guides before G3. -->
|
|
||||||
|
|
||||||
## Semantic Events
|
## Semantic Events
|
||||||
|
|
||||||
| Event | Trigger | Required attributes | Consumer / purpose |
|
| Событие | Trigger | Обязательные атрибуты | Потребитель / назначение |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
|
||||||
## Logs, Metrics and Traces
|
## Logs, Metrics and Traces
|
||||||
@@ -14,11 +13,11 @@
|
|||||||
|
|
||||||
## SLI and Alert Conditions
|
## SLI and Alert Conditions
|
||||||
|
|
||||||
| SLI | Definition | Target | Alert condition |
|
| SLI | Определение | Цель | Условие алерта |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
|
||||||
## Diagnostic Scenarios
|
## Diagnostic Scenarios
|
||||||
|
|
||||||
<!-- guide: "When X happens, an operator can determine Y from Z." -->
|
<!-- «Когда X, оператор определяет Y по Z». -->
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
# Process Record
|
# Process Record
|
||||||
|
|
||||||
**Mode:** <!-- guide: lab | delivery -->
|
**Mode:** <!-- lab | delivery -->
|
||||||
**Class:** <!-- guide: standard | complex -->
|
**Class:** <!-- standard | complex -->
|
||||||
**Issue:** <!-- guide: Gitea Issue link -->
|
**Issue:** <!-- ссылка на Gitea Issue -->
|
||||||
|
|
||||||
## Change Profile
|
## Change Profile
|
||||||
|
|
||||||
<!-- guide: One row per conditional artifact, all ten rows required. Selected =
|
<!-- Строка на каждый условный артефакт, все десять обязательны. Выбран = yes|no.
|
||||||
yes|no. Reason cites the concrete trigger from the instruction table or why
|
Причина называет конкретный trigger из instruction артефакта или объясняет, почему
|
||||||
it does not apply; "lab practice" is a valid reason only in lab mode and
|
он не применим; «lab: практика …» допустима только в lab и называет практику.
|
||||||
must name the practice. -->
|
Пример:
|
||||||
|
| failure-model | yes | синхронизация брокера повторяет неидемпотентную запись |
|
||||||
|
| nfr | no | нет новых обязательств по задержке, объёму или хранению |
|
||||||
|
| data-mapping | yes | lab: практика data mapping |
|
||||||
|
Плохо: `| nfr | no | N/A |` — причина должна объяснять, почему не нужно. -->
|
||||||
|
|
||||||
| Artifact | Selected | Reason |
|
| Артефакт | Выбран | Причина |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| research | | |
|
| research | | |
|
||||||
| ux-design | | |
|
| ux-design | | |
|
||||||
@@ -26,32 +30,40 @@
|
|||||||
|
|
||||||
## Gate Log
|
## Gate Log
|
||||||
|
|
||||||
<!-- guide: Append-only, chronological. Scope = artifact ids covered by the
|
<!-- Только дописывание, по хронологии. Scope — ids артефактов в решении.
|
||||||
decision. Validation = commands run and their results, review verdicts.
|
Validation — выполненные команды и результаты, вердикты review.
|
||||||
Decision = `approved "<quoted owner words>"`, `rejected`, `changes requested`
|
Decision — `approved "<дословные слова владельца>"`, `rejected`,
|
||||||
or `invalidated: <reason>`. Never write a decision the owner did not give. -->
|
`changes requested` или `invalidated: <причина>`. Никогда не записывай решение,
|
||||||
|
которого владелец не давал. ПОСЛЕДНЯЯ строка gate — его состояние; более поздняя
|
||||||
|
rejected / changes requested / invalidated снимает этот gate и все последующие.
|
||||||
|
Для Specs: Validation отмечает нормоконтроль и смысловую проверку набора по
|
||||||
|
spec-writing.md; Findings — конкретные неясности/пропуски и их разрешение.
|
||||||
|
Пример:
|
||||||
|
| G1 | 2026-10-29 | proposal, change-profile | `openspec validate` OK; readiness: plan OK | product-analyst: 1 major — закрыт в Open Decisions | approved "ок, идём дальше" (сессия) |
|
||||||
|
| G2 | 2026-10-30 | specs | `openspec validate --strict` OK; readiness: plan OK | qa-engineer: в Scenario нет значения THEN | changes requested |
|
||||||
|
Плохо: `approved` без цитаты владельца; строка G4 со Scope `design`. -->
|
||||||
|
|
||||||
| Gate | Date | Scope | Validation | Findings | Decision |
|
| Gate | Дата | Scope | Validation | Findings | Decision |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Descoped Work
|
## Descoped Work
|
||||||
|
|
||||||
<!-- guide: Tasks removed from tasks.md after G3. Reason and approving gate are
|
<!-- Tasks, удалённые из tasks.md после G3. Причина и gate решения обязательны.
|
||||||
required. Leave the header row only when nothing was descoped. -->
|
Если ничего не снято — только строка заголовка. -->
|
||||||
|
|
||||||
| Task | Reason | Gate |
|
| Task | Причина | Gate |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
|
||||||
## Lab Journal
|
## Lab Journal
|
||||||
|
|
||||||
<!-- guide: lab mode: one row per completed stage, written right after it.
|
<!-- lab: строка на каждую завершённую стадию, сразу после неё. Уникальные находки —
|
||||||
Unique findings = what this stage found that no other stage did.
|
то, что нашла только эта стадия. Стоимость — вызовы сабагентов, примерно токены
|
||||||
Cost = subagent calls, approximate tokens or time. delivery mode: optional. -->
|
или время. delivery: необязательно. -->
|
||||||
|
|
||||||
| Stage | Role / subagent | Unique findings | Decisions | Risks found / excluded | Handoff issues | Skills | Cost | Transferable |
|
| Стадия | Роль / сабагент | Уникальные находки | Решения | Риски найдены / исключены | Проблемы передачи | Skills | Стоимость | Переносимо |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Retrospective
|
## Retrospective
|
||||||
|
|
||||||
<!-- guide: Written after G4. Which stages paid off, which were redundant for this
|
<!-- После G4. Какие стадии окупились, какие были лишними для этого класса change,
|
||||||
class of change, what to change in the schema. Facts first, then opinion. -->
|
что поменять в schema. Сначала факты, потом мнение. -->
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# Proposal
|
# Proposal
|
||||||
|
|
||||||
<!-- guide: Language of the Change Owner. No files, APIs, tables, libraries,
|
<!-- Язык Change Owner. Без файлов, API, таблиц, библиотек, диаграмм и тест-планов. -->
|
||||||
diagrams or test plans. Remove every guide comment before G1. -->
|
|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
@@ -23,14 +22,18 @@
|
|||||||
|
|
||||||
## Success Criteria
|
## Success Criteria
|
||||||
|
|
||||||
|
<!-- Каждый критерий измерим или наблюдаем: что увидит пользователь или владелец. -->
|
||||||
|
|
||||||
## Evidence
|
## Evidence
|
||||||
|
|
||||||
<!-- guide: Facts that justify the change, each with a source: Issue, research
|
<!-- Факты, обосновывающие change, каждый с источником: Issue, research (ссылка или
|
||||||
(link or `research.md`), incident, measurement. Mark hypotheses as such. -->
|
`analysis/research.md`), инцидент, измерение. Гипотезы помечай явно. -->
|
||||||
|
|
||||||
## Assumptions / Dependencies
|
## Assumptions / Dependencies
|
||||||
|
|
||||||
## Open Decisions
|
## Open Decisions
|
||||||
|
|
||||||
<!-- guide: Decisions not yet taken that may change scope. Each item: question,
|
<!-- Ещё не принятые решения, способные изменить scope. Каждое: вопрос, кто решает,
|
||||||
who decides, what blocks until it is answered. Empty only if none exist. -->
|
зависимое требование/артефакт и что блокируется до ответа. Несогласованное
|
||||||
|
правило, термин, порог или вариант поведения не заменяй допущением в Specs.
|
||||||
|
Пусто, только если таких вопросов нет. -->
|
||||||
|
|||||||
@@ -1,24 +1,34 @@
|
|||||||
# QA Analysis
|
# QA Analysis
|
||||||
|
|
||||||
<!-- guide: Risk-based system verification strategy. Reference scenarios; do not
|
<!-- Risk-based стратегия системной проверки. Ссылайся на Scenarios, не пересказывай.
|
||||||
restate them. No developer unit/component test plans. Remove guides. -->
|
Без планов developer unit/component tests. -->
|
||||||
|
|
||||||
## Risk Assessment
|
## Risk Assessment
|
||||||
|
|
||||||
| Risk | Affected requirements | Likelihood | Impact | Verification depth |
|
| Риск | Затронутые требования | Вероятность | Ущерб | Глубина проверки |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Coverage Matrix
|
## Coverage Matrix
|
||||||
|
|
||||||
<!-- guide: One row per delta requirement. Check = system-level way of proving
|
<!-- Строка на каждый ADDED/MODIFIED/REMOVED Requirement. Назови покрытые Scenarios
|
||||||
it; test case ids when test-cases is selected; or "not verified: <risk
|
точными headings и ветви таблицы/переходы, если они есть. Проверка даёт наблюдение,
|
||||||
reason>". -->
|
отличающее исполнение нормы от нарушения; test cases — ссылками, не копиями.
|
||||||
|
Пропуск случая — «not verified: <риск-причина>» с названием случая.
|
||||||
|
Покрытие — только строки этой таблицы; упоминание требования в другом месте
|
||||||
|
файла покрытием не считается. REMOVED требует проверки миграции/регрессии.
|
||||||
|
При skip_specs: true вместо дельт укажи существующее поведение/ограничение,
|
||||||
|
которое сохраняем, точный источник и регрессионную проверку.
|
||||||
|
Пример:
|
||||||
|
| `example-report` — `Отбор операций за календарный период` | Последний день периода и следующий день | Отчёт за октябрь включает X за 31.10 и исключает Y за 01.11 | API | ссылка на Scenario | -->
|
||||||
|
|
||||||
| Requirement | Check | Level | Test cases |
|
| Requirement / регрессионный baseline | Сценарии / ветви правила | Проверка / ожидаемое наблюдение | Уровень | Test cases |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Negative and Boundary Focus
|
## Negative and Boundary Focus
|
||||||
|
|
||||||
|
<!-- Границы до/на/после порога, отказ, повтор, конкуренция, сохранность данных —
|
||||||
|
только применимое. Ссылайся на нормы, не придумывай исходы. -->
|
||||||
|
|
||||||
## Cross-system Verification
|
## Cross-system Verification
|
||||||
|
|
||||||
## Test Data and Environment Needs
|
## Test Data and Environment Needs
|
||||||
@@ -31,4 +41,5 @@
|
|||||||
|
|
||||||
## Gaps / Blockers
|
## Gaps / Blockers
|
||||||
|
|
||||||
<!-- guide: Each gap names its routing destination artifact. -->
|
<!-- Цитата/heading → неоднозначность или непокрытый исход → владелец решения и
|
||||||
|
upstream-артефакт. Вопрос о значении expected result блокирует согласование. -->
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Product Research
|
# Product Research
|
||||||
|
|
||||||
<!-- guide: Input context, not a requirement source. Every claim is marked
|
<!-- Входной контекст, не источник требований. Каждое утверждение помечено:
|
||||||
[F] fact with source, [I] interpretation or [H] hypothesis. Remove guides. -->
|
[F] факт с источником, [I] интерпретация, [H] гипотеза. -->
|
||||||
|
|
||||||
## Problem Statement
|
## Problem Statement
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
## Existing Solutions and Alternatives
|
## Existing Solutions and Alternatives
|
||||||
|
|
||||||
| Option | What it does | Strengths | Weaknesses | Source |
|
| Вариант | Что делает | Сильные стороны | Слабые стороны | Источник |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Evidence & Sources
|
## Evidence & Sources
|
||||||
@@ -24,5 +24,5 @@
|
|||||||
|
|
||||||
## Recommendation
|
## Recommendation
|
||||||
|
|
||||||
<!-- guide: build / do not build / narrow scope / research more, with reasons.
|
<!-- делать / не делать / сузить scope / исследовать дальше — с причинами.
|
||||||
"Do not build" is a valid outcome. -->
|
«Не делать» — допустимый итог. -->
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Security Analysis
|
# Security Analysis
|
||||||
|
|
||||||
<!-- guide: Threat model of this change only. Reference security NFR rows by id;
|
<!-- Threat model только этого change. На security-NFR ссылайся по id, не
|
||||||
do not restate them. No generic checklist. Remove guides before G3. -->
|
пересказывай. Без универсального чек-листа. -->
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
@@ -11,16 +11,16 @@
|
|||||||
|
|
||||||
## Threats
|
## Threats
|
||||||
|
|
||||||
| ID | Threat (STRIDE) | Attack scenario | Asset / boundary | Existing control | Likelihood / impact |
|
| ID | Угроза (STRIDE) | Сценарий атаки | Актив / граница | Существующий контроль | Вероятность / ущерб |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Recommended Mitigations
|
## Recommended Mitigations
|
||||||
|
|
||||||
| Threat | Mitigation | Owner artifact (specs / system-analysis / design) |
|
| Угроза | Mitigation | Артефакт-владелец (specs / system-analysis / design) |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
|
||||||
## Residual Risks
|
## Residual Risks
|
||||||
|
|
||||||
## Validation Requirements
|
## Validation Requirements
|
||||||
|
|
||||||
<!-- guide: Evidence needed before G4: review focus, negative tests, tooling. -->
|
<!-- Evidence, нужные до G4: фокус review, негативные тесты, инструменты. -->
|
||||||
|
|||||||
@@ -1,18 +1,48 @@
|
|||||||
<!--
|
<!--
|
||||||
Choose only the sections this delta needs; delete this guide before approval.
|
Перед заполнением прочитай единый профиль:
|
||||||
|
openspec/schemas/wealth-ledger-sdd-lab/spec-writing.md.
|
||||||
|
Оставь только нужные этой дельте разделы; подсказки и образец не входят в результат.
|
||||||
|
Файл — specs/<capability-path>/spec.md с точным путём из Proposal.
|
||||||
|
|
||||||
New capability only: add `## Purpose` with 1–2 sentences (50+ characters).
|
Только новая capability: `## Purpose` в 1–2 предложения (от 50 символов).
|
||||||
Existing capability: do not add Purpose.
|
Существующая capability: Purpose не добавляй.
|
||||||
|
|
||||||
Supported operations:
|
Операции:
|
||||||
- `## ADDED Requirements`: new complete Requirement blocks.
|
- `## ADDED Requirements` — новые полные блоки Requirement.
|
||||||
- `## MODIFIED Requirements`: full existing Requirement blocks with every
|
- `## MODIFIED Requirements` — полный существующий блок со всеми сохраняемыми
|
||||||
surviving Scenario, then edited behavior; heading matches exactly.
|
Scenario, затем изменённое поведение; heading совпадает точно.
|
||||||
- `## REMOVED Requirements`: Requirement heading plus **Reason** and **Migration**.
|
- `## REMOVED Requirements` — heading Requirement плюс **Reason** и **Migration**.
|
||||||
- `## RENAMED Requirements`: `FROM:` and `TO:` heading pairs only.
|
- `## RENAMED Requirements` — только пары `FROM:` и `TO:`.
|
||||||
|
|
||||||
Every normative Requirement uses BCP 14 (RFC 2119 + RFC 8174) uppercase
|
Каркас ADDED/MODIFIED: устойчивое имя → субъект SHALL <наблюдаемый результат>
|
||||||
SHALL/MUST deliberately, and at least one `#### Scenario:` with `- **WHEN**`
|
с явным условием, если оно нужно → определения/таблица → Scenarios.
|
||||||
and `- **THEN**`. Lowercase must/should/may are ordinary language.
|
SHALL NOT — запрет; существующие MUST/MUST NOT равнозначны. Один Requirement —
|
||||||
Do not leave empty operation sections or a sample Requirement.
|
одно независимо принимаемое обязательство, не подсчёт предложений или SHALL.
|
||||||
|
Сценарий: GIVEN — существенное исходное состояние, WHEN — событие,
|
||||||
|
THEN — конкретный наблюдаемый исход. Минимум один Scenario; границы/отказы/повторы
|
||||||
|
добавляй по смыслу. Новое правило в одном лишь сценарии недопустимо.
|
||||||
|
AND дополняет предыдущий тип шага GIVEN/WHEN/THEN.
|
||||||
|
Термины определены, величины имеют единицы и границы. Неизвестное — вопрос в
|
||||||
|
Open Decisions с владельцем и зависимым требованием, не придуманный ответ.
|
||||||
|
|
||||||
|
Для спорного/выведенного Requirement укажи источник перед первым Scenario по профилю.
|
||||||
|
Пример формата (подставь реальное согласованное решение):
|
||||||
|
**Источник (ненормативно):** [решение владельца](<URL-комментария-Issue>), пункт «Границы периода».
|
||||||
|
Ссылка остаётся пригодной после archive; норму полностью сформулируй в Requirement.
|
||||||
|
|
||||||
|
Учебный образец формы, не продуктовая политика:
|
||||||
|
## ADDED Requirements
|
||||||
|
### Requirement: Отбор операций за календарный период
|
||||||
|
При формировании отчёта система SHALL включать все и только операции выбранного
|
||||||
|
счёта, дата учёта которых входит в выбранный период включительно по Europe/Moscow.
|
||||||
|
|
||||||
|
#### Scenario: Последний день периода и следующий день
|
||||||
|
- **GIVEN** выбран период 01.10.2026–31.10.2026 и счёт A
|
||||||
|
- **AND** на счёте A есть операции X с датой учёта 31.10.2026 и Y с датой 01.11.2026
|
||||||
|
- **WHEN** формируется отчёт
|
||||||
|
- **THEN** отчёт включает X и не включает Y
|
||||||
|
|
||||||
|
Плохо: «корректно отбирать операции до конца месяца» — не определены месяц,
|
||||||
|
границы, дата отбора и счёт; «THEN список корректен» — нет проверяемого исхода.
|
||||||
|
Для таблиц решений и переходов используй эталоны из профиля.
|
||||||
-->
|
-->
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
# System Analysis
|
# System Analysis
|
||||||
|
|
||||||
<!-- guide: Hub document. Owns context, boundaries, flows overview and contract
|
<!-- Hub. Владеет контекстом, границами, обзором flows и семантикой контрактов;
|
||||||
semantics; details live in selected modules and are referenced, not copied.
|
детали — в выбранных модулях, на них ссылаются, а не копируют. -->
|
||||||
Remove guides before G3. -->
|
|
||||||
|
|
||||||
## Requirement Coverage
|
## Requirement Coverage
|
||||||
|
|
||||||
| Requirement | Flow / contract | Module |
|
| Requirement | Flow / контракт | Модуль |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
|
||||||
## System Context and Boundaries
|
## System Context and Boundaries
|
||||||
@@ -19,32 +18,32 @@
|
|||||||
|
|
||||||
## Data Ownership
|
## Data Ownership
|
||||||
|
|
||||||
| Data | Source of truth | Writers | Readers |
|
| Данные | Источник истины | Пишут | Читают |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
|
||||||
## Interaction Flows
|
## Interaction Flows
|
||||||
|
|
||||||
<!-- guide: Reference diagrams/*.mmd; text only for what a diagram cannot carry. -->
|
<!-- Ссылки на diagrams/*.mmd; текст — только для того, что диаграмма не передаёт. -->
|
||||||
|
|
||||||
## Contracts
|
## Contracts
|
||||||
|
|
||||||
<!-- guide: Per contract: producer/consumer, operation/event, version, file in
|
<!-- На каждый контракт: producer/consumer, операция/событие, версия, файл в
|
||||||
contracts/, compatibility class (additive / breaking), published source it
|
contracts/, класс совместимости (additive / breaking), опубликованный источник,
|
||||||
must match after implementation. -->
|
с которым он обязан совпасть после реализации. -->
|
||||||
|
|
||||||
| Contract | Producer → Consumer | File | Compatibility | Published source |
|
| Контракт | Producer → Consumer | Файл | Совместимость | Опубликованный источник |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Error Semantics
|
## Error Semantics
|
||||||
|
|
||||||
<!-- guide: Business vs technical failures at boundaries. Detailed retry/
|
<!-- Бизнес- и технические отказы на границах. Подробные retry/идемпотентность/
|
||||||
idempotency/recovery belongs to failure-model.md when selected. -->
|
восстановление — в failure-model.md, если выбран. -->
|
||||||
|
|
||||||
## Compatibility / Migration
|
## Compatibility / Migration
|
||||||
|
|
||||||
## Detailed Modules
|
## Detailed Modules
|
||||||
|
|
||||||
<!-- guide: Link each selected module (data-mapping, failure-model, nfr,
|
<!-- Ссылка на каждый выбранный модуль (data-mapping, failure-model, nfr,
|
||||||
observability) with one line on what it decides. -->
|
observability) и одна строка о том, что он решает. -->
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
# Tasks
|
# Tasks
|
||||||
|
|
||||||
<!-- guide: Task line format:
|
<!-- Формат task:
|
||||||
- [ ] X.Y [main|frontend|backend|docs|specs] <Kind>: <action + outcome>
|
- [ ] X.Y [main|frontend|backend|docs|specs] <Kind>: <действие + результат>
|
||||||
- **Covers:** `capability` — `Requirement heading` pairs, or `N/A` — <reason>
|
- **Covers:** пары `capability` — `Requirement heading` или `N/A` — <причина>
|
||||||
- **Source:** exact upstream artifact section / contract / docs-impact entry
|
- **Source:** точный раздел upstream-артефакта / контракт / запись docs-impact
|
||||||
- **Verification:** owner-controlled command, observable check or artifact
|
- **Verification:** команда владельца, наблюдаемая проверка или артефакт
|
||||||
- **Depends on:** task ids or none
|
- **Depends on:** ids tasks или none
|
||||||
Kinds: Implementation, Migration, Automated Testing, Contract Parity, Docs,
|
Kinds: Implementation, Migration, Automated Testing, Contract Parity, Docs,
|
||||||
Independent Review, QA Verification, Verification. Remove guides before G3. -->
|
Independent Review, QA Verification, Verification.
|
||||||
|
Пример:
|
||||||
|
- [ ] 2.1 [backend] Implementation: пропускать операции, чей внешний id уже сохранён
|
||||||
|
- **Covers:** `portfolio/holdings` — `Итог портфеля не учитывает дубли операций`
|
||||||
|
- **Source:** design.md Decisions D2; analysis/failure-model.md F3
|
||||||
|
- **Verification:** `npm run verify` в backend; повторный импорт fixture оставляет итог 100.00
|
||||||
|
- **Depends on:** 1.1
|
||||||
|
Плохо: «Verification: проверить, что работает»; Covers с heading, которого нет в specs. -->
|
||||||
|
|
||||||
## 1. <!-- guide: delivery phase -->
|
## 1. <!-- этап delivery -->
|
||||||
|
|
||||||
## Docs Impact
|
## Docs Impact
|
||||||
|
|
||||||
<!-- guide: Only when docs-impact is not selected: list docs/ targets with
|
<!-- Только если docs-impact не выбран: targets в docs/ с итоговым AS-IS или одна
|
||||||
resulting AS-IS, or one concrete no-change reason. Delete when docs-impact.md
|
конкретная причина без изменений. Удали раздел, если есть analysis/docs-impact.md. -->
|
||||||
exists. -->
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Test Cases — <!-- guide: area -->
|
# Test Cases — <!-- область -->
|
||||||
|
|
||||||
<!-- guide: One file per area under test-cases/. A case adds concrete data, steps
|
<!-- Один файл на область в analysis/test-cases/. Case добавляет конкретные данные,
|
||||||
or an oracle that the scenario does not already state; otherwise reference the
|
шаги или oracle, которых нет в Scenario; иначе qa-analysis ссылается на Scenario,
|
||||||
scenario from qa-analysis instead of writing a case. Remove guides. -->
|
а case не пишется. -->
|
||||||
|
|
||||||
### TC-<!-- guide: AREA-NNN --> <!-- guide: title -->
|
### TC-<!-- AREA-NNN --> <!-- название -->
|
||||||
|
|
||||||
- **Verifies:** `capability` — `Requirement heading` / Scenario
|
- **Verifies:** `capability` — `Requirement heading` / Scenario
|
||||||
- **Risk / Priority:**
|
- **Risk / Priority:**
|
||||||
@@ -13,4 +13,4 @@
|
|||||||
- **Steps:**
|
- **Steps:**
|
||||||
1.
|
1.
|
||||||
- **Expected Results:**
|
- **Expected Results:**
|
||||||
- **Automation Candidate:** <!-- guide: yes (level) | no (reason) -->
|
- **Automation Candidate:** <!-- yes (уровень) | no (причина) -->
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
# UX Design
|
# UX Design
|
||||||
|
|
||||||
<!-- guide: Interaction design of observable behavior from Delta Specs. Reference
|
<!-- Interaction design observable behavior из Delta Specs. Ссылайся на требования
|
||||||
requirements as `capability` — `Requirement heading`; do not restate them.
|
как `capability` — `Requirement heading`, не пересказывай. Без серверной
|
||||||
No server architecture, contracts or component/state-management choices.
|
архитектуры, контрактов и выбора компонентов/state management. -->
|
||||||
Remove guides before G3. -->
|
|
||||||
|
|
||||||
## User Goals
|
## User Goals
|
||||||
|
|
||||||
## User Flows
|
## User Flows
|
||||||
|
|
||||||
<!-- guide: Mermaid flowchart or numbered steps per flow, with entry and exit points. -->
|
<!-- Mermaid flowchart или нумерованные шаги на каждый flow, с точками входа и выхода. -->
|
||||||
|
|
||||||
## Information Architecture and Screen Structure
|
## Information Architecture and Screen Structure
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@
|
|||||||
|
|
||||||
## UI States
|
## UI States
|
||||||
|
|
||||||
| Screen / region | Loading | Empty | Error | Success | Partial / stale |
|
| Экран / область | Loading | Empty | Error | Success | Partial / stale |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
|
||||||
## Validation Feedback
|
## Validation Feedback
|
||||||
@@ -26,15 +25,15 @@
|
|||||||
|
|
||||||
## Accessibility
|
## Accessibility
|
||||||
|
|
||||||
<!-- guide: Keyboard path, focus order, names/roles, contrast, non-color cues. -->
|
<!-- Путь с клавиатуры, порядок фокуса, имена/роли, контраст, признаки не только цветом. -->
|
||||||
|
|
||||||
## Design System Reuse
|
## Design System Reuse
|
||||||
|
|
||||||
## Materials
|
## Materials
|
||||||
|
|
||||||
<!-- guide: Inline Markdown by default; local files follow the Visual Package rule. -->
|
<!-- По умолчанию inline Markdown; локальные файлы — по правилу Visual Package. -->
|
||||||
|
|
||||||
| Material ID | Relative path / reference | Screen/state and purpose | SHA-256 |
|
| ID материала | Относительный путь / ссылка | Экран/состояние и назначение | SHA-256 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
|
|
||||||
## Open UX Questions
|
## Open UX Questions
|
||||||
|
|||||||
Reference in New Issue
Block a user