feat(admin): spend per trigger and per repo, and the COSTS table cycle (issue #175) - #177
Merged
Merged
Conversation
…e (issue #175) The second insights slice: the joins the graph already owned become the cost breakdowns the fold was missing. - attributeRunsToTriggers (read-model, beside joinRunsToTriggers): the per-jobId join for the cost fold -- cron via the raw repeat jobId grammar cronRunStats already uses, forge via the index+type agreement doctrine. A forge record whose pair disagrees with the current file (or predates the field) gets an explicit unattributed entry, never silence, so the fold cannot misfile it under manual. - foldCosts gains triggerJoin -> byTrigger (typed costs, outcome split, failedCost, honesty buckets chained/manual/unattributed pinned to the tail) and byRepo (repoOfTarget, the one stripping grammar, now also called by forgeRepoTargets). byTrigger is null without a join: not computed and nothing attributed are different sentences. - foldTriggerCosts: the per-trigger spend map keyed by the graph node id trigger:<index>, for the topology surfaces the next slices add. - The COSTS view's f cycles flow/model/trigger/repo; footer hint renamed [f] table so width 80 still fits whole; w stays flow-table-only. /dispatch costs renders the two new tables; the trigger join is one file read, so the 10s stale-gated poll piggyback policy stands untouched. Specs in the same PR: REQ-COST-ANALYTICS and DES-COST-FOLD-BY-SCAN and DES-ADMIN-VIA-PI-EXTENSION amended; REQ-TOPOLOGY-GRAPH UNCHANGED, checked (its join doctrine gained a second consumer, not a second definition); DES-GRAPH-EDGE-DERIVATION UNCHANGED, checked. Suite green in the CI posture: 2194 tests, 0 skipped, live Valkey. Signed-off-by: Rob Boerman <robboerman@live.nl>
edgehero
force-pushed
the
feat/cost-trigger-repo-folds
branch
from
August 12, 2026 07:26
41ac08d to
e6831aa
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Second slice of #175, stacked on #176: the joins the graph already owned become the cost breakdowns the fold was missing. The diff collapses to this slice once #176 merges.
What
attributeRunsToTriggers(read-model, besidejoinRunsToTriggers): the per-jobId join for the cost fold. Cron attributes via the rawrepeat:<id>:<millis>grammarcronRunStatsalready uses (digits-tail disambiguated); forge attributes via the index+type agreement doctrine. A forge record whose persisted pair disagrees with the current file, or predates the field, gets an explicitunattributedentry rather than silence, so the fold cannot misfile it under manual.triggerMatchLabelis exported from graph-model so the label vocabulary keeps one home.foldCostsgainstriggerJoin→byTrigger: typed costs per trigger with an outcome split andfailedCost(a trigger whose spend is mostly failures is a different problem than an expensive one), and the honesty buckets(chained runs)/(manual/local)/(unattributed)pinned to the table tail in fixed order however the dollars compare. Chained spend is deliberately NOT rolled up to the ancestor trigger: a parent chain walked across the retention boundary attributes partially, and a partial rollup wearing a trigger's name would lie.byTriggeris null (not[]) without a join: "not computed" and "nothing attributed" are different sentences.byRepovia the new sharedrepoOfTargetgrammar;forgeRepoTargetsnow calls the same function, so the graph's repo list and the cost table can never disagree on what a repo is.foldTriggerCosts: the per-trigger spend map keyed by the graph node idtrigger:<index>, for the topology surfaces the next slices add.fcycles four tables (flow / model / trigger / repo); the footer hint becomes[f] tableso width 80 still fits whole;wstays flow-table-only./dispatch costsrenders the two new tables;dispatch_costsreturns the same fold, so its JSON gains the arrays additively, every dollar still classed.rposture is not involved at all.Specs
REQ-COST-ANALYTICSAMENDED: the rollup list gains per-trigger and per-repository-target; two acceptance rows (the disagreeing-pair bucket; nullbyTriggerrenders as absence).DES-COST-FOLD-BY-SCANAMENDED: the join is passed IN (the injected-pricing pattern); "the fold re-deriving the join" joins the Rejected list.DES-ADMIN-VIA-PI-EXTENSIONAMENDED: the four-table cycle, and why the extra file read is tick-safe.REQ-TOPOLOGY-GRAPHUNCHANGED, checked: its join doctrine gained a second consumer, not a second definition.DES-GRAPH-EDGE-DERIVATIONUNCHANGED, checked. Nodispatch_costsshape pin exists ininterfaces.md(checked), so the additive fold fields need no INT row.Tests
Suite green in the CI posture (2194 tests, 0 skipped, live Valkey). New pins: the byTrigger attribution ladder (cron jobId, agreeing forge pair, refused pair → unattributed, chained via parentJobId, manual), tail ordering,
failedCosttyping, byRepo grouping includinglocal:and the no-target bucket,repoOfTargetunit cases,foldTriggerCostsmapping only real triggers,attributeRunsToTriggersmirroring the adversarial join cases (type shift, out-of-range, pre-field), the four-tablefcycle withwinert off the flow table, null-byTrigger degrade, the new tables in plain text, and the 80-col SGR invariant re-run per table.Stacked on #176.