Use this skill when regenerating .archive/graph.dot.
Goal
.archive/graph.dot must be deterministically derivable from .archive/records/*.md.
- Derivation must fail closed when the archive is ill-formed.
Inputs
- Record files:
.archive/records/*.md
- Pointer files:
.archive/pointers/*.yaml and .archive/pointers/branches/*.yaml
Validation (fail closed)
Derivation MUST fail if any of the following are true:
- A record file is missing YAML front matter.
- YAML front matter does not parse.
- Required keys missing:
id, schema_version, type, title, parents.
schema_version is not 1.
id does not match filename.
id does not match [a-z0-9][a-z0-9-]*.
type is not one of: Design, Build, Evaluate, Decide.
- Duplicate record IDs.
parents references a non-existent record ID.
- The
parents graph contains a cycle.
- A pointer references a non-existent record ID.
- Any
edges[*].to references a non-existent record ID.
Warnings (do not fail derivation)
- Missing optional fields (
status, tags, git, metrics).
- Unknown
edges[*].type (ignore unless used as a hint).
DOT output rules (deterministic)
- Always generate a single DOT file:
- path:
.archive/graph.dot
- graph name:
phira_archive
- Determinism:
- sort nodes by
id ascending
- sort parent edges by
(from,to)
- sort lineage edge labels deterministically (token sort +
| join)
Graph structure
- Use
digraph phira_archive { ... }.
- Set global defaults:
rankdir=LR
concentrate=true
node [fontname="Helvetica", shape="box", style="rounded,filled", fillcolor="white", color="gray30"]
edge [fontname="Helvetica", color="gray40"]
Node labeling
- Node id is the record
id.
- Label should be compact and stable:
label = "<id>\n<type>\n<title>"
- Optional: append
status on its own line if present.
Type styling (suggested)
- Design: shape=folder, fillcolor="#f0f0f0"
- Build: shape=box, fillcolor="#e8f4ff"
- Evaluate: shape=component, fillcolor="#eaffea"
- Decide: shape=box, fillcolor="#fff7cc"
Edges
Only draw ONE directed edge between records: the lineage edge from parent -> child.
- Do not render a separate semantic-edge layer.
- Instead, attach a short label to the lineage edge.
This avoids duplicate/opposing arrows and keeps the DAG readable.
Lineage edge emission
For each parent in parents, emit exactly:
"<parent>" -> "<id>" [label="<edge_label>", weight=2, penwidth=1.5]
Edge label computation (deterministic)
Compute edge_label for the lineage edge parent -> child as follows:
- Collect semantic hints from YAML
edges across all records.
- If there exists any semantic
edges relationship connecting child and parent (in either direction), convert those to one or more lineage label tokens.
- If one or more tokens exist, use them (sorted, joined with
|) as the lineage edge label.
- Otherwise, fall back to a type-based default label:
Design -> Build: implemented_by
Build -> Evaluate: evaluated_by
Evaluate -> Decide: supports
Evaluate -> Evaluate: refined_by
Decide -> Decide: superseded_by
- fallback:
derives
Semantic-hint mapping
Map semantic edge types to lineage label tokens, relative to the lineage direction parent -> child:
- If semantic edge is
child -> parent:
implements => implemented_by
tests => evaluated_by
evaluates => evaluated_by
refines => refined_by
supersedes => superseded_by
depends_on => required_by
- If semantic edge is
parent -> child:
If multiple semantic hints apply, sort the resulting tokens and join with |.
Pointers in the graph
- Do not add pointer nodes.
- Include a short comment header listing resolved pointer head(s) (record IDs).
Error reporting
- On failure, report:
- which file
- what key/path is invalid
- the smallest concrete fix (rename id, add missing field, correct reference)
1---2name: phira-archive-dag3description: Deterministic Graphviz/DOT derivation + validation for archive records.4---56Use this skill when regenerating `.archive/graph.dot`.78## Goal910- `.archive/graph.dot` must be deterministically derivable from `.archive/records/*.md`.11- Derivation must fail closed when the archive is ill-formed.1213## Inputs1415- Record files: `.archive/records/*.md`16- Pointer files: `.archive/pointers/*.yaml` and `.archive/pointers/branches/*.yaml`1718## Validation (fail closed)1920Derivation MUST fail if any of the following are true:2122- A record file is missing YAML front matter.23- YAML front matter does not parse.24- Required keys missing: `id`, `schema_version`, `type`, `title`, `parents`.25- `schema_version` is not `1`.26- `id` does not match filename.27- `id` does not match `[a-z0-9][a-z0-9-]*`.28- `type` is not one of: `Design`, `Build`, `Evaluate`, `Decide`.29- Duplicate record IDs.30- `parents` references a non-existent record ID.31- The `parents` graph contains a cycle.32- A pointer references a non-existent record ID.33- Any `edges[*].to` references a non-existent record ID.3435## Warnings (do not fail derivation)3637- Missing optional fields (`status`, `tags`, `git`, `metrics`).38- Unknown `edges[*].type` (ignore unless used as a hint).3940## DOT output rules (deterministic)4142- Always generate a single DOT file:43 - path: `.archive/graph.dot`44 - graph name: `phira_archive`45- Determinism:46 - sort nodes by `id` ascending47 - sort parent edges by `(from,to)`48 - sort lineage edge labels deterministically (token sort + `|` join)4950### Graph structure5152- Use `digraph phira_archive { ... }`.53- Set global defaults:54 - `rankdir=LR`55 - `concentrate=true`56 - `node [fontname="Helvetica", shape="box", style="rounded,filled", fillcolor="white", color="gray30"]`57 - `edge [fontname="Helvetica", color="gray40"]`5859### Node labeling6061- Node id is the record `id`.62- Label should be compact and stable:63 - `label = "<id>\n<type>\n<title>"`64- Optional: append `status` on its own line if present.6566### Type styling (suggested)6768- Design: shape=folder, fillcolor="#f0f0f0"69- Build: shape=box, fillcolor="#e8f4ff"70- Evaluate: shape=component, fillcolor="#eaffea"71- Decide: shape=box, fillcolor="#fff7cc"7273### Edges7475Only draw ONE directed edge between records: the lineage edge from `parent -> child`.7677- Do not render a separate semantic-edge layer.78- Instead, attach a short label to the lineage edge.7980This avoids duplicate/opposing arrows and keeps the DAG readable.8182#### Lineage edge emission8384For each `parent` in `parents`, emit exactly:8586`"<parent>" -> "<id>" [label="<edge_label>", weight=2, penwidth=1.5]`8788#### Edge label computation (deterministic)8990Compute `edge_label` for the lineage edge `parent -> child` as follows:91921) Collect semantic hints from YAML `edges` across all records.9394- If there exists any semantic `edges` relationship connecting `child` and `parent` (in either direction), convert those to one or more lineage label tokens.95- If one or more tokens exist, use them (sorted, joined with `|`) as the lineage edge label.96972) Otherwise, fall back to a type-based default label:9899- `Design -> Build`: `implemented_by`100- `Build -> Evaluate`: `evaluated_by`101- `Evaluate -> Decide`: `supports`102- `Evaluate -> Evaluate`: `refined_by`103- `Decide -> Decide`: `superseded_by`104- fallback: `derives`105106#### Semantic-hint mapping107108Map semantic edge types to lineage label tokens, relative to the lineage direction `parent -> child`:109110- If semantic edge is `child -> parent`:111 - `implements` => `implemented_by`112 - `tests` => `evaluated_by`113 - `evaluates` => `evaluated_by`114 - `refines` => `refined_by`115 - `supersedes` => `superseded_by`116 - `depends_on` => `required_by`117- If semantic edge is `parent -> child`:118 - `produces` => `produces`119120If multiple semantic hints apply, sort the resulting tokens and join with `|`.121122### Pointers in the graph123124- Do not add pointer nodes.125- Include a short comment header listing resolved pointer head(s) (record IDs).126127## Error reporting128129- On failure, report:130 - which file131 - what key/path is invalid132 - the smallest concrete fix (rename id, add missing field, correct reference)