Architecture decisions: files, diagram references, views and panels

- *.archify-decision files: id (unique, words), title, status, date,
  elements, diagrams, supersedes, and prose; parsed, merged and link-checked
  in the model index; diagnostics on the file
- diagrams point at decisions with decisions: [...] on the diagram, nodes
  and edges; stripped before Archify, unknown ids reported, completion,
  hover and go-to-definition for the ids and for elements/diagrams/
  supersedes in decision files
- Decisions panel on the main.archify page; Decisions rail panel under a
  diagram (hover lights up the node or connection); decisions on element
  cards and in the element dialog; decision view for the file, with a
  lost-model note outside a model
- Archify | New Decision… command and template; editor schemas; the three
  New Model examples carry decisions; docs, changelog, 0.8.0
This commit is contained in:
root-at-skic committed 2026-10-19 23:13:05 +03:00
1 parent 33ebdd6328
commit ebf320a023
49 files changed
+1637 -55

No files matched your search

+37 -5
View File
@@ -1,6 +1,8 @@
import path from 'node:path';
import * as vscode from 'vscode';
import { DiagramSource, readDiagramSource } from '../sources/detect';
import { ModelProblem, Registry, resolveRefs } from './format';
import { DecisionAttachment, ModelProblem, Registry, resolveRefs } from './format';
import { effectiveStatus } from './decisions';
import { ModelService } from './service';
/**
@@ -18,6 +20,16 @@ export interface PreparedDiagram {
links: Map<string, string>;
/** Node id → registry description. */
descriptions: Map<string, string>;
/** Decisions of this diagram for the Decisions rail panel: pointed at by the diagram, or naming it. */
decisions: DiagramDecision[];
}
export interface DiagramDecision {
id: string;
title?: string;
status: string;
file: string;
attachedTo: DecisionAttachment[];
}
/**
@@ -28,12 +40,32 @@ export interface PreparedDiagram {
export async function prepareDiagram(models: ModelService, uri: vscode.Uri, text: string): Promise<PreparedDiagram | undefined> {
const source = readDiagramSource(text, uri.path);
if (!source) return undefined;
const found = await models.registryFor(uri);
const resolved = resolveRefs(source.diagram, { registry: found?.registry, fileName: uri.path });
const index = await models.indexFor(uri);
const found = index ? { root: index.root, registry: index.registry } : undefined;
const resolved = resolveRefs(source.diagram, { registry: found?.registry, decisions: index ? new Set(index.decisions.decisions.keys()) : undefined, fileName: uri.path });
// Only diagrams that use refs (or carry a model block) need the resolved form.
const changed = resolved.refs.size > 0 || resolved.problems.length > 0 || 'model' in source.diagram || resolved.links.size > 0;
const changed = resolved.refs.size > 0 || resolved.problems.length > 0 || 'model' in source.diagram || resolved.links.size > 0 || resolved.decisionRefs.length > 0;
const prepared: DiagramSource = changed
? { ...source, diagram: resolved.diagram, json: JSON.stringify(resolved.diagram, null, 2) }
: source;
return { source: prepared, root: found?.root, registry: found?.registry, problems: resolved.problems, links: resolved.links, descriptions: resolved.descriptions };
// Decisions for the rail: the ones this diagram points at, plus the ones that name this diagram.
const decisions: DiagramDecision[] = [];
if (index) {
const rel = path.relative(index.root.fsPath, uri.fsPath).split(path.sep).join('/');
const byId = new Map<string, DiagramDecision>();
const add = (id: string, attachedTo?: DecisionAttachment) => {
const d = index.decisions.decisions.get(id);
if (!d) return;
let row = byId.get(id);
if (!row) {
row = { id, title: d.title, status: effectiveStatus(d, index.decisions.decisions).status, file: d.file, attachedTo: [] };
byId.set(id, row);
decisions.push(row);
}
if (attachedTo && !row.attachedTo.some((a) => JSON.stringify(a) === JSON.stringify(attachedTo))) row.attachedTo.push(attachedTo);
};
for (const ref of resolved.decisionRefs) add(ref.id, ref.attachedTo);
for (const d of index.decisions.decisions.values()) if (d.diagrams.includes(rel)) add(d.id, { kind: 'diagram' });
}
return { source: prepared, root: found?.root, registry: found?.registry, problems: resolved.problems, links: resolved.links, descriptions: resolved.descriptions, decisions };
}