Repository navigation
fix(admin): the panel's trigger row shows every badge, and the post-mortem wraps (#482) - #486
Merged
Merged
Conversation
edgehero
force-pushed
the
fix/panel-badges-postmortem
branch
from
September 29, 2026 08:37
45f740d to
4229033
Compare
edgehero
added a commit
that referenced
this pull request
Sep 29, 2026
…ortem wraps (#482) The LIST trigger row showed [packages], [image], [resume], [xN], [once]/[spent] and the health badge, but not the [skills <basename>], [instructions] and [secrets N via <profile>] that render.mjs's triggerLine shows for the same trigger. Not a width decision: the read model has carried all four fields since #60 and #225, and those commits added the badges to render.mjs and the trust model without touching the row. [secrets] is the badge that says credentials reach the job, so its absence was the 0.1.4 defect again. Colours follow the file's own doctrine. [skills] and [instructions] are accent: both are operator-authored overrides of a deployment default, not third-party code. [secrets] is warning: it is what the job can REACH, and the drill-in's own secrets lines are already amber. Both surfaces take the skills basename from one skillsBasename in render.mjs. A row too wide for the frame gives way in a fixed order (fitTriggerRow). The floor is the cursor, the kind and the target with its flow; the risk badges ([packages], [resume], [xN], [secrets], health) must survive. What yields: the selector down to 8 columns, the free text inside a badge (image ref, secrets profile, skills basename) down to 6, the non-risk badges right to left, the selector entirely, the free text down to its ellipsis; room a later step freed is then given back. Only a row that still cannot fit gets the plain right-hand clip. The first version split at one fixed threshold and still lost [secrets] at 80 to 136 columns with a long image ref (review round 1 of PR #486). A row that fits renders byte-identically, pinned against goldens rendered from origin/main. RUN_DETAIL's post-mortem sentence is 86 columns in a 66-column pane and was cut at "+ optiona". It is now word-wrapped by columns through the styler's visibleLen, every line dimmed on its own. A word wider than the pane breaks at a grapheme boundary inside the column budget, so a joined emoji is never split onto a line starting with a bare U+200D. TRIGGER_DETAIL now shows the skills dir in full (render.mjs's comment said it lived there, and it did not), the instructions text, and a secrets row, so the three read alike on both surfaces. Long values wrap under a blank label; the instructions stop at six lines with the rest counted. The trust-model bullets wrap too ("the job holds no vau..." before). The instructions text is deliberately NOT on the display record: those records are what the model-callable dispatch_triggers tool returns, which is why they carry a boolean. read-model.mjs gains readTriggersWithInstructions, called only by the panel's deps factory: the same view plus the text by raw file index, from one read through readTriggers' own fs seam. The text is captured with the record when the pane opens, so a trigger deleted or moved under an open pane cannot show another trigger's words (review round 1). Nothing routes drill-in text elsewhere: the overlay resolves with an action and an index only. Every trigger renderer now scrubs the record at its door (scrubTrigger in render.mjs): the LIST row, TRIGGER_DETAIL, and renderTriggers, whose line the unframed degrade prints. The panes' gate keeps SGR runs because the styler emits them, so an SGR run in a label, phrase, image, flow or secrets profile painted the pane and pushed its line past the frame, and the degrade printed it raw at render(undefined) and render(NaN). The colour loss was general, so it is fixed at the rule. fitLine, and the frame's padVisible belt, clipped an over-wide line through styler.cell on its stripped text, so every overflowing line printed in the default colour. Both now use styler.clipStyled, which cuts each text run between SGR codes with the same columnsOf/sliceColumns the cell uses, strips a joiner left at the cut, closes the cut run with one reset, and falls back to the colourless cell on the one shape where the whole-line count exceeds the run-by-run count (issue #417). With no escape in the input it is byte-identical to the old clip. Specs: DES-ADMIN-VIA-PI-EXTENSION amended (LIST row badges and overflow order, TRIGGER_DETAIL rows and wrapping, RUN_DETAIL post-mortem wrap), with one revision row. Checked and unchanged: REQ-PER-TRIGGER-INSTRUCTION, REQ-PER-TRIGGER-SKILLS, INT-TRIGGERS-FILE-CONTRACT, the layout of render.mjs's triggerLine (only scrubbed now, and a segmentless skills dir reads "-" where it read "undefined"), styler.cell itself, and normalizeTriggerForDisplay, whose records still carry the instructions as a boolean only. Signed-off-by: Rob Boerman <robboerman@live.nl>
…ortem wraps (#482) The LIST trigger row showed [packages], [image], [resume], [xN], [once]/[spent] and the health badge, but not the [skills <basename>], [instructions] and [secrets N via <profile>] that render.mjs's triggerLine shows for the same trigger. Not a width decision: the read model has carried all four fields since #60 and #225, and those commits added the badges to render.mjs and the trust model without touching the row. [secrets] is the badge that says credentials reach the job, so its absence was the 0.1.4 defect again. Colours follow the file's own doctrine. [skills] and [instructions] are accent: both are operator-authored overrides of a deployment default, not third-party code. [secrets] is warning: it is what the job can REACH, and the drill-in's own secrets lines are already amber. Both surfaces take the skills basename from one skillsBasename in render.mjs. A row too wide for the frame follows one rule (fitTriggerRow). Its floor is its identity and is never given away: cursor, kind, the target with its flow, and the selector's first 4 columns plus an ellipsis (a cron row's id). With it, the risk badges ([packages], [resume], [xN], [secrets], health) always stay. The row is taken to its minimum (selector at its floor, the text inside every badge at its ellipsis), the non-risk badges drop right to left only if that still does not fit, and the room left is given back to the selector, then the risk badges' text, then the other badges' text. Below the floor plus the risk badges' minimum, which the spec states as a formula, the plain right-hand clip applies. Review of PR #486 refuted two earlier rules: a fixed threshold that still lost [secrets] at 80 to 136 columns, then a give-way order under which two rules differing only in label rendered as one row. A row that fits renders byte-identically, pinned against goldens rendered from origin/main. RUN_DETAIL's post-mortem sentence is 86 columns in a 66-column pane and was cut at "+ optiona". It is now word-wrapped by columns through the styler's visibleLen, every line dimmed on its own. A word wider than the pane breaks at a grapheme boundary inside the column budget, so a joined emoji is never split onto a line starting with a bare U+200D. TRIGGER_DETAIL now shows the skills dir in full (render.mjs's comment said it lived there, and it did not), the instructions text, and a secrets row, so the three read alike on both surfaces. Long values wrap under a blank label; the instructions stop at six lines with the rest counted. The trust-model bullets wrap too ("the job holds no vau..." before). The instructions text is deliberately NOT on the display record: those records are what the model-callable dispatch_triggers tool returns, which is why they carry a boolean. read-model.mjs gains readTriggersWithInstructions, called only by the panel's deps factory: the same view plus the text by raw file index, from one read through readTriggers' own fs seam. The text is captured with the record when the pane opens, so a trigger deleted or moved under an open pane cannot show another trigger's words (review round 1). Nothing routes drill-in text elsewhere: the overlay resolves with an action and an index only. Every trigger renderer now scrubs the record at its door (scrubTrigger in render.mjs): the LIST row, TRIGGER_DETAIL, and renderTriggers, whose line the unframed degrade prints. The panes' gate keeps SGR runs because the styler emits them, so an SGR run in a label, phrase, image, flow or secrets profile painted the pane and pushed its line past the frame, and the degrade printed it raw at render(undefined) and render(NaN). The colour loss was general, so it is fixed at the rule. fitLine, and the frame's padVisible belt, clipped an over-wide line through styler.cell on its stripped text, so every overflowing line printed in the default colour. Both now use styler.clipStyled, which cuts each text run between SGR codes with the same columnsOf/sliceColumns the cell uses, strips a joiner left at the cut, closes the cut run with one reset, and falls back to the colourless cell on the one shape where the whole-line count exceeds the run-by-run count (issue #417). With no escape in the input it is byte-identical to the old clip. Specs: DES-ADMIN-VIA-PI-EXTENSION amended (LIST row badges and overflow order, TRIGGER_DETAIL rows and wrapping, RUN_DETAIL post-mortem wrap), with one revision row. Checked and unchanged: REQ-PER-TRIGGER-INSTRUCTION, REQ-PER-TRIGGER-SKILLS, INT-TRIGGERS-FILE-CONTRACT, the layout of render.mjs's triggerLine (only scrubbed now, and a segmentless skills dir reads "-" where it read "undefined"), styler.cell itself, and normalizeTriggerForDisplay, whose records still carry the instructions as a boolean only. Signed-off-by: Rob Boerman <robboerman@live.nl>
edgehero
force-pushed
the
fix/panel-badges-postmortem
branch
from
September 29, 2026 09:10
4229033 to
dbea180
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.
Closes #482.
The panel showed the same trigger differently from
/dispatch triggers, and clipped lines lost their colour.[skills <dir>]and[instructions](accent, operator overrides of a default) and[secrets N via <profile>](warning, it changes what a job can reach), inrender.mjs's order, with the skills basename from one sharedskillsBasename. A row too wide for the pane never gives away its identity (cursor, kind, the target with its flow, and the selector's first 4 columns, or a cron row's id) nor its risk badges ([packages],[resume],[xN],[secrets], health): the text inside every badge is cut to its ellipsis first, non-risk badges drop only if that is still too wide, and the room left goes back to the selector, then the risk badges' text, then the rest. Below that floor plus the risk badges' minimum (a formula in the spec, not a fixed width) the plain right-hand clip applies. Rows that fit are byte identical to before.dispatch_triggersreturns to the model, and it is captured with the record when the pane opens, so an edit to the file under an open pane cannot show another trigger's text.fitLineand the frame's fallback stripped styling before clipping, so every overflowing line in the panel lost its colour. Onestyler.clipStylednow clips between codes, strips a joiner left at the cut, and resets once at the end.render.mjs'srenderTriggers) now scrubs the whole trigger record on entry (scrubTrigger), one rule rather than a fix per field.Review round 1 corrected the first overflow rule (a fixed threshold that still dropped
[secrets]at common widths), the drill-in's live read of the instructions, the unscrubbedrenderTriggers, and a joiner before the ellipsis. Round 2 replaced the give-way order that followed with the floor rule above, after two rules differing only in their label rendered as one row; twin tests now differ only in selector, and only in cron id, across widths 60 to 200.DES-ADMIN-VIA-PI-EXTENSIONamended with one revision row. New tests inadmin/test/dashboard-badges.test.mjs, every new branch mutation checked. Full suite: 5198 tests, 0 fail.