import { ALGO_FACE, LAYER_FACE, LEX_FACE, TENSION_FACE } from "@govlab/constants"; import { MECHANISM_SECTION_ICON, TENSION_SECTION_ICON } from "#configuration/icons/architecture.icons"; import { MECHANISM_SECTION_ID, TENSION_SECTION_ID } from "#core/ids/architecture.ids"; import { RESOLUTION_SECTION_ID, SCHEMA_TAB } from "#core/ids/ontology.ids"; import { ONTOLOGY_PAGE } from "#core/ids/page.ids"; import type { Section } from "#types/document.types"; import { TYPESCRIPT_LANGUAGE } from "#configuration/constants/code.constants"; import { faceLink } from "#domain/converters/ontology.converter"; import { tabLink } from "#assets/link.assets"; const RESOLUTION_SHAPE = 'export type Mechanism = "scope-separation" | "irreducible-tradeoff" | "mitigation";\n\nexport interface Resolution {\n readonly a: RecordId;\n readonly b: RecordId;\n readonly mechanism: Mechanism;\n readonly scopeA: LayerId;\n readonly scopeB: LayerId;\n readonly rule: string;\n}\n\nexport const mechanismOf = (a: Record, b: Record, recorded: ReadonlyMap): Mechanism => {\n const explicit = recorded.get(pairKey(a.id, b.id));\n if (explicit !== undefined) {\n return explicit.mechanism;\n }\n const separable = a.kind === "principle" && b.kind === "principle" && a.layer !== b.layer;\n return separable ? "scope-separation" : "irreducible-tradeoff";\n};'; const TENSION_DIAGRAM = 'flowchart TB\n pair["Two records pull against each other on one construct"]\n exemption["An exemption · one wins here, for a reason nobody wrote down"]\n recorded{"Is a resolution recorded for the pair?"}\n kinds{"What kind of thing is on each side, and in which scope?"}\n separate["Two principles in different scopes · each holds whole in its own"]\n trade["Anything else · a quality on either side, or two principles in one scope · measure, choose an operating point, write it down"]\n mitigate["Recorded with a discriminator · a rule names what tells the two apart"]\n pair -. the tempting answer .-> exemption\n pair --> recorded\n recorded -- yes --> mitigate\n recorded -- no --> kinds\n kinds -- principle and principle, scopes differ --> separate\n kinds -- otherwise --> trade'; const QUADRANT_DIAGRAM = "quadrantChart\n title Where a pair lands decides its mechanism\n x-axis one scope --> two scopes\n y-axis a quality on one side --> two principles\n quadrant-1 separate by scope\n quadrant-2 trade at a measured point\n quadrant-3 trade at a measured point\n quadrant-4 trade at a measured point\n statelessness against state before mutation: [0.85, 0.85]\n single source of truth against decentralization: [0.75, 0.9]\n fail fast against graceful degradation: [0.15, 0.8]\n consistency against availability: [0.8, 0.2]\n encapsulation against debuggability: [0.3, 0.15]\n backpressure against throughput: [0.2, 0.3]"; const EXAMPLES_DIAGRAM = 'flowchart TB\n subgraph scope["Scope separation · both hold whole"]\n a1["stateless"] -- computation · resource --- a2["state before mutation"]\n b1["immutability"] -- computed data · resource state --- b2["state over code"]\n c1["fail fast"] -- resource halts · computation marks --- c2["explicit invalidity"]\n d1["do not build what is not needed"] -- implementation · interfaces --- d2["deliberate under-specification"]\n e1["single owner"] -- runtime resources · definitions --- e2["code as data"]\n g1["normalisation"] -- the canonical store · derived read models --- g2["query performance"]\n end\n subgraph trade["Irreducible trade-off · measured to an operating point"]\n i1["consistency"] -- inside a boundary · across autonomy boundaries --- i2["availability"]\n j1["fail fast"] -- where a halt is cheaper than a wrong answer --- j2["graceful degradation"]\n k1["encapsulation"] -- hidden internals · a live inspector --- k2["debuggability"]\n l1["backpressure"] -- bounded producers · sustained rate --- l2["throughput"]\n end\n subgraph mitigate["Mitigation · a rule names the discriminator"]\n m1["one source of truth"] -- semantic sameness · incidental likeness --- m2["locality of behaviour"]\n end'; const TENSION_SECTION: Section = { icon: TENSION_SECTION_ICON, id: TENSION_SECTION_ID, intro: `Two things that pull against each other on one construct are resolved by first asking what kind of thing each of them is and which scope it holds in. Every resolution the canon holds is ${faceLink(TENSION_FACE, "decentralization-single-source-of-truth", "readable as a record")}, and where no record exists the mechanism is derived from the kinds and scopes of the two sides, in the order recorded or derived draws and over the grid kinds against scopes lays out. That is why a tension held as data, the shape a resolution record types, is consulted before the collision happens while one held in prose is rediscovered by collision.`, subsections: [ { blocks: [ { application: "Classify each side before resolving anything: a principle, a quality, a metric, a cost, and the scope it holds in. Record what you derive with its two records by identity, the mechanism, the scope each side holds in and the rule in one sentence, so the next reader finds a rule rather than a memory.", boundary: "A tension is a pair the canon relates by a tension edge, which is a different relation from a conflict. A conflict points from a principle to the anti-pattern that negates it, and it carries no resolution because one side is simply refused. Two rules that merely differ in strictness are not a tension, and neither is a rule that does not apply to a construct; the first is one rule with a scope, the second is a classification question.", cause: "A principle stated without its scope reads as universal, so the moment two universal statements meet on one construct one of them has to lose, and the loser is chosen by whoever is reviewing.", decision: "Look for a recorded resolution before deriving one, and refuse an exemption as the third option, because one side winning for a reason nobody wrote down is a rule with no scope.", failureMode: "A review says the config must fail fast, the next review says the parser must tolerate bad input, both cite a principle, both are right, and the code ends up doing neither consistently.", kind: "lesson", principle: "An apparent conflict has a mechanism that follows from the kinds and scopes on each side, and the resolution is recorded rather than remembered.", problem: "Principles that appear to conflict are resolved case by case, and case-by-case resolution is a different rule in every case.", validation: "Take any construct where two things seemed to collide and name the kind and scope of each. If a resolution is recorded, it is settled; if a mechanism follows from the kinds and scopes, write it down and it is settled. If the construct still sits on both sides, it is two constructs and needs splitting.", }, { caption: "recorded or derived", kind: "mermaid", text: TENSION_DIAGRAM }, ], title: "Recorded, never remembered", }, { blocks: [ { kind: "text", text: "The resolution is typed for the same reason a principle is. It names its two records by identity, the mechanism from the closed set of three, the scope each side holds in, and the rule in one sentence. The derivation has the shape of every default that must be safe when nobody has thought about the case.", }, { kind: "text", text: "A recorded resolution wins outright, since a person has already decided. Failing that, two principles that hold in different scopes separate, because each can hold whole on its own side. Everything else, a quality on either side or two principles that share one scope, falls to a trade-off, because the only thing that can be said about a pair nobody has decided is that it has to be measured. Mitigation is never derived: a discriminator is a judgement, and a judgement the data does not carry explicitly does not exist.", }, { caption: "kinds against scopes", kind: "mermaid", text: QUADRANT_DIAGRAM }, { code: RESOLUTION_SHAPE, kind: "code", language: TYPESCRIPT_LANGUAGE, title: "a resolution record" }, ], title: "The derivation", }, { blocks: [ { kind: "text", text: `Recording it that way lets a check resolve a finding to its side. A validator that fires on a fallback pattern in a resource path reaches the fail fast side, and one that fires on an unmarked uncertainty in a computation reaches the explicit-invalidity side, and neither has to know the other exists. The canon's ${faceLink(ALGO_FACE, "conflict-and-tension-resolution", "resolution contract")} states the same obligation from the other direction: architecture is trade-off governance as much as principle application, and its policy admits an override only as a recorded decision beside the mitigations and the documented trade-offs.`, }, { kind: "text", text: `Every resolution the canon currently holds is listed on the schema tab, with its mechanism, its two scopes and its rule.`, }, ], title: "What a check does with it", }, { blocks: [ { kind: "text", text: "Where two enforced checks conflict on one construct, neither is satisfied by violating the other and neither is disabled: the construct is reshaped into the single form that satisfies every rule, and where that form is not obvious the question goes to whoever owns the rules. An exclusion added to make a check pass is the exemption this whole section refuses, arriving through the tooling instead of the review; when rules collide on the methodology page walks two such collisions.", }, ], title: "Two checks on one construct", }, ], title: "A tension has a mechanism", }; const MECHANISM_SECTION: Section = { icon: MECHANISM_SECTION_ICON, id: MECHANISM_SECTION_ID, intro: "Three mechanisms resolve every tension the canon holds, and the wrong one applied to a pair is what turns a review into an argument. A tension has a mechanism derives which one applies; this chapter walks the recurring pairs under each, grouped as the recurring pairs draws.", subsections: [ { blocks: [ { application: "Where two principles hold in different scopes, name the scope each holds in and what holding means there, and classify the construct under review to one scope before applying either. Where a principle meets a quality, or two principles share one scope, measure the thing in tension where the construct lives, choose the operating point, and write the point down beside the choice so it is a decision with an owner rather than a mood. Where two things share a scope and differ in meaning, write the rule that names the discriminator. Where a construct genuinely sits on both sides of a scope boundary, split it along the boundary rather than weakening either rule for it.", boundary: "None of the three mechanisms is an exemption. A resolution where one principle is weakened inside its own scope was not a boundary, an operating point nobody measured is a mood, a discriminator held in someone's judgement is not yet a rule, and an override that names no scope and no reason is not a resolution at all.", cause: "A quality treated as a rule has no scope to be given, so a review argues a trade-off as though it were a violation, and a semantic difference treated as a scope has no boundary to draw, so a review draws one anyway and moves it next time.", decision: "Apply the mechanism the pair's kinds and scopes select, rather than the one the reviewer prefers.", failureMode: "A team decides that consistency beats availability, ships a service that refuses every request under partition, and discovers that the decision was never a boundary but an operating point nobody had measured.", kind: "lesson", principle: "Scope separation, a measured trade-off and a mitigating rule are the three mechanisms, and which one applies follows from what is on each side and where it holds.", problem: "One mechanism is applied to every tension, so a trade-off is argued as a violation and a semantic difference is drawn as a boundary that moves every time.", validation: "Take any resolved tension and ask which mechanism resolved it. If both sides are principles and their scopes differ, the boundary settles it and both sides survive whole. If one side is a quality, or both sides share one scope, ask where the operating point sits and who measured it. If the pair claims a discriminator, ask where it is written.", }, { caption: "the recurring pairs", kind: "mermaid", text: EXAMPLES_DIAGRAM }, ], title: "Three mechanisms, no exemption", }, { blocks: [ { kind: "text", text: `Scope separation is the mechanism the core layers were built to produce, and its pairs recur because the ${faceLink(LAYER_FACE, "computation-core", "computation")} and ${faceLink(LAYER_FACE, "resource-core", "resource")} scopes answer the same question with different rules. Statelessness against snapshot before mutation: a computation flows through, a resource is snapshotted before it changes. Immutability against protecting state: computed data is frozen after creation, resource state is mutable but managed.`, }, { kind: "text", text: "Fail fast against explicit invalidity: a broken resource invariant halts, a computation uncertainty is marked and carried. Not building the hypothetical against under-specifying deliberately: no code for a feature nobody asked for, and no constraint on an interface that would block one later. Each pair is the derivation at work, two principles on two layers.", }, { kind: "text", text: `The canon holds the same derivation as records where it has been consulted. Single source of truth against decentralization, and autonomy against standardization, separate because each side governs a different layer. Normalization against ${faceLink(LEX_FACE, "query-performance", "query performance")} is ${faceLink(TENSION_FACE, "normalization-query-performance", "recorded explicitly")}: the canonical model is normalised and only derived read models are denormalised, never the store. The test of a boundary is that both sides survive it whole.`, }, ], title: "Separated by scope", }, { blocks: [ { kind: "text", text: `A trade-off cannot be scoped away, and seeing why is what stops a review from arguing one as if it were a violation. A quality is exhibited on both sides of any boundary you draw. Consistency against ${faceLink(LEX_FACE, "availability", "availability")} is strong inside one transaction boundary and eventual consistency across autonomy boundaries, and both halves of that sentence are an operating point somebody chose, ${faceLink(TENSION_FACE, "availability-consistency", "not a side that won")}. The CAP theorem is the reason the point exists and never the point itself.`, }, { kind: "text", text: `Fail fast against graceful degradation shows the other road into a trade-off. Both are principles, but both hold in the one correctness scope, so there is no side for either to hold whole on. The pair is ${faceLink(TENSION_FACE, "fail-fast-graceful-degradation", "decided")} by where a halt costs less than a wrong answer, which is a measurement of the failure's blast radius and never a principle outranking another.`, }, { kind: "text", text: `Encapsulation against ${faceLink(LEX_FACE, "debuggability", "debuggability")} is ${faceLink(TENSION_FACE, "debuggability-encapsulation", "settled")} by hiding internals and shipping a live inspector, a point on a line rather than a wall. Backpressure against throughput is ${faceLink(TENSION_FACE, "backpressure-throughput", "bounded producers against sustained rate")}. In each case the mechanism is the same: name the quality, measure it where the construct lives, choose the point, and record who chose it and against what number.`, }, ], title: "Traded at a measured point", }, { blocks: [ { kind: "text", text: `Mitigation is the mechanism for two things in one scope told apart by meaning. Do not repeat yourself against ${faceLink(LEX_FACE, "locality-of-behavior", "locality of behaviour")} is the recurring case. Semantics are centralised, the rules, the schemas and the one place a fact lives, and incidental co-occurrence stays local, because two passages that happen to read alike are not one fact.`, }, { kind: "text", text: `No boundary separates them, since both live in the ${faceLink(LAYER_FACE, "structural-core", "structural")} scope, and no measurement decides them, since neither is a quality. What resolves them is a ${faceLink(TENSION_FACE, "do-not-repeat-yourself-dry-locality-of-behavior", "rule that names the discriminator")}: is the sameness semantic or textual? A discriminator written down is consultable before the collision. One held in someone's judgement is rediscovered at every clone report, and abstracted wrongly half the time.`, }, ], title: "Mitigated by a rule", }, ], title: "Separate, trade, or mitigate", }; export const TENSION_SECTIONS: readonly Section[] = [TENSION_SECTION, MECHANISM_SECTION];