Skip to content

feat(admin): buildGraphModel, the graph's edge-honesty fold (issue #54) - #167

Merged
edgehero merged 1 commit into
mainfrom
feat/graph-model-assembler
Aug 11, 2026
Merged

feat(admin): buildGraphModel, the graph's edge-honesty fold (issue #54)#167
edgehero merged 1 commit into
mainfrom
feat/graph-model-assembler

Conversation

@edgehero

Copy link
Copy Markdown
Owner

Third slice of issue #54: the pure assembler every graph consumer renders from. Stacked on #166 (merged).

What

buildGraphModel (admin/src/graph-model.mjs) folds the read-model's outputs into one model, and the derivation rules are the design (DES-GRAPH-EDGE-DERIVATION, new):

  • Four evidence classes, a closed test-pinned vocabulary: config (every trigger naming a flow gets one, always — dangling, unverifiable and charset-invalid included), observed (records only, with count + last occurrence), potential (a text mention, labelled strong/eligible), cron-rearm (the one self-edge every cron trigger carries by definition).
  • The OQ-009 prohibitions are structural: no chain edge out of a forge trigger's flow, none across folders — the harness makes both unrepresentable, so drawing either would draw a lie.
  • Dangling is precise: no-skill only where enumeration succeeded and the path is absent at HEAD; an unreachable or remote folder renders unverified, never dangling; a run.flow failing SKILL_NAME_RE is its own charset-invalid flag (the gate answers deny for it, and deny proves nothing about existence).
  • Orphanhood is three facts: orphan, ai-reachable-no-trigger, injected-ai-trigger (the OQ-022 silent no-op, badged loudly). Sub-skills are never orphan candidates.
  • Gate eligibility is a node badge, never an all-pairs edge fabric — with N allow-listed skills that would be N×(N−1) identical arrows; a mention is the edge.
  • An observed edge that cannot hang on exactly one enumerated folder is dropped and counted (meta.droppedObservedEdges), never guessed onto a basename that merely matches.
  • An unenumerated cron folder (the folder cap) still draws its trigger and config edge on a group that says not-enumerated — the cap must not silently lose an edge.
  • Caps and honesty counters (unattributed runs, refusals, truncation) ride every model; consumers are required to render them.
  • The OQ-020 pr-spend-loop-risk badge: a pull_request trigger on opened/synchronize is the static signature of the cross-actor spend loop.

Specs (same PR)

NEW DES-GRAPH-EDGE-DERIVATION with the rejected alternatives recorded (declared chain topology; the all-pairs eligibility fabric; deny-as-dangling; first-match target resolution). DES-JOB-OUTBOX-CHAINING UNCHANGED, checked. DES-COST-FOLD-BY-SCAN UNCHANGED, checked (same scan, second consumer).

Tests

21 new assembler tests over one canned deployment: the closed-vocabulary literal pin, per-rule positives, and the negative claims as their own assertions (unreachable folder → zero dangling flags; a potential edge never carries a count; eligibility alone draws no edge; the dropped edge is counted). Suite in the CI posture: 2115 pass, 0 skipped; admin bundle builds.

One pure function assembles the model every graph consumer renders
from, and the derivation rules are the design: four evidence classes
(config, observed, potential, cron-rearm) from a closed test-pinned
vocabulary, the two OQ-009 structural prohibitions (no forge-parent
chain edges, no cross-folder chain edges: the harness makes both
unrepresentable, so drawing either would draw a lie), precise dangling
(no-skill only where enumeration succeeded; unverified is not
dangling; charset-invalid is its own flag because deny proves nothing
about existence), three-way orphanhood (orphan vs
ai-reachable-no-trigger vs injected-ai-trigger), the OQ-020
pr-spend-loop badge, and caps plus honesty counters (unattributed
runs, refusals, truncation, dropped edges) on every model.

Gate eligibility is a node badge, never an all-pairs edge fabric; a
mention is the edge, labelled strong/eligible. An observed edge that
cannot hang on exactly one enumerated folder is dropped and counted,
never guessed onto a basename that merely matches. An unenumerated
cron folder still draws its trigger and config edge on a group that
says not-enumerated, so the folder cap cannot silently lose an edge.

Specs: NEW DES-GRAPH-EDGE-DERIVATION with the rejected alternatives
recorded. DES-JOB-OUTBOX-CHAINING UNCHANGED, checked.
DES-COST-FOLD-BY-SCAN UNCHANGED, checked (same scan, second consumer).

Suite in the CI posture: 2115 pass, 0 skipped; admin bundle builds.

Signed-off-by: Rob Boerman <robboerman@live.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant