318 lines
14 KiB
Plaintext
318 lines
14 KiB
Plaintext
|
|
let s = import "../reflection/schema.ncl" in
|
||
|
|
|
||
|
|
# ADR System — Operational reflection guide
|
||
|
|
# Covers: authoring, reading, validating, and superseding ADRs in the ontoref ecosystem.
|
||
|
|
# All content in English. Schema field names (actor, depends_on, verify) are API identifiers.
|
||
|
|
|
||
|
|
let AdrAction = [|
|
||
|
|
'new_adr,
|
||
|
|
'read_adr,
|
||
|
|
'validate_decision,
|
||
|
|
'supersede_adr,
|
||
|
|
'export_adr,
|
||
|
|
|] in
|
||
|
|
|
||
|
|
{
|
||
|
|
style = {
|
||
|
|
format = "Nickel ADR",
|
||
|
|
schema = "adrs/defaults.ncl",
|
||
|
|
content_lang = "English",
|
||
|
|
field_names = "API identifiers — do not translate",
|
||
|
|
invariant_check = "Every ADR decision string must be validated against .ontology/core.ncl before writing",
|
||
|
|
},
|
||
|
|
|
||
|
|
agent = {
|
||
|
|
rules = [
|
||
|
|
"Read adrs/defaults.ncl and adrs/schema.ncl before generating any ADR content",
|
||
|
|
"Run `nickel export adrs/adr-NNN-*.ncl` to verify the file exports cleanly after writing",
|
||
|
|
"Check .ontology/core.ncl invariants_at_risk and ontology_check.verdict before marking status = 'Accepted",
|
||
|
|
"If verdict is 'Risky or 'Unsafe, require explicit justification in InvariantJustification before accepting",
|
||
|
|
"All string values in id, title, context, decision, rationale, constraints, consequences must be in English",
|
||
|
|
"Schema field names (name, description, closing_condition) are defined by ontoref-ontology in English",
|
||
|
|
"Never create an ADR without at least one Hard constraint with a check_hint",
|
||
|
|
"Superseded ADRs keep status = 'Superseded — do not delete them",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
modes = [
|
||
|
|
|
||
|
|
{
|
||
|
|
id = "new_adr",
|
||
|
|
trigger = "A significant architectural decision has been made or is being considered",
|
||
|
|
preconditions = [
|
||
|
|
"Decision affects the ecosystem architecture, not a single project implementation detail",
|
||
|
|
"No existing ADR covers this decision",
|
||
|
|
"adrs/defaults.ncl and adrs/schema.ncl are readable",
|
||
|
|
],
|
||
|
|
steps = [
|
||
|
|
{
|
||
|
|
id = "check_ontology",
|
||
|
|
action = 'validate_decision,
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export .ontology/core.ncl | get nodes | where level == 'Axiom | get id",
|
||
|
|
verify = "Output lists the invariant IDs that the decision may affect",
|
||
|
|
note = "Identify which invariants are at risk before writing the ADR",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "draft_adr",
|
||
|
|
action = 'new_adr,
|
||
|
|
depends_on = [{ step = "check_ontology", kind = 'OnSuccess }],
|
||
|
|
actor = 'Both,
|
||
|
|
note = "Use the next sequential adr-NNN id. Title must be a declarative statement, not a question.",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "add_constraints",
|
||
|
|
action = 'new_adr,
|
||
|
|
depends_on = [{ step = "draft_adr", kind = 'Always }],
|
||
|
|
actor = 'Both,
|
||
|
|
note = "Every ADR requires at least one Hard constraint. The check_hint must be an executable command.",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "verify_export",
|
||
|
|
action = 'export_adr,
|
||
|
|
depends_on = [{ step = "add_constraints", kind = 'OnSuccess }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export adrs/adr-NNN-*.ncl",
|
||
|
|
verify = "Command exits 0 and produces valid JSON",
|
||
|
|
on_error = { strategy = 'Stop },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "set_accepted",
|
||
|
|
action = 'new_adr,
|
||
|
|
depends_on = [{ step = "verify_export", kind = 'OnSuccess }],
|
||
|
|
actor = 'Human,
|
||
|
|
note = "Only the project maintainer sets status = 'Accepted. An ADR in 'Proposed is not authoritative.",
|
||
|
|
},
|
||
|
|
],
|
||
|
|
postconditions = [
|
||
|
|
"adrs/adr-NNN-*.ncl exports cleanly with status = 'Accepted",
|
||
|
|
"ontology_check.verdict is 'Safe or has explicit InvariantJustification",
|
||
|
|
"At least one constraint has severity = 'Hard and a non-empty check_hint",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
{
|
||
|
|
id = "read_as_human",
|
||
|
|
trigger = "Understanding a past decision or exploring the ADR corpus",
|
||
|
|
preconditions = [
|
||
|
|
"adrs/ directory contains at least one .ncl file",
|
||
|
|
],
|
||
|
|
steps = [
|
||
|
|
{
|
||
|
|
id = "list_adrs",
|
||
|
|
action = 'read_adr,
|
||
|
|
actor = 'Human,
|
||
|
|
cmd = "ls adrs/adr-*.ncl | sort",
|
||
|
|
verify = "Files are listed in adr-NNN order",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "export_adr",
|
||
|
|
action = 'export_adr,
|
||
|
|
depends_on = [{ step = "list_adrs", kind = 'OnSuccess }],
|
||
|
|
actor = 'Human,
|
||
|
|
cmd = "nickel export adrs/adr-NNN-*.ncl | jq '{title, status, decision, constraints}'",
|
||
|
|
verify = "decision and constraints.claim fields are in English",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "check_related",
|
||
|
|
action = 'read_adr,
|
||
|
|
depends_on = [{ step = "export_adr", kind = 'Always }],
|
||
|
|
actor = 'Human,
|
||
|
|
note = "Follow related_adrs references to understand the decision chain",
|
||
|
|
},
|
||
|
|
],
|
||
|
|
postconditions = [
|
||
|
|
"Decision chain is understood: context → decision → constraints → ontology_check",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
{
|
||
|
|
id = "read_as_agent",
|
||
|
|
trigger = "AI agent needs to understand the current architectural position before proposing changes",
|
||
|
|
preconditions = [
|
||
|
|
"Task affects ontoref protocol architecture, crate scope, or schema definitions",
|
||
|
|
],
|
||
|
|
steps = [
|
||
|
|
{
|
||
|
|
id = "export_all_accepted",
|
||
|
|
action = 'export_adr,
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "ls adrs/adr-*.ncl | each { |f| nickel export $f } | where status == 'Accepted",
|
||
|
|
verify = "Only Accepted ADRs are in the working set",
|
||
|
|
note = "Proposed and Superseded ADRs are not authoritative — do not use them to justify decisions",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "extract_constraints",
|
||
|
|
action = 'validate_decision,
|
||
|
|
depends_on = [{ step = "export_all_accepted", kind = 'OnSuccess }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export adrs/adr-*.ncl | get constraints | where severity == 'Hard | get check_hint",
|
||
|
|
verify = "Hard constraints are available as executable check_hints",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "cross_reference_ontology",
|
||
|
|
action = 'validate_decision,
|
||
|
|
depends_on = [{ step = "extract_constraints", kind = 'Always }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export .ontology/core.ncl | get nodes | where invariant == true | get id",
|
||
|
|
verify = "Invariant IDs match the invariants_at_risk fields across accepted ADRs",
|
||
|
|
note = "A proposed change that touches any invariant requires a new ADR, not a patch",
|
||
|
|
},
|
||
|
|
],
|
||
|
|
postconditions = [
|
||
|
|
"Agent has the active constraint set and knows which invariants bound the decision space",
|
||
|
|
"No change is proposed that violates a Hard constraint without a new ADR",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
{
|
||
|
|
id = "validate_decision",
|
||
|
|
trigger = "A proposed code or architecture change needs validation against existing ADRs",
|
||
|
|
preconditions = [
|
||
|
|
"The change is described in concrete terms: what is being added, removed, or modified",
|
||
|
|
"Accepted ADRs are available via `nickel export`",
|
||
|
|
],
|
||
|
|
steps = [
|
||
|
|
{
|
||
|
|
id = "run_check_hints",
|
||
|
|
action = 'validate_decision,
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export adrs/adr-*.ncl | get constraints | where severity == 'Hard | each { |c| nu -c $c.check_hint }",
|
||
|
|
verify = "All Hard check_hints exit 0 or produce no output (no violation found)",
|
||
|
|
on_error = { strategy = 'Stop },
|
||
|
|
note = "A non-zero exit or match output means a Hard constraint is violated — stop and document",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "check_protocol_invariants",
|
||
|
|
action = 'validate_decision,
|
||
|
|
depends_on = [{ step = "run_check_hints", kind = 'OnSuccess }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export .ontology/core.ncl | get nodes | where invariant == true | get id",
|
||
|
|
verify = "None of the invariant node IDs are affected by the proposed change",
|
||
|
|
note = "A change touching any invariant node requires a new ADR, not a patch",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "report_result",
|
||
|
|
action = 'validate_decision,
|
||
|
|
depends_on = [{ step = "check_protocol_invariants", kind = 'Always }],
|
||
|
|
actor = 'Agent,
|
||
|
|
note = "Report: which constraints passed, which failed, which ADR is implicated for each failure",
|
||
|
|
},
|
||
|
|
],
|
||
|
|
postconditions = [
|
||
|
|
"All Hard constraints pass or a blocking ADR violation is documented with the implicated constraint ID",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
{
|
||
|
|
id = "query_constraints",
|
||
|
|
trigger = "Need to know the current active constraint set, or reconstruct constraints at a specific point in time",
|
||
|
|
preconditions = [
|
||
|
|
"adrs/ directory contains exported ADR files",
|
||
|
|
"`nickel export` is available",
|
||
|
|
],
|
||
|
|
steps = [
|
||
|
|
{
|
||
|
|
id = "active_constraints",
|
||
|
|
action = 'read_adr,
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "ls adrs/adr-*.ncl | each { |f| nickel export $f } | where status == 'Accepted | each { |a| { adr: $a.id, title: $a.title, constraints: $a.constraints } }",
|
||
|
|
verify = "Output contains only Accepted ADRs with their constraint sets",
|
||
|
|
note = "This is the authoritative current constraint set. Proposed and Superseded ADRs are excluded.",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "hard_constraints_only",
|
||
|
|
action = 'validate_decision,
|
||
|
|
depends_on = [{ step = "active_constraints", kind = 'Always }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "ls adrs/adr-*.ncl | each { |f| nickel export $f } | where status == 'Accepted | get constraints | flatten | where severity == 'Hard | select id claim check_hint",
|
||
|
|
verify = "Each Hard constraint has a non-empty check_hint",
|
||
|
|
note = "Hard constraints are non-negotiable. A change that violates one requires a new ADR, not a workaround.",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "point_in_time",
|
||
|
|
action = 'read_adr,
|
||
|
|
depends_on = [{ step = "active_constraints", kind = 'Always }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "ls adrs/adr-*.ncl | each { |f| nickel export $f } | where (($it.status == 'Accepted or $it.status == 'Superseded) and $it.date <= \"YYYY-MM\") | where superseded_by? == null | get constraints | flatten",
|
||
|
|
verify = "Returns the constraint set that was active at the given date",
|
||
|
|
note = "Replace YYYY-MM with target date. An ADR without superseded_by was still Accepted at that date.",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "supersession_chain",
|
||
|
|
action = 'read_adr,
|
||
|
|
depends_on = [{ step = "point_in_time", kind = 'Always }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "ls adrs/adr-*.ncl | each { |f| nickel export $f } | select id status supersedes superseded_by date | sort-by date",
|
||
|
|
verify = "Each superseded ADR has a superseded_by reference to the replacing ADR",
|
||
|
|
note = "Follow the chain: superseded_by → new ADR → check its supersedes field to confirm bidirectional link",
|
||
|
|
},
|
||
|
|
],
|
||
|
|
postconditions = [
|
||
|
|
"Agent has the active Hard constraint set for validation",
|
||
|
|
"Or: the historical constraint set at a specific date is reconstructed",
|
||
|
|
"Supersession chain is traceable in both directions via supersedes / superseded_by",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
{
|
||
|
|
id = "supersede_adr",
|
||
|
|
trigger = "An existing Accepted ADR must be replaced due to architectural evolution",
|
||
|
|
preconditions = [
|
||
|
|
"A new ADR (adr-NNN) has been drafted and exports cleanly",
|
||
|
|
"The superseding ADR references the old ADR in related_adrs",
|
||
|
|
"The old ADR ID and title are known",
|
||
|
|
],
|
||
|
|
steps = [
|
||
|
|
{
|
||
|
|
id = "set_superseded_status",
|
||
|
|
action = 'supersede_adr,
|
||
|
|
actor = 'Human,
|
||
|
|
note = "Edit the old ADR file: set status = 'Superseded. Do not delete the file.",
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "verify_new_exports",
|
||
|
|
action = 'export_adr,
|
||
|
|
depends_on = [{ step = "set_superseded_status", kind = 'OnSuccess }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export adrs/adr-NNN-*.ncl",
|
||
|
|
verify = "New ADR exports with status = 'Accepted",
|
||
|
|
on_error = { strategy = 'Stop },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "verify_old_exports",
|
||
|
|
action = 'export_adr,
|
||
|
|
depends_on = [{ step = "set_superseded_status", kind = 'OnSuccess }],
|
||
|
|
actor = 'Agent,
|
||
|
|
cmd = "nickel export adrs/adr-OLD-*.ncl | get status",
|
||
|
|
verify = "Output is 'Superseded",
|
||
|
|
on_error = { strategy = 'Stop },
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id = "update_references",
|
||
|
|
action = 'supersede_adr,
|
||
|
|
depends_on = [
|
||
|
|
{ step = "verify_new_exports", kind = 'OnSuccess },
|
||
|
|
{ step = "verify_old_exports", kind = 'OnSuccess },
|
||
|
|
],
|
||
|
|
actor = 'Human,
|
||
|
|
cmd = "grep -r 'adr-OLD' adrs/ reflection/ .ontology/",
|
||
|
|
note = "Update any references to the old ADR ID in other files",
|
||
|
|
},
|
||
|
|
],
|
||
|
|
postconditions = [
|
||
|
|
"Old ADR has status = 'Superseded and remains in adrs/",
|
||
|
|
"New ADR has status = 'Accepted and references the old ADR in related_adrs",
|
||
|
|
"No file references the old ADR as if it were still active",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
],
|
||
|
|
}
|
||
|
|
| {
|
||
|
|
modes
|
||
|
|
| Array (s.Mode AdrAction)
|
||
|
|
| doc "ADR operational modes — validated against reflection/schema.ncl at eval time",
|
||
|
|
..
|
||
|
|
}
|