Repository navigation
fix: compose gate read the wrong directory; report CSS escape emitted the wrong glyph - #4
Merged
Merged
Conversation
`ComposeChains.ts` resolved its analysis directory with:
argv.find((a) => !a.startsWith("--") && a !== argv[depthArg + 1])
When `--max-depth` is not passed, `depthArg` is -1, so `argv[depthArg + 1]`
is `argv[0]` — the positional directory itself. The directory was therefore
filtered out of its own lookup and `dir` fell back to `"."`.
The visible symptom is worse than a wrong path: `stpa run` invokes compose
without `--max-depth`, so the gate read the *current working directory*,
found no `remediation.json`, and printed
missing remediation.json in <cwd> — run `stpa plan` first
!! UNRATED COMPOSITION (exit 1).
against an analysis whose composition was fully rated. A gate that reports
a failure it did not actually observe is worse than one that stays quiet,
because the banner it puts on the report is false.
Guarding on `depthArg !== -1` changes behaviour only in the two broken
cases (no `--max-depth`). Verified against all argv permutations, including
`--max-depth 4` with no positional directory, which still correctly
resolves to "." rather than treating "4" as the directory.
…t \x15B8
The collapsible-section marker was written as a single-backslash escape
inside the `html` template literal:
content:"\25B8"
`\25` is consumed by the *JavaScript* parser before CSS ever sees it, so the
emitted stylesheet did not contain the intended `\25B8` (U+25B8 ▸). Where a
parser accepts it as a legacy octal escape it yields U+0015 followed by the
literal characters `B8`, so the rule becomes `content:"\x15B8"` and the
triangle renders as a control character rather than a marker.
Template literals also forbid legacy octal escapes outright, so a
spec-compliant parser rejects the file at parse time rather than
mis-rendering it — which is how this surfaced.
Doubling the backslash emits the two characters `\` `2` `5` `B` `8` into the
CSS, which is what the CSS escape needs. Verified in a rendered REPORT.html:
the stylesheet now contains content:"\25B8".
Note: I could not exercise this under Bun (not available in the environment
where it was found), so the Bun-side rendering claim above is derived from
the escape semantics rather than observed. The emitted-CSS fix is verified.
This was referenced Jul 25, 2026
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.
Two bugs found while running a full-surface analysis against a large TypeScript monorepo (~3,600 files, 35 control actions, 140 cells). Both were hit in the course of a normal
stpa run, and one of them put a false banner on a finished report, which is why I'd treat it as the more urgent of the pair.1 ·
ComposeChains.tsdiscarded its own directory argumentWith
--max-depthabsent,depthArgis-1, soargv[depthArg + 1]isargv[0]— the positional directory itself. It was filtered out of its own lookup anddirfell back to".".stpa runcalls compose without--max-depth, so the gate read the current working directory, found noremediation.json, and printed:…against an analysis whose composition was fully rated (0 upgrades, 0 band changes when invoked with an explicit
--max-depth). A gate reporting a failure it did not observe is worse than one that stays silent, because the banner it stamps on the report is false — and this gate exists precisely to stop an under-rated band reaching a reader.Guarding on
depthArg !== -1changes behaviour only in the two broken cases. Verified across argv permutations:["dir","--check"].dir✅["dir"].dir✅["dir","--check","--max-depth","4"]dirdir["--check","--max-depth","4"]..["--max-depth","4","dir"]dirdir[]..Note the fourth row:
"4"is still correctly not mistaken for the directory.2 · The disclosure-triangle escape never reached CSS
\25is consumed by the JavaScript parser before CSS sees it, so the emitted stylesheet never contained\25B8(U+25B8 ▸). Where a parser accepts it as a legacy octal escape it yields U+0015 followed by the literalB8, making the rulecontent:"\x15B8"— a control character instead of a marker. Template literals also forbid legacy octal escapes outright, so a spec-compliant parser rejects the file at parse time rather than mis-rendering it, which is how it surfaced.Doubling the backslash emits
\25B8into the stylesheet. Verified in a renderedREPORT.html.One honest caveat, stated in the commit too: I could not exercise this under Bun in the environment where I found it, so the "what Bun renders" claim is derived from escape semantics rather than observed. The emitted-CSS fix itself is verified.
Deliberately not included
bun→nodeshim I used locally. That's an environment workaround, not a toolkit change, andCONTRIBUTING.mdis explicit that this is a Bun project — making it runtime-agnostic is a scope decision for you, not something to smuggle into a bugfix PR.Tools/UcaGrid.tsreadJson()callsBun.file(path)and then discards the result (void f) while reading viafs.readFileSync. Harmless dead code, but it's the only thing forcing a Bun global in that file. Happy to strip it in a separate PR if you want it gone.No dependencies added; no behaviour changed beyond the two defects.