method-ASK_system-ASK-hero.figure.js
This view shows one file as text; the diagram front door links it. It is generated at build time, static and bounded: nothing here runs the code, and there is no browsing beyond this file.
/* method-ASK_system-ASK-hero.figure.js — D11 // system-ASK causal orientation (source-v2, ASK 2026-09-14) Bespoke one-off method figure. Projects the accepted P1 v5 claim set as published in examples/system-ASK.md. Consumes design-system-ASK Three Functions by reference (_dsa-tokens/three-functions.css) at the owner's 30% role-fill + full role-stroke contract, on FUNCTION-BEARING APPARATUS ONLY: legislative the SOURCE-OF-INTENT ROLE, as the constitutive legislative source; the execution grant and the enforced path-scoped grant, as DELEGATED apparatus. The aperture a grant produces is a permission — a quantity, not apparatus — and takes no role color. executive the executor — the bounded realization chamber — and the realization span inside the grant judicial the adjudication gate, and disposition ACTOR / ROLE BINDING. The occupying actor and the role it occupies are drawn as two elements. "human ASK" is a NEUTRAL ACTOR ENVELOPE; inside it sit two role boxes — the legislative source-of-intent role and the judicial adjudication gate. The envelope takes no role color: the actor is not exhausted by any role it occupies, and the figure shows it, because one envelope holds a legislative role and a judicial one. The role at the apex is constitutively legislative, so it carries the role treatment. Its label stays on the theme foreground — a label is never the role hue. Constitutive and delegated legislation share the one role value; their rank is carried by TOPOLOGY (the source role sits outside the realization field that its grant opens inside) and by labels — never a second hue, an opacity rank, a fourth function or a role-colored label. Everything else is NEUTRAL, and the exclusions are load-bearing. Artifact governance and the governed artifact take no role color: carrying judicial cyan up into governance would imply selection = governance. Project cells, public surfaces, and the structural frames and edges are neutral for the same reason — they are the structure the functions act within, not functions. Structure, not state — Spectral State is deliberately NOT used. COMPOSITION. source-v2 is composed on a grid. Every ORDINARY NODE — a labeled box that is not a container — has ONE common size (NW × NH below): the two role boxes, the three rungs and members of the left stack, the right stack, domain authority, project cells and the enforced grant. Only structural containers differ: the actor envelope (sized around its two nodes), the executor chamber (the column's width, taller for its nested apparatus), the nested grant and intent frames, the realization field frame, and the cross-cutting design rail. Peer stacks share both column edges (X, below); relation labels and routed paths run in reserved lanes (Y, below); the ordered-authority descent takes a deliberate gutter beside the chamber, and the evidence return from project cells takes the exterior gutter around domain authority. Both keys — the three functions and the edge grammar — are drawn inside the SVG (KEY, below). Elements carry data-node / data-col / data-edge attributes so the grid and the routing can be measured on the rendered page rather than asserted. */ (function () { /* FAIL CLOSED BEFORE ANY LAYOUT. diagrams-fit.js is a DS-owned support file vendored alongside this figure and loaded immediately BEFORE it. Checked here so a missing helper stops the builder before the SVG exists, rather than leaving a half-built artifact whose failure has to be inferred. */ if (!window.DIAGRAM_FIT || typeof window.DIAGRAM_FIT.compute !== 'function') { throw new Error('Diagram fit support is missing. Load diagrams-fit.js before the figure builder.'); } /* The Fit's v3 placement options (balance, compactClearance) are not read by an older diagrams-fit.js, which would silently keep the old geometry. FAIL CLOSED instead. */ if (!(window.DIAGRAM_FIT.VERSION >= 3)) { throw new Error('diagrams-fit.js is older than v3. Re-vendor it from design-system-ASK with this figure builder.'); } /* FAIL-CLOSED on the design-system gesture carrier, on the same terms: diagrams-pointer.js (v2) is vendored alongside this figure and loaded BEFORE it. */ if (!window.DIAGRAM_POINTER || typeof window.DIAGRAM_POINTER.attach !== 'function' || !(window.DIAGRAM_POINTER.VERSION >= 2)) { throw new Error('Diagram gesture support is missing. Load diagrams-pointer.js (v2) before the figure builder.'); } const svgNS = 'http://www.w3.org/2000/svg'; const W = 2000, H = 1140; const svg = document.getElementById('svg'); svg.setAttribute('width', W); svg.setAttribute('height', H); svg.setAttribute('viewBox', `0 0 ${W} ${H}`); while (svg.firstChild) svg.removeChild(svg.firstChild); function el(name, attrs = {}, kids = []) { const e = document.createElementNS(svgNS, name); for (const [k, v] of Object.entries(attrs)) if (v != null) e.setAttribute(k, v); for (const c of kids) e.appendChild(typeof c === 'string' ? document.createTextNode(c) : c); return e; } const edges = el('g', { class: 'edges' }), nodes = el('g', { class: 'nodes' }); svg.append(edges, nodes); /* --------------------------------------------------------------------------- EDGE GRAMMAR — six classes. Five inherited from the ASK figure vocabulary, plus CONFORMANCE, which exists because ROLEREL-1's second limb cannot be drawn in any of the five. INHERITANCE and CONFORMANCE are the closest neighbours — both dashed, both about one owner consuming another — so they differ on TWO axes, dash cadence AND terminal form, and are separable without the key. --------------------------------------------------------------------------- */ const AH = 9; const headR = (x, y) => el('path', { class: 'edge-arrowhead', d: `M ${x-AH} ${y-AH/1.6} L ${x-AH} ${y+AH/1.6} L ${x} ${y} Z` }); const headL = (x, y) => el('path', { class: 'edge-arrowhead', d: `M ${x+AH} ${y-AH/1.6} L ${x+AH} ${y+AH/1.6} L ${x} ${y} Z` }); const headD = (x, y) => el('path', { class: 'edge-arrowhead', d: `M ${x-AH/1.6} ${y-AH} L ${x+AH/1.6} ${y-AH} L ${x} ${y} Z` }); const headU = (x, y) => el('path', { class: 'edge-arrowhead', d: `M ${x-AH/1.6} ${y+AH} L ${x+AH/1.6} ${y+AH} L ${x} ${y} Z` }); /* EVIDENCE terminal — OPEN chevron, never filled. The visual weight difference between this and a filled authority head is the figure's core non-identity. Two orientations: leftward (into the gate's side) and upward (into the gate from below). */ const openL = (x, y) => el('path', { class: 'edge e-evidence', fill: 'none', d: `M ${x+11} ${y-6} L ${x} ${y} L ${x+11} ${y+6}` }); const openU = (x, y) => el('path', { class: 'edge e-evidence', fill: 'none', d: `M ${x-6} ${y+11} L ${x} ${y} L ${x+6} ${y+11}` }); /* CONFORMANCE terminal — a HOLLOW head: an outlined triangle that gives the edge its direction, apart from the filled heads of authority and inheritance and from evidence's open chevron. Two orientations: upward (into the consuming repository's bottom edge) and rightward (the key's sample). */ const hollowU = (x, y) => el('path', { class: 'edge e-conform-head', d: `M ${x-AH/1.6} ${y+AH} L ${x+AH/1.6} ${y+AH} L ${x} ${y} Z` }); const hollowR = (x, y) => el('path', { class: 'edge e-conform-head', d: `M ${x-AH} ${y-AH/1.6} L ${x-AH} ${y+AH/1.6} L ${x} ${y} Z` }); /* RELAY marker — a small square sitting in the break. */ const relayMark = (x, y) => el('rect', { x: x-5, y: y-5, width: 10, height: 10, class: 'edge e-relay-mark' }); const box = (x, y, w, h, cls='node-box', rx=4, data={}) => el('rect', { x, y, width:w, height:h, rx, ry:rx, class:cls, ...data }); const line = (d, cls='edge', name=null) => el('path', { class:cls, fill:'none', d, 'data-edge': name }); const lbl = (x, y, t, cls='node-label', a='start') => el('text', { x, y, class:cls, 'text-anchor':a }, [t]); const note = (x, y, t, a='start', extra='') => el('text', { x, y, class:'node-note'+(extra?' '+extra:''), 'text-anchor':a }, [t]); const tag = (x, y, t, a='middle') => el('text', { x, y, class:'flow-tag', 'text-anchor':a }, [t]); const sect = (x, y, t, a='middle') => el('text', { x, y, class:'section-tag', 'text-anchor':a }, [t]); /* =========================================================================== GRID — one ordinary-node size; columns with shared left AND right edges; lanes. NW × NH is the size of every ordinary node. A node's label sits at +26 and its notes at +46 / +62. NH is sized for the fullest node, a heading and two note lines, so the last line clears the box bottom by one note pitch (16 units); a node with one note line keeps the same size, with that room below it. The enforced grant, the one node with no note, centers its label in the box instead. The columns are read left to right: the actor envelope (outside the realization field), the method / advisor / critic stack, the control-surface / executor / project-cells stack, the disposition / governance / artifact / public-surface stack, and the cross-cutting design rail. Domain authority straddles the field's left edge and shares the left stack's row grid. Frames share one inset, 32 units: the field sits 32 outside its columns and the design rail, and the system boundary 32 outside the actor envelope and the field at left and right (its top and bottom edges are set by the top lanes and the bottom band). Across the actor's column every vertical edge sits 32 from the next: boundary, envelope, disposition route, role boxes; then role boxes, envelope, field, left stack. Lanes are the horizontal bands between box rows; each y below names what occupies it. The ordered-authority descent runs in the gutter between the left stack and the chamber column (X.desc); the evidence return from project cells runs in the exterior gutter between the system boundary and domain authority (X.ev), up into the gate from below. Page export. The grid is not sized to the exporter; the viewBox is (PAGE EXPORT, at the end of the builder). The actual page result is inspected, not assumed. =========================================================================== */ const NW = 264, NH = 84; const X = { outer0: 4, outer1: 1812, // system-ASK boundary: 32 outside the actor envelope and the field env: 36, envW: 360, // actor envelope: the disposition route runs 32 inside its left edge role: 100, // role + gate boxes: 32 right of the disposition route, 32 inside the envelope's right edge field0: 428, field1: 1780, // ecology-ASK realization field: 32 outside its columns and the design rail c2: 460, // method-ASK · advisor · critic desc: 792, // ordered-authority descent gutter c3: 864, // control-surface · executor · project cells c4: 1236, // disposition · artifact governance · governed artifact · public surfaces · enforced grant rail: 1608, railW: 140, // design rail da: 360, // domain authority (straddles field0) ev: 232, // exterior evidence-return gutter (= the gate's center) }; const CX = { c2: X.c2 + NW/2, c3: X.c3 + NW/2, c4: X.c4 + NW/2 }; /* Rows and lanes are measured from the bottom edge of the row above them, so the node height moves every row, lane, port and container that depends on it together, and each lane keeps its clearances. */ const Y = {}; Y.outerTop = 40; // system-ASK boundary Y.dispRail = 76; // top lane: ASK's adjudicated disposition, descending Y.fieldTop = 108; // ecology-ASK realization field Y.rowA = 140; // method-ASK · control-surface · disposition · design rail /* Lane A/B is sized for the conformance U under the first two rungs: its run sits 40 units below row A, the depth of the legs in lane D/E, and clears the execution-grant label below it by about 18. */ const A = Y.rowA + NH; // lane A/B, from row A's bottom edge: Y.confLabel = A + 20; // conformance label, beside the U's first leg Y.confRun = A + 40; // conformance U, its run Y.grantLabel = A + 64; // execution-grant label Y.grantRail = A + 80; // execution-grant rail = the source-of-intent role's center Y.mutLabel = A + 120; // mutation-channel label Y.rowB = A + 136; // advisor · executor · artifact governance Y.rowC = Y.rowB + NH + 44; // critic · governed artifact (the chamber spans rows B–C) const C = Y.rowC + NH; // lane C/D, from row C's bottom edge: Y.ptrRail = C + 32; // continuity pointer rail Y.ptrLabel = C + 48; // continuity label Y.prodTag = C + 68; // production-circuit name Y.laneLabel = C + 92; // ordered-authority label Y.rowD = C + 112; // domain authority · project cells · public expression const D = Y.rowD + NH; // lane D/E, from row D's bottom edge: Y.routesLabel = D + 24; // owner-declared routes label (right of project cells) Y.evLabel = D + 24; // evidence-return label, on the same line Y.laneDE = D + 40; // ONE rail line for the two edges that meet project cells' bottom edge: Y.routesRail = Y.laneDE; // the owner-declared routes rail, entering from the right, Y.evRail = Y.laneDE; // and the evidence return, leaving to the left Y.rowE = D + 56; // public context scaffold Y.fieldBot = Y.rowE + NH + 32; // the field's bottom edge: the same 32-unit inset as its top (fieldTop → rowA) /* BOTTOM BAND — measured from the field's bottom edge, top to bottom: the field's two-line caption, the private context perimeter note just inside the system boundary, the boundary's bottom edge, the membrane with the enforced grant (an ordinary node, centered on the membrane), the wall note, the key rule. The order and every relation are unchanged; the spacing is set from the measured ink of each line (a 9-unit section tag spans about 4 units either side of its anchor; a 10-unit note about 4.5 above and 5.5 below) so that each distinct element clears the next by about 16 units, the caption hangs about 14 below its frame, its two lines clear each other by about 10, and the wall note keeps its 18 above the key rule. */ const F = Y.fieldBot; Y.cap1 = F + 19; // the figure's core non-identity Y.cap2 = F + 37; // the field's name Y.outerBot = F + 84; // system-ASK boundary, bottom edge Y.perimNote = Y.outerBot - 22; // private context perimeter note, inside the boundary Y.membrane = Y.outerBot + 59; // the enforced grant's top clears the boundary edge by about 16 Y.memNote = Y.membrane + 63; // wall note, about 16 below the enforced grant Y.keyRule = Y.memNote + 24; Y.key = [Y.keyRule + 17, Y.keyRule + 43, Y.keyRule + 63, Y.keyRule + 83]; Y.keyDivider = Y.keyRule + 30; const ROLE_Y = Y.grantRail - NH/2; // the grant leaves the role at rail height const GATE_Y = Y.rowC; // the gate shares the critic's row const EXH = Y.rowC + NH - Y.rowB; // chamber height: rows B–C const ENV = { top: ROLE_Y - 58, bot: GATE_Y + NH + 32 }; // envelope, sized around its two nodes /* ===== outer system boundary ===== */ nodes.append(el('rect', { x:X.outer0, y:Y.outerTop, width:X.outer1 - X.outer0, height:Y.outerBot - Y.outerTop, rx:16, ry:16, class:'flow-group', 'data-node':'boundary' })); nodes.append(tag((X.outer0 + X.outer1) / 2, Y.outerTop - 14, 'system-ASK')); /* ===== inner realization field — a STRUCTURAL FRAME, therefore NEUTRAL. It bounds where realization happens; it is not itself the executive function. Drawn as a path with ONE deliberate gap: the left edge breaks where domain authority straddles it, so the boundary meets that box instead of running through its labels. ===== */ const FR = 14, GAP0 = Y.rowD, GAP1 = Y.rowD + NH; nodes.append(el('path', { class:'flow-group', fill:'none', 'data-node':'field', d: `M ${X.field0} ${GAP1} V ${Y.fieldBot-FR} A ${FR} ${FR} 0 0 0 ${X.field0+FR} ${Y.fieldBot} H ${X.field1-FR} ` + `A ${FR} ${FR} 0 0 0 ${X.field1} ${Y.fieldBot-FR} V ${Y.fieldTop+FR} A ${FR} ${FR} 0 0 0 ${X.field1-FR} ${Y.fieldTop} ` + `H ${X.field0+FR} A ${FR} ${FR} 0 0 0 ${X.field0} ${Y.fieldTop+FR} V ${GAP0}` })); /* ===== human ASK — the OCCUPYING ACTOR. A NEUTRAL envelope, inside the outer boundary and OUTSIDE the field: P1 SPINE-1b, a geometric claim, not a caption. It holds two roles. ===== */ nodes.append(el('rect', { x:X.env, y:ENV.top, width:X.envW, height:ENV.bot - ENV.top, rx:10, ry:10, class:'flow-group actor-envelope', 'data-node':'envelope' })); nodes.append(lbl(X.role, ENV.top + 24, 'human ASK')); nodes.append(note(X.role, ENV.top + 42, 'occupying actor · more than one role')); /* ROLE 1 — the source-of-intent role at the apex. LEGISLATIVE (CONSTITUTIVE). Its center is the grant rail's height, so the grant leaves it directly. */ nodes.append(box(X.role, ROLE_Y, NW, NH, 'node-box fn-leg', 4, { 'data-node':'ordinary', 'data-col':'c1' })); nodes.append(lbl(X.role + 18, ROLE_Y + 26, 'source-of-intent role')); nodes.append(note(X.role + 18, ROLE_Y + 46, 'the single normative apex', 'start', 'on-role')); nodes.append(note(X.role + 18, ROLE_Y + 62, 'membership without derivability', 'start', 'on-role')); /* ROLE 2 — the adjudication gate, attached to the apex role, not a second authority center. JUDICIAL. */ edges.append(line(`M ${X.ev} ${ROLE_Y + NH} L ${X.ev} ${GATE_Y}`, 'edge e-auth', 'apex-gate attachment')); nodes.append(box(X.role, GATE_Y, NW, NH, 'node-box fn-jud', 4, { 'data-node':'ordinary', 'data-col':'c1' })); nodes.append(lbl(X.role + 18, GATE_Y + 26, 'adjudication gate')); nodes.append(note(X.role + 18, GATE_Y + 46, 'evidence reaches ASK', 'start', 'on-role')); nodes.append(note(X.role + 18, GATE_Y + 62, 'THROUGH the gate, as input', 'start', 'on-role')); /* ===== ordinary nodes — one size, placed on the column grid ===== */ const node = (col, x, y, label, notes, cls='node-box') => { nodes.append(box(x, y, NW, NH, cls, 4, { 'data-node':'ordinary', 'data-col':col })); nodes.append(lbl(x + 18, y + 26, label)); const onRole = /fn-/.test(cls) ? 'on-role' : ''; notes.forEach((t, i) => nodes.append(note(x + 18, y + (i ? 62 : 46), t, 'start', onRole))); }; /* ===== RAILS — two relations, which must not be drawn as one. ===== */ node('c2', X.c2, Y.rowA, 'method-ASK', ['explains · portable primitives']); node('c3', X.c3, Y.rowA, 'control-surface', ['operates · execution protocol']); /* DESIGN RAIL — vertical, CROSSING the chain rather than extending it. A cross-cutting axis, not an ordinary node: it spans rows A–E. */ const RAIL_BOT = Y.rowE + NH; nodes.append(box(X.rail, Y.rowA, X.railW, RAIL_BOT - Y.rowA, 'node-box', 4, { 'data-node':'rail' })); nodes.append(lbl(X.rail + 16, Y.rowA + 28, 'design-')); nodes.append(lbl(X.rail + 16, Y.rowA + 46, 'system-ASK')); nodes.append(note(X.rail + 16, Y.rowA + 68, 'expresses')); nodes.append(note(X.rail + 16, Y.rowA + 96, 'foundations')); nodes.append(note(X.rail + 16, Y.rowA + 112, 'scaffolds')); nodes.append(note(X.rail + 16, Y.rowA + 128, 'surface patterns')); nodes.append(note(X.rail + 16, Y.rowA + 156, 'three kinds,')); nodes.append(note(X.rail + 16, Y.rowA + 172, 'and only three')); nodes.append(note(X.rail + 16, RAIL_BOT - 58, 'a CROSS-CUTTING')); nodes.append(note(X.rail + 16, RAIL_BOT - 42, 'visual-inheritance')); nodes.append(note(X.rail + 16, RAIL_BOT - 26, 'axis — not a rung')); /* ===== FIELD MEMBERS ===== */ node('c2', X.c2, Y.rowB, 'advisor', ['reviews, does not write']); node('c2', X.c2, Y.rowC, 'critic', ['fresh-context challenge']); /* EXECUTOR + nested delegation — not sub-apexes. EXECUTIVE on the chamber. The chamber's executive treatment marks REALIZATION UNDER THE RULE: as that function it authors no rule and judges no result. It does not exhaust what the occupying executor may do — deriving the task intent under its grant is delegated legislation, and verifying returns and selecting locally within the grant are judicial acts (normative-apex.md §Three Functions bridge; §Delegation). The figure colors the function the chamber performs, not the whole of its occupant. LEGISLATIVE on the execution grant that opens inside it: the grant is DELEGATED apparatus carrying the standard into operative scope. The aperture it produces is a permission, not apparatus, and is not colored. Inside the grant, the derived task intent is a further delegated narrowing — geometry, not a second fill — and the REALIZATION span is cut out of the grant's fill and carries the executive treatment, so no realization label sits on legislative fill. Each level is an even-odd path with the next level CUT OUT of it, so no two role washes ever stack into a lighter one: the owner's contract carries field-vs-object in geometry and negative space, never in stacked opacity. The chamber keeps the column's edges; only its height differs from an ordinary node. */ const rr = (x, y, w, h, r) => `M ${x+r} ${y} H ${x+w-r} A ${r} ${r} 0 0 1 ${x+w} ${y+r} V ${y+h-r} ` + `A ${r} ${r} 0 0 1 ${x+w-r} ${y+h} H ${x+r} A ${r} ${r} 0 0 1 ${x} ${y+h-r} ` + `V ${y+r} A ${r} ${r} 0 0 1 ${x+r} ${y} Z`; const EX = Y.rowB, EXB = Y.rowB + EXH; const EN = EX + (EXH - 180) / 2; // the nest's reference top: centered in added height const GX = X.c3 + 22, GW = NW - 44; // grant frame: 886..1106 const IX = GX + 20, IW = GW - 40; // derived-intent frame: 906..1086 const RX = IX + 20, RW = IW - 40; // realization span: 926..1066 nodes.append(el('path', { class:'node-box fn-exe', 'fill-rule':'evenodd', 'data-node':'chamber', 'data-col':'c3', d: rr(X.c3, EX, NW, EXH, 8) + ' ' + rr(GX, EN + 52, GW, 112, 6) })); nodes.append(lbl(X.c3 + 18, EX + 24, 'executor')); nodes.append(note(X.c3 + 18, EX + 42, 'plans + mutates · single-writer', 'start', 'on-role')); nodes.append(el('path', { class:'flow-group fn-leg', 'fill-rule':'evenodd', 'data-node':'grant', d: rr(GX, EN + 52, GW, 112, 6) + ' ' + rr(RX, EN + 92, RW, 34, 4) })); nodes.append(el('rect', { x:IX, y:EN + 72, width:IW, height:74, rx:5, ry:5, class:'flow-group', 'data-node':'intent' })); nodes.append(el('rect', { x:RX, y:EN + 92, width:RW, height:34, rx:4, ry:4, class:'node-box fn-exe', 'data-node':'realization' })); /* Each nested label is centered in ITS OWN band, between the rect it names and the next rect inside it. */ nodes.append(note(GX + 10, EN + 63, 'execution grant', 'start', 'on-role')); nodes.append(note(IX + 10, EN + 83, 'derived task intent', 'start', 'on-role')); nodes.append(note(RX + 10, EN + 110, 'realization', 'start', 'on-role')); /* DOMAIN AUTHORITY — straddles the field boundary. TWO axes, drawn separately. It is an ordinary node, the same size as project cells, on the same row. */ node('da', X.da, Y.rowD, 'domain authority', ['externality: DECLARED', 'by its position, per engagement']); edges.append(line(`M ${X.da + NW} ${Y.rowD + NH - 12} L ${X.c3 - 8} ${Y.rowD + NH - 12}`, 'edge e-auth', 'binding scope')); edges.append(headR(X.c3 - 4, Y.rowD + NH - 12)); nodes.append(note(X.da + NW + 16, Y.rowD + NH - 26, 'binding scope')); node('c3', X.c3, Y.rowD, 'project cells', ['domain truth + pressure', 'INSIDE the field']); /* RIGHT COLUMN — one judicial act, then two NEUTRAL downstream objects. Disposition is JUDICIAL: it evaluates, selects, ratifies and closes. Artifact governance and the governed artifact are DOWNSTREAM of that closure and outside the three-function palette — carrying judicial cyan up into governance would imply selection = governance. The break in color here is the claim. */ node('c4', X.c4, Y.rowA, 'disposition', ['the learning circuit terminates HERE'], 'node-box fn-jud'); node('c4', X.c4, Y.rowB, 'artifact governance', ['binds the accepted result']); node('c4', X.c4, Y.rowC, 'governed artifact', ['+ record']); /* PUBLIC SURFACES — inside the field, at its outer edge. */ node('c4', X.c4, Y.rowD, 'public expression', ['renders + articulates', 'INSIDE the field']); node('c4', X.c4, Y.rowE, 'public context scaffold', ['inside the field, wall-safe side']); /* ===== MEMBRANE — a GATED crossing, not a plain boundary. ===== */ /* The enforced grant is an ORDINARY NODE on the membrane: the one size, in the right-hand column, under the public context scaffold whose inheritance edge enters it. Delegated legislative apparatus, like the execution grant; its label is a box heading on the theme foreground, as on every role-bearing ordinary node. It carries one line and no note, so the label is centered in the box, both ways, rather than set at the heading position. Its ink runs about 5.8 units above its middle anchor and 7.3 below it (the g's descender), so the anchor sits 0.7 above the box's center. */ const GRANT_Y = Y.membrane - NH/2; /* The wall line spans the system boundary's width. It is schematic: its length measures no grant's reach. An enforced grant confines one named surface to its granted subtree, and only there is the wall structural. */ edges.append(line(`M ${X.outer0} ${Y.membrane} L ${X.c4} ${Y.membrane}`, 'edge e-membrane', 'membrane')); edges.append(line(`M ${X.c4 + NW} ${Y.membrane} L ${X.outer1} ${Y.membrane}`, 'edge e-membrane', 'membrane')); nodes.append(box(X.c4, GRANT_Y, NW, NH, 'node-box fn-leg', 4, { 'data-node':'ordinary', 'data-col':'c4', 'data-role':'enforced-grant' })); nodes.append(lbl(CX.c4, Y.membrane - 0.7, 'enforced grant', 'node-label', 'middle')); nodes.append(note(X.c2, Y.perimNote, 'private context perimeter — folder separation DEFINES the boundary and makes it AUDITABLE')); nodes.append(note(X.c2, Y.memNote, 'schematic wall, not a measure of reach · an enforced grant confines ONE named surface to its granted subtree, and only there is the wall structural · NO edge crosses into the private side')); /* =========================================================================== EDGES — each routed in the lane or gutter reserved for it above. =========================================================================== */ /* AUTHORITY — the grant leaves the source-of-intent role at its center height and descends INTO the field, into the chamber's top. */ edges.append(line(`M ${X.role + NW} ${Y.grantRail} L ${CX.c3} ${Y.grantRail} L ${CX.c3} ${Y.rowB - 4}`, 'edge e-auth', 'execution grant')); edges.append(headD(CX.c3, Y.rowB)); nodes.append(note(X.c2 + 20, Y.grantLabel, 'execution grant · authority descends')); /* AUTHORITY — the ordered chain, rung 1 >> rung 2. Enters the rung's left side, upper. */ edges.append(line(`M ${X.c2 + NW} ${Y.rowA + 22} L ${X.c3 - 8} ${Y.rowA + 22}`, 'edge e-auth', 'ordered chain 1-2')); edges.append(headR(X.c3 - 4, Y.rowA + 22)); /* AUTHORITY — rung 2 >> rung 3. THE THIRD RUNG IS PROJECT CELLS. It leaves control-surface's left side, lower, takes the gutter beside the chamber, and enters project cells' left side, upper: two purposeful turns, a bracket, not a staircase. It crosses the execution-grant rail once, perpendicularly, mid-segment and far from either terminal — a crossing, not a junction — and the relay line, which is dashed. */ edges.append(line(`M ${X.c3} ${Y.rowA + NH - 14} L ${X.desc} ${Y.rowA + NH - 14} L ${X.desc} ${Y.rowD + 16} L ${X.c3 - 8} ${Y.rowD + 16}`, 'edge e-auth', 'ordered chain 2-3')); edges.append(headR(X.c3 - 4, Y.rowD + 16)); nodes.append(note(X.desc + 10, Y.laneLabel, 'ordered authority')); /* CONFORMANCE — the reverse axis. The execution protocol runs from control-surface back to method-ASK, which, as a repository, consumes it: a rectangular U under the two rungs, down from control-surface's bottom edge, along the lane and up into method-ASK's bottom edge, where a HOLLOW head marks the consumer. Different dash cadence AND terminal form from inheritance. The label sits beside the U's first leg. */ edges.append(line(`M ${X.c3 + 96} ${Y.rowA + NH} L ${X.c3 + 96} ${Y.confRun} L ${CX.c2} ${Y.confRun} L ${CX.c2} ${Y.rowA + NH + AH}`, 'edge e-conform', 'conformance')); edges.append(hollowU(CX.c2, Y.rowA + NH)); nodes.append(note(X.c3 + 112, Y.confLabel, 'conformance — the method REPO consumes the protocol')); /* MUTATION CHANNEL — exactly one. */ edges.append(line(`M ${X.c3 + NW} ${Y.rowB + 22} L ${X.c4 - 8} ${Y.rowB + 22}`, 'edge e-auth', 'mutation channel')); edges.append(headR(X.c4 - 4, Y.rowB + 22)); nodes.append(note(X.c3 + NW + 6, Y.mutLabel, 'ONE mutation channel')); /* EVIDENCE — thin, upward, OPEN heads, terminating at the GATE. The critic's return enters the gate's side; the project-cells return leaves the cells' bottom, takes the exterior gutter around domain authority, and rises into the gate from below. */ edges.append(line(`M ${X.c2 - 4} ${Y.rowC + NH/2 + 6} L ${X.role + NW + 4} ${Y.rowC + NH/2 + 6}`, 'edge e-evidence', 'critique')); edges.append(openL(X.role + NW, Y.rowC + NH/2 + 6)); edges.append(line(`M ${X.c3 + 40} ${Y.rowD + NH} L ${X.c3 + 40} ${Y.evRail} L ${X.ev} ${Y.evRail} L ${X.ev} ${GATE_Y + NH + 4}`, 'edge e-evidence', 'evidence return')); edges.append(openU(X.ev, GATE_Y + NH)); nodes.append(note(X.c2, Y.evLabel, 'evidence · critique · candidate correction')); /* AUTHORITY — the gate's adjudicated disposition, descending. It leaves the gate on the envelope's inner gutter, so it passes beside the roles rather than through their labels. */ edges.append(line(`M ${X.role} ${GATE_Y + 16} L ${X.env + 32} ${GATE_Y + 16} L ${X.env + 32} ${Y.dispRail} L ${CX.c4} ${Y.dispRail} L ${CX.c4} ${Y.rowA - 4}`, 'edge e-auth', 'disposition')); edges.append(headD(CX.c4, Y.rowA)); nodes.append(note(770, Y.dispRail - 12, 'ASK adjudicates · authorized disposition descends')); /* LEARNING — two exits from the disposition. */ edges.append(line(`M ${CX.c4} ${Y.rowA + NH} L ${CX.c4} ${Y.rowB - 4}`, 'edge e-auth', 'durable owner')); edges.append(headD(CX.c4, Y.rowB)); nodes.append(note(CX.c4 + 10, Y.grantLabel - 6, 'to the correct durable owner')); edges.append(line(`M ${X.c4 + NW} ${Y.rowA + NH/2} L ${X.c4 + NW + 60} ${Y.rowA + NH/2} L ${X.c4 + NW + 60} ${Y.dispRail + 12}`, 'edge e-auth', 'nomination')); edges.append(headU(X.c4 + NW + 60, Y.dispRail + 8)); nodes.append(note(X.c4 + 164, Y.dispRail - 12, 'nomination · hold ·')); nodes.append(note(X.c4 + 164, Y.dispRail + 6, 'no retention')); /* RELAY — cross-surface transfer through ASK: a gated break carrying a marker. The marker sits left of the descent gutter, so the crossing there is a crossing of two plain lines. */ const RY = Y.rowB + NH/2 + 2; edges.append(line(`M ${X.c2 + NW} ${RY} L ${X.c2 + NW + 20} ${RY}`, 'edge e-relay', 'relay')); edges.append(relayMark(X.c2 + NW + 30, RY)); edges.append(line(`M ${X.c2 + NW + 40} ${RY} L ${X.c3 - 8} ${RY}`, 'edge e-relay', 'relay')); edges.append(headR(X.c3 - 4, RY)); nodes.append(note(X.c2 + 154, Y.rowB + NH + 20, 'relay through ASK')); /* POINTER — the continuity rail, returning to the field entry. */ edges.append(line(`M ${CX.c4} ${Y.rowC + NH} L ${CX.c4} ${Y.ptrRail} L ${CX.c3} ${Y.ptrRail} L ${CX.c3} ${EXB + 4}`, 'edge e-pointer', 'continuity')); edges.append(headU(CX.c3, EXB)); nodes.append(note(CX.c3 + 14, Y.ptrLabel, 'continuity · fresh context >> bootstrap + index >> named owners >> current work >> lineage')); /* INHERITANCE — the design axis reaching its named consumers, and the scaffold's edge to the membrane. PINNED, NEVER HOT-LINKED; no universal byte-identity is asserted. */ edges.append(line(`M ${X.rail - 4} ${Y.rowD + NH/2 + 2} L ${X.c4 + NW + 4} ${Y.rowD + NH/2 + 2}`, 'edge e-inherit', 'design axis')); edges.append(headL(X.c4 + NW, Y.rowD + NH/2 + 2)); edges.append(line(`M ${X.rail - 4} ${Y.routesRail} L ${X.c3 + 190} ${Y.routesRail} L ${X.c3 + 190} ${Y.rowD + NH + 4}`, 'edge e-inherit', 'owner-declared routes')); edges.append(headU(X.c3 + 190, Y.rowD + NH)); nodes.append(note(X.c3 + NW + 6, Y.routesLabel, 'owner-declared routes · pinned, never hot-linked')); edges.append(line(`M ${CX.c4} ${Y.rowE + NH} L ${CX.c4} ${GRANT_Y - 4}`, 'edge e-inherit', 'scaffold to membrane')); edges.append(headD(CX.c4, GRANT_Y)); /* ===== the figure's core non-identity, stated on the canvas — bottom band ===== */ nodes.append(sect(X.c2, Y.cap1, 'evidence return != authority ascent — upward edges are visibly weaker, carry open heads, and terminate at the gate', 'start')); nodes.append(sect(X.c2, Y.cap2, 'ecology-ASK · bounded realization field — membership is FUNCTIONAL, not architectural', 'start')); /* ===== production circuit, named along the field ===== */ nodes.append(sect(X.c3 + 20, Y.prodTag, 'production · intent / aperture >> authorized realization >> selection + closure >> governance', 'start')); /* =========================================================================== KEY — both keys are drawn INSIDE the SVG, in one aligned grid below the membrane. Why here. (1) It must be visible in the live HTML and in BOTH page exports; SVG content is the only surface all three share. (2) A key drawn from the SAME classes as the apparatus cannot drift from it: each chip and each line sample below is literally the treatment it names. At source-v1 the edge grammar sat in an HTML legend panel whose line samples were inline SVG; the exporter rebuilds that panel from box swatches, so the committed raster showed six class names and no line to match them. (3) With no corner legend panel, the page export's panel band is the caption's. Row 1 names the three functions. Then the owner's form for what is NOT a function: a dotted, legend-only divider, and ONE neutral exemplar under its own heading — naming the human ASK actor envelope and the downstream governance objects; those exclusions are the load-bearing part of the binding, and they are never a fourth color. Rows 3-4 name the six edge classes; each sample is long enough to show its dash cadence and carries the terminal the figure uses. =========================================================================== */ const COL = [180, 800, 1280], CHIP_W = 22, CHIP_H = 12; edges.append(line(`M ${X.outer0} ${Y.keyRule} L ${X.outer1} ${Y.keyRule}`, 'section-rule')); /* One label column per key column: chip rows and edge rows both start their text at COL + 64, and a chip sits centered on the 56-unit span the line samples occupy. */ const chip = (x, y, cls) => nodes.append(el('rect', { x: x + 28 - CHIP_W/2, y: y - CHIP_H/2, width: CHIP_W, height: CHIP_H, rx: 3, ry: 3, class: cls })); const chipEntry = (c, y, cls, label) => { chip(COL[c], y, cls); nodes.append(note(COL[c] + 64, y, label)); }; const edgeEntry = (c, y, draw, label) => { draw(COL[c], y); nodes.append(note(COL[c] + 64, y, label)); }; const [K1, K2, K3, K4] = Y.key; nodes.append(sect(X.outer0, K1, 'three functions', 'start')); chipEntry(0, K1, 'node-box fn-leg', 'legislative — source-of-intent role (constitutive) · grants (delegated)'); chipEntry(1, K1, 'node-box fn-exe', 'executive — executor · realization'); chipEntry(2, K1, 'node-box fn-jud', 'judicial — adjudication / disposition'); /* the owner's dotted, legend-only divider: the three functions above it, the one neutral exemplar below it */ edges.append(line(`M ${X.outer0} ${Y.keyDivider} L ${X.outer1} ${Y.keyDivider}`, 'key-divider')); nodes.append(sect(X.outer0, K2, 'not a fourth function', 'start')); chipEntry(0, K2, 'node-box', 'NEUTRAL — the human ASK actor envelope · artifact governance · governed artifact'); nodes.append(sect(X.outer0, K3, 'edge grammar', 'start')); edgeEntry(0, K3, (x, y) => { edges.append(line(`M ${x} ${y} L ${x+47} ${y}`, 'edge e-auth')); edges.append(headR(x+56, y)); }, 'authority — intent · grant · adjudication'); edgeEntry(1, K3, (x, y) => { edges.append(line(`M ${x} ${y} L ${x+47} ${y}`, 'edge e-inherit')); edges.append(headR(x+56, y)); }, 'inheritance — vendoring, pinned, never hot-linked'); edgeEntry(2, K3, (x, y) => { edges.append(line(`M ${x} ${y} L ${x+47} ${y}`, 'edge e-conform')); edges.append(hollowR(x+56, y)); }, 'conformance — control-surface protocol >> method-ASK repo · HOLLOW head'); edgeEntry(0, K4, (x, y) => { edges.append(line(`M ${x+1} ${y} L ${x+56} ${y}`, 'edge e-evidence')); edges.append(openL(x, y)); }, 'evidence — pressure · critique · OPEN head, never filled'); edgeEntry(1, K4, (x, y) => { edges.append(line(`M ${x} ${y} L ${x+20} ${y}`, 'edge e-relay')); edges.append(relayMark(x+26, y)); edges.append(line(`M ${x+32} ${y} L ${x+47} ${y}`, 'edge e-relay')); edges.append(headR(x+56, y)); }, 'relay — cross-surface transfer through ASK · gated break'); edgeEntry(2, K4, (x, y) => { edges.append(line(`M ${x} ${y} L ${x+47} ${y}`, 'edge e-pointer')); edges.append(headR(x+56, y)); }, 'pointer — live state resolving to its owner'); /* ===== the viewBox: the drawn content plus a uniform margin, so the export frames the drawing rather than the fixed canvas — widened only as PAGE EXPORT, below, describes ===== */ /* PAGE EXPORT. The DS exporter (export-png.js) scales a landscape figure to the page's content width, 3760 px, and places it below the caption panel only while its scaled height fits the 2128 px band beneath the panel (page y 656..2784); otherwise it centers the figure in the full band and, when a drawn mark then comes within the panel gutter, refits the whole figure below the panel band at the smaller scale that band allows. For a figure of height H units, 2128 / H px per unit is therefore the largest scale that keeps it below the caption, whatever its width. When the drawn content is taller than that band allows at its own width, the VIEWBOX — not the drawing — is widened, evenly on both sides and with nothing drawn in the added margin, to the width at which the exporter takes its below-caption branch with 4 px to spare. The width selects the placement; the height sets the scale. The two constants are the exporter's page geometry for this page's current caption panel (band top 656 = overlay 288 + panel 320 + gutter 48; band bottom 2784): a change to the caption text or its fonts, or to the exporter's page geometry, moves them. The page export is measured, not assumed, and is re-measured whenever any of those changes. */ const VB_PAD = 12, EXPORT_CONTENT_W = 3760, EXPORT_BELOW_CAPTION_H = 2124; const bb = svg.getBBox(); const vbY = Math.floor(bb.y - VB_PAD), vbH = Math.ceil(bb.height + 2*VB_PAD); let vbX = Math.floor(bb.x - VB_PAD), vbW = Math.ceil(bb.width + 2*VB_PAD); const exportW = Math.ceil(vbH * EXPORT_CONTENT_W / EXPORT_BELOW_CAPTION_H); if (vbW < exportW) { vbX -= Math.floor((exportW - vbW) / 2); vbW = exportW; } svg.setAttribute('viewBox', `${vbX} ${vbY} ${vbW} ${vbH}`); svg.setAttribute('width', vbW); svg.setAttribute('height', vbH); /* ---- pan / zoom / fit (the camera is local; FIT and gestures are DS-owned) ---- */ const wrap = document.getElementById('canvasWrap'), stage = document.getElementById('stage'), pct = document.getElementById('zoomPct'); if (wrap && stage) { let tx=0, ty=0, sc=1; const BASE_MIN_SCALE = 0.2; let fittedMinScale = BASE_MIN_SCALE; /* FIT MODE (design-system-ASK responsive chrome). The view is at Fit after fit() and until the reader zooms, pans or pinches. Only a view at Fit follows a resize, the webfonts landing, or `diagram-chrome-change` from diagrams-chrome.js (a panel opening or closing, the compact layout switching, its control band changing). A reader's own pan and zoom is not reset by a browser-toolbar resize or a disclosure. */ let atFit = true; const apply = () => { stage.style.transform = `translate(${tx}px,${ty}px) scale(${sc})`; if (pct) pct.textContent = Math.round(sc*100)+'%'; }; /* Caller-owned: clearance 90 and maxScale 1.3. Zero-origin bounds — the transform target is the stage div, whose box starts at 0 in CSS space. */ const fitResult = () => window.DIAGRAM_FIT.compute({ wrap, bounds: { minX: 0, minY: 0, maxX: vbW, maxY: vbH }, clearanceX: 90, clearanceY: 90, maxScale: 1.3, gutter: 26, /* v3 (diagrams-fit.js): centre vertically between the chrome above and below the figure, and cap the clearance while the responsive chrome is compact. */ balance: true, compactClearance: 32 }); const applyFit = (f) => { fittedMinScale = Math.min(BASE_MIN_SCALE, f.scale); sc = f.scale; tx = f.tx; ty = f.ty; atFit = true; apply(); }; /* The page's responsive chrome (diagrams-chrome.js): its open compact panel, the Fit the reader returns to when that panel closes, and its one truthful way to close it. An open panel moves the drawing only where the drawing keeps its closed-panel size; otherwise the drawing keeps that closed-panel Fit and the panel overlays it until it closes. */ const chrome = window.DIAGRAM_CHROME || null; /* FAIL-CLOSED with the chrome: its edge declaration needs diagrams-fit.js v2. */ if (chrome && !(window.DIAGRAM_FIT.VERSION >= 2)) throw new Error('The responsive chrome needs the current diagrams-fit.js. Re-vendor it with diagrams-chrome.js.'); const panelOpen = () => !!(chrome && chrome.openPanel(wrap)); const closedFit = () => (chrome ? chrome.withoutOpenPanel(wrap, fitResult) : fitResult()); const keepsSize = (f, c) => f.clear && f.scale >= c.scale * (1 - 1e-6); const fitAround = () => { const f = fitResult(); if (!panelOpen()) return f; const c = closedFit(); return keepsSize(f, c) ? f : c; }; /* FIT, the reader's request, always ends at a usable fitted view: when the open compact panel would cover the drawing, Fit closes it through the chrome's state controller and fits against the chrome that remains. */ const fit = () => { if (panelOpen()) { const f = fitResult(); if (keepsSize(f, closedFit())) { applyFit(f); return; } chrome.close(wrap); } applyFit(fitResult()); }; /* A resize, the webfonts landing or a chrome change refits only a view at Fit, by the same open-panel rule. */ const refitAtFit = () => { if (atFit) applyFit(fitAround()); }; fit(); const fonts = document.fonts; if (fonts && fonts.ready && typeof fonts.ready.then === 'function') { fonts.ready.then(refitAtFit).catch(() => {}); } window.addEventListener('resize', refitAtFit); wrap.addEventListener('diagram-chrome-change', refitAtFit); const zi=document.getElementById('zoomIn'), zo=document.getElementById('zoomOut'), zf=document.getElementById('zoomFit'); /* The view leaves Fit only when the reader moves it: a zoom at its limit changes nothing, and a press that stays inside the pointer controller's tap slop is a tap. */ const zoomTo = (s) => { if (s === sc) return; sc = s; atFit = false; apply(); }; if (zi) zi.onclick=()=>zoomTo(Math.min(sc*1.2,4)); if (zo) zo.onclick=()=>zoomTo(Math.max(sc/1.2,fittedMinScale)); if (zf) zf.onclick=fit; /* GESTURES through the design-system pointer controller (diagrams-pointer.js, checked at the top of this file): one pointer pans from anywhere on the drawing, two pinch about their centroid, a wheel zooms about the pointer. The canvas carries `touch-action: none` (diagrams.css), so a touch gesture there moves the drawing, never the page. A press on the HUD or the chrome block stays native, and a wheel over the chrome block scrolls an open panel unless it is a trackpad pinch (ctrlKey). A moved gesture swallows its own click. */ const clampK = (k) => Math.max(fittedMinScale, Math.min(4, k)); window.DIAGRAM_POINTER.attach({ stage: wrap, exclude: '.hud, .legend, .caption, .diagram-info', wheelStep: 1.1, clampK, getView: () => ({ k: sc, x: tx, y: ty }), setView: (v) => { /* A gesture that moves the view takes it off Fit; float noise from a still pinch does not. */ if (Math.abs(v.k - sc) > 1e-9 * sc || Math.abs(v.x - tx) > 1e-6 || Math.abs(v.y - ty) > 1e-6) atFit = false; sc = v.k; tx = v.x; ty = v.y; apply(); }, zoomAt: (f, mx, my) => { const ns = clampK(sc*f); if (ns === sc) return; const k = ns/sc; tx = mx-(mx-tx)*k; ty = my-(my-ty)*k; sc = ns; atFit = false; apply(); } }); } })();