Skip to content

Signal Range - #203

Merged
thkruz merged 131 commits into
mainfrom
dev
Sep 26, 2026
Merged

thkruz merged 131 commits into
mainfrom
dev

Conversation

@thkruz

@thkruz thkruz commented Sep 26, 2026 •

Copy link
Copy Markdown
Owner

This pull request modernizes and hardens the project's CI/CD pipeline, improves developer experience, and enforces better code and commit hygiene. Key changes include switching from npm to pnpm for dependency management, updating and simplifying workflow steps, adding support for a private submodule, and introducing stricter commit message validation.

CI/CD Pipeline Modernization and Dependency Management:

  • Switched from npm to pnpm for dependency installation and scripts throughout all GitHub Actions workflows, improving speed and consistency. Updated Node.js version to 24 and setup actions to latest versions. [1] [2] [3] [4] [5] [6] [7] [8] [9]
  • Replaced ESLint with Biome for linting, updating scripts and workflow output accordingly. [1] [2]
  • Updated all uses of actions/checkout and actions/setup-node to their latest major versions, and disabled submodule recursion except where explicitly needed. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12]

Private Submodule Integration:

  • Added src/private as a git submodule for private code, with secure SSH key handling in deploy and preview workflows. [1] [2] [3]
  • Updated build and verification workflows to ensure src/private is excluded from open source builds and only included where appropriate. (F4f0eb36L4R4, [1] [2]

Code Quality and Commit Hygiene:

  • Added a .husky/commit-msg hook to enforce a strict, conventional commit message format, improving commit history readability and automation.
  • Added a comprehensive .gitattributes file to standardize line endings and file handling across platforms, preventing Windows/Unix line ending issues.
  • Removed the .eslintrc file, as linting is now handled by Biome.

Static Analysis and Linting Configuration:

  • Updated .deepsource.toml to refine test and exclude patterns, and to better reflect the project's structure and environments.

Minor Improvements:

  • Updated documentation and workflow messages to reference pnpm instead of npm where relevant.

These changes collectively improve the project's maintainability, security, and developer experience by using modern tooling and enforcing stricter standards.## Description

Testing

  • Unit tests pass (npm test)
  • E2E tests pass locally (pnpm run test:e2e)

thkruz added 30 commits July 2, 2026 06:22
… configs

Opt-in gainModel: 'fixed' renders wire antennas (QFH, crossed yagi, GPS patch) with explicit gain/HPBW instead of parabolic aperture math, capped off-axis at a front-to-back ratio. Wide-beam antennas accept satellites out to one HPBW using true great-circle separation (the planar +/-1 deg box breaks near zenith). Adds circular handedness selection with config-driven cross-pol loss, VHF/UHF band entries, and three DIY antenna configs. All changes are opt-in; parabolic antennas are bit-identical.
…ass-through

isDirectSampling bypasses the LNB mixer (RF = IF) with an RTL-SDR-style 24-1766 MHz passband. An OMT configured for LHCP/RHCP becomes a pass-through so handedness discrimination is charged once, at the antenna feed, never double-counted. Both opt-in; legacy H/V and block-downconversion paths unchanged.
Opt-in isAfcEnabled slews the modem center toward the received carrier (200 Hz/tick, 10 Hz deadband) to track Doppler drift. Adds public handleModemFrequencyChange/handleModemConfigChange for UI layers (SDR Console). Defaults preserve fully manual tuning.
…ndwidth with PSD scaling

getInputSignals() clamped every displayed signal's bandwidth to the RBW, so a 2 MHz GPS hump at 30 kHz RBW drew as a 30 kHz needle (and a 36 MHz video at 1 MHz RBW as a 1 MHz spike). RBW sets resolution and the displayed noise floor, not a signal's occupied width. Removes the clamp and renders signals wider than the RBW at per-RBW-bin power (PSD), so widths and heights are both honest against the per-RBW noise floor. Display-only; no physics or objective changes. Also exposes the spectrum data processor for campaign UIs.
…ion tab set

Opt-in stationClass: 'backyard' replaces the professional tab set with SDR Console + Observations (Pass Schedule reskin; gating broadened to any OrbitalSatellite so MEO birds appear). The console drives the same headless cores as the professional tabs: spectrum trace with dB axis, waterfall with VFO passband overlay and band plan ribbon, digit-wise VFO readout, click/scroll tuning, bookmarks from scenario downlinks, mode/FEC/BW controls, AFC, polarization switch, rotctl-style rotator panel, RF gain with real ADC clipping/quantization consequences, and a decoder with RDS-style ID. S-meter reads passband energy when the demod has no carrier and the lock indicator self-diagnoses (BW TOO WIDE / BW TOO NARROW / MODE?). NOTE: audio demodulation is a future phase (see header comment). Professional stations render exactly as before.
Activates the ham-sdr campaign stub as 'Backyard Operator': Riley Brooks (KD2RLY) teaches satellite tracking from a Burlington VT backyard. Three receive-only stations (137 MHz QFH, 70cm crossed yagi on a TV rotator, GPS L1 patch) with direct-sampling SDR chains, and three authored-TLE birds (WXSAT-19 APT at AOS T+3, CUBEHOP-1 FM at T+18, NAVSTAR-77 GPS overhead from T+0) validated by deterministic pass tests. Sandbox objectives exercise every new mechanic: observations list, QFH first contact + decode, yagi program-track with RHCP handedness, Doppler chase/AFC, and GPS spread-spectrum detection. Checks in the TLE grid-search authoring tool (C2 retro action item) and fixes the duplicate 'ccs' campaign id on the geolocation stub.
body.campaign-ham-sdr reskins the app as 2000s hobbyist freeware: phosphor-green accent, Verdana/Tahoma system fonts, zero border radius, beveled outset/inset buttons. Scoped to the campaign body class; --mc-danger* untouched so faults stay red everywhere.
Phase plan, phase retrospective, and a cross-cutting post-mortem covering the four live-only defects (reception box, decode noise gate, RBW render clamp, mute lock failures) and ten lessons learned - including instrumenting over theorizing, recomputed-config traps, and self-diagnosing instruments.
…rence events

Optional emitter { latitude, longitude, altitudeKm } on InterferenceEventConfig
plus getEvent/getEventsWithEmitters query API. Absent by default, so existing
jamming scenarios (e.g. nats-scenario21) are unchanged; present only for
Campaign 5 geolocation gameplay.
Pure math core (no DOM/EventBus): TDOA/FDOA forward model over SGP4 satellite
positions, noisy measurement synthesis, weighted grid-search + Gauss-Newton
solver with a 95% error ellipse, and an lopResidual primitive for line-of-
position tracing. Unit-tested for exact and noisy recovery, ellipse tightening,
and clustered-capture latitude observability.
…ission Control tab

- GeolocationConsoleCore: correlator tuning, duty-cycle-gated capture, and an
  auto-capture toggle that retries across the interferer's on/off cycle.
- GeoMap: full-Earth basemap (earthmap4k.jpg) with scroll-zoom/drag-pan and
  viewport-traced TDOA/FDOA lines of position that stay solid at any zoom.
- Geolocation tab: correlator + captured-measurements side by side over a
  full-width map; registered only when settings.geolocation is present.
- SimulationSettings gains the opt-in geolocation block and the interference
  emitter field; console lifecycle wired into base-page/mission-control-page.

Opt-in throughout, so Campaigns 1-4 are unaffected.
…itions

New additive condition types geolocation-measurements-collected (minCount) and
geolocation-fix-accuracy (maxErrorKm), evaluated against GeolocationConsoleCore.
Existing evaluator cases untouched.
…eolocation sandbox

- Peterson Annex (PA-22) C-band station and two inclined-GEO OrbitalSatellite
  birds (SENTRY-7 victim, SENTRY-9 adjacent collector), TLEs authored against
  the scenario epoch by scripts/author-tle-signal-hunter.mjs. The 3.0/4.5 deg
  inclinations make the emitter latitude observable from clustered captures.
- Sandbox scenario: detect -> characterize duty cycle -> geolocate -> report,
  with a hidden West Texas emitter.
- Activates the campaign (id signal-hunter, replacing the duplicate ccs stub id).
Scoped body.campaign-signal-hunter block: near-black surfaces with a coyote
brown accent, distinct from Campaign 1 (red) and Campaign 2 (blue). Semantic
--mc-danger* left untouched so faults stay red in every campaign.
… spec

Registry test guards unique campaign ids (the old ccs collision) and the
Signal Hunter activation. E2E spec drives the console end to end: theme
applied, tab present, duty-cycle-gated captures, and a computed fix.
…d EA console

Activates the ccs campaign stub with offensive SATCOM-denial mechanics:
ElectronicAttackManager (player-driven jam chain with J/S assessment),
scheduled transmit-string hardware faults, own-force deconfliction
interlock (protected-frequency fratricide fail), jamming objective
conditions, EA Assessment tab, and the black-ops OD-green theme.
Eight opt-in mechanics for the nats-eu campaign, each gated by its own
SimulationSettings block so Campaigns 1/3/4/5 are unaffected:
- M1 LinkBudgetManager (Friis C/N worksheet + margin commit)
- M2/M5 CommandingManager (uplink Doppler, TT&C command queue, key ops)
- M3 ContactScheduleManager (multi-station pass allocation + conflicts)
- M4 SpaceEventManager (scheduled maneuver -> stale TLE -> reacquire)
- M6 SecurityConsoleCore (SOC-lite audit log + access control)
- M7 TransecManager (fixed/hopping waveform + keyed hop-sync)
- M8 GnssThreatManager (spoof window + timing drift + reference-mode defense)

Adds 15 ConditionTypes + evaluators, OrbitalSatellite.reloadTle(),
SimulationManager.hasInstance(), manager lifecycle wiring, the nats-eu
sandbox that turns them all on, and a proof test driving each manager
from the real sandbox settings.
Four gated Mission Control tabs make the nats-eu mechanics human-driveable:
- link-budget (M1): Friis worksheet -> Compute -> Commit with acceptance badges
- commanding (M2/M5): Doppler switch, command window, key lifecycle, guarded
  zeroize, canned + manual TT&C sends with a reject-reason log
- contact-schedule (M3): per-contact station selects + live conflict/plan status
- security-console (M6+M7): audit log flag/review, access-control accounts,
  TRANSEC panel (mode, hop-set key, sync badge)

Tab registration is gated on the same opt-in settings blocks, so the legacy
nats campaign registers none of them. Adds DOM-level click-through tests plus
tabbed-canvas gating tests (absent by default, present with blocks, transec-only).
Campaign 4 counter-comms plan + retro, the full 24-scenario nats-eu
campaign design plan, phase-2 mechanics and phase-3 console-tabs retros,
and docs-local analysis notes (nats campaign analysis, campaign-5 lessons).
Snapshots the valid NIST NICE codes into a checked-in scripts/nice-catalog.json
(542 codes) so the CI test is self-contained rather than depending on the
gitignored .claude/ skill reference files, while gen-nice-catalog.mjs keeps it
regenerable from those sources.

nice-coverage.mjs prints the product-wide coverage table and fails on any code
not in the catalog; nice-coverage.test.ts enforces that in CI. It immediately
caught an off-catalog code in the signal-hunter sandbox (K0108 -> K1032).

Adds npm run nice-coverage / nice-catalog.
author-passes.mjs grid-searches TLEs that produce requested pass windows over a
given station and self-verifies the result with a pass table, so authoring
"give me a pass at T+X over station Y" stops being hand-tuned trial and error.

Reproduces the checked-in MERIDIAN-SAR-1 pass as its correctness demo. Pass
authoring at scale is the structural cost of the 24-scenario Campaign 2 arc;
this is the tool that attacks it.
KU_BAND_4M_LEO_TRACKER was configured at 5 deg/s in azimuth, which is
unrealistically slow for a purpose-built Ku LEO tracker (real pedestals do
20-30 deg/s). At 5 deg/s the pedestal could not hold the narrow ~0.45 deg beam
through even a moderate pass, so C/N collapsed mid-pass for reasons no operator
could diagnose.

Found by playtesting the live app, not by unit tests: the displayed downlink
C/N only reached ~6.4 dB where analysis predicted ~14 dB. The link budget was
never the problem - the pointing loop was.

A near-zenith pass still hits an azimuth keyhole (rate -> infinity at the
zenith); that is real physics and is reserved as a dedicated later lesson
rather than engineered away.
LEO pass scenarios put objectives many simulated minutes apart, so an E2E spec
that waits wall-clock time is infeasible. advanceClock() jumps the scenario
clock in one step and is exposed on window for tests, mirroring the existing
window.debugSignalPath hook in RFFrontEndCore.

Orbital physics read absolute sim time via getSimulatedNowMs() and follow on
the next tick; mission/objective timers tick on real update deltas and are
deliberately unaffected by the jump.
The 1290 MHz transponded return that appears when the HPA comes up is now a
decision graded on live evidence facts (no interference event, no injected
fault, crypto intact), with the HPA state and RX key status as the evidence
the operator must read first. secure-for-decode follows it. The decision box
no longer shares the quiz box class, which made the shared e2e quiz locator
ambiguous; it also clears its option buttons on close.
Night receive shift with three things that do not belong: a password spray
in the overnight log, a terrestrial Ku carrier inside the SAR-1 video band
that does not Doppler and outlives the pass, and a live configuration export
by a decommissioned service account. Priya Sharma debuts. call-the-carrier is
the first Gray Zone decision, graded on live evidence facts. Adds the Phase 3
validation test; registers the scenario in the manager, campaign, duration
and sweep-quiz counts.
The S17 emitter returns on both passes, cycling 60 s on / 30 s off and sitting
in the band of whichever bird is being received: the configuration export in
use. Characterise the cadence, call it, notch it and keep the pass, log the
event, notch it again when it follows the plan, file the regulator package.

The interference-active evidence fact now reads the event envelope rather than
the instantaneous radiating state, so a duty-cycled jammer is one incident and
a decision about it cannot flip in its off phase.
…plink jammer

Gray Zone arc 3/8. The SAR-2 commanding pass: HK-DUMP ACKs, then a
transponder-path jammer on the 14035 MHz uplink from 13:22:30 and PLD-STATUS
NAKs "Uplink denied - carrier jammed" with the key Valid, Doppler engaged and
the window open. The operator calls the denial (decision graded on the new
uplink-jammed fact with the key status and console state as evidence), keys
the hop set both ends, takes the waveform to hopping, gets SYNC LOCKED, and
the resend ACKs. SAR-1 receive-only afterwards, untouched. 17 objectives,
190 pts, 6 clips.

Engine: CommandingManager.isUplinkJammed - a transponder-path interference
event on the target bird overlapping settings.commanding.uplinkFrequencyHz
(bandwidth/2 + UPLINK_JAM_GUARD_HZ) while in its envelope and TRANSEC not
hop-synced rejects commands 'uplink-jammed'. New evidence fact
'uplink-jammed'; SimulationSettings.commanding.uplinkFrequencyHz; console
reject copy; e2e sendCommandAndExpectNak helper; unit tests for the coupling
and the fact; S19 registered in scenario-manager, campaign data, board sweep
and phase-3 validation.
Gray Zone arc 4/8. Pre-dawn receive shift; a GNSS spoofer local to GW-01
from 05:08 to 05:36 walks the GNSS-vs-reference timing offset at 2 us/s while
the constellation panel stays LOCKED with a full satellite count. Shetland
reads +0.0. The operator reads the walk, cross-checks SH-02, calls the spoof
(decision graded on timing-drifting AND gnss-constellation-healthy), takes the
GNSS switch down, flies SAR-1 in holdover, flags Rotterdam's timestamp-skew
audit entry and reports in progress, probes the sky after LOS (a second,
clock-dependent decision: still walking before 05:36), and comes back to GNSS
only when the offset holds still with GNSS re-selected. SAR-2 on true time.
17 objectives, 190 pts, 7 clips.

Engine: GPS Timing tab gains a 'GNSS vs REF delta-T' readout
(#gpsdo-time-offset) fed by GnssThreatManager, and its GNSS switch now sets
the manager's reference mode (the coupling the SDR console already had).
GnssThreatManager: affectsStation/timeOffsetUsFor (groundStationIds scoping
so a second station is the cross-check) and secondsSinceOffsetChange. New
conditions gpsdo-time-offset-exceeds (minOffsetUs) and
gpsdo-time-offset-stable (holdSeconds). S20 registered in scenario-manager,
campaign data (725-760 min), board sweep (13) and phase-3 validation; M8
mechanics test covers the scoping and the hold counter.
A scenario's Working Document is filed into a campaign-scoped store
(localStorage, keyed by campaign id) when it completes, with any audit-event
ids a destroyEvidence consequence removed during the run. A read-only
Campaign Record panel (sidebar, next to Working Doc) lists every earlier
scenario's record - the player's own where it was completed in this browser,
otherwise the scenario's canonical documentLines marked as filed - and the
campaign-document-reviewed condition latches on opening it.

The evidence chain crosses scenarios: an audit entry can declare
requiresCampaignEvidence { scenarioId, eventId }; SecurityConsoleCore drops
it at load when the record says a decision destroyed that evidence earlier,
and the new evidence-chain-intact fact reads false so a later decision can
grade attributable against unattributable on what the player kept. The
security console now copies its event list so a replay starts pristine.
S21 "Knocking on the Door": the SAR-2 commanding pass; replayed HK-DUMP
frames rejected at the bird with a stale counter, every key badge Valid.
call-the-knock graded on crypto-intact; out-of-cycle rotation to Q2b;
retain-the-evidence - the zeroize branch fires destroyEvidence on the KG-01
unit log, which the campaign record remembers.

S22 "Connecting the Dots": the attribution report. The Campaign Record panel
and the audit log carry S17-S21 forward; the KG-01 line is present only if
S21 kept it, and attribute-the-actor grades on evidence-chain-intact - one
actor, or the recorded gap at a cost. The referral beat is Charlie's bridge
to campaign 4.

S23 "Dark Passes": spoof, SAR-2 burn (spaceEvents initialTle) and a jammer
in the window, eight minutes before a pass that must deliver PLD-SAFE.
triage is graded on the clock (ephemeris first while uplink-jammed is
false); the denial is called with the reference in holdover.

S24 "North Atlantic Storm": the capstone. Contact plan across both sites,
rain then sleet, SAR-1 under the terrestrial carrier (notch), SAR-2 from
Shetland on the post-burn set with the spoof on SH-02, SAR-3 commanded
through the sleet with the denial and the hop; incident log and handover.
20 objectives, 10 clips (density gate allows 10 for the capstone).

All four registered (campaign data 905-940 min, scenario registry, board
sweep 17, phase-3 validation with fact tables per decision) and proven by
their full-completion specs.
Phase 18 E, the spacecraft half. settings.telemetry starts TelemetryManager
and a read-only Telemetry tab: channels grouped by subsystem with nominal
values, noise, yellow/red limit bands and scripted excursions on the mission
clock. Frames flow only while an antenna on the station is locked on the
bird or pointed within tolerance of it (a manually pointed dish on a GEO
slot); a dropped link freezes the last values and reads STALE. An excursion
can end on a TT&C command ACK (endsOnCommandId) and recover over
recoverySeconds, so a command is verifiable in the channel it was sent for.

Conditions telemetry-frames-received, telemetry-channel-in-band (channel +
band, observation-gated on the telemetry tab), telemetry-soh-nominal and
ranging-measurements; facts soh-red-limit, soh-yellow-limit, telemetry-stale.

Ranging: settings.commanding.ranging adds a TONE control to the TT&C
console; each tone is gated like a command and, when it ACKs, records the
true slant range to the target (CommandRecord.rangeKm/elapsedS).
TALON-2 (90071): the unit's own X-band MILSATCOM relay at 110.0 W, the bird
behind the protected band, with SANDSTORM as its TT&C backup while the
primary terminal is down. The contractor's arc (the campaign-2 bridge):

S3 "First Shift": point the monitor, take telemetry, read EPS/TCS/ADCS/COMM/
OBC in band, call state of health (decision graded on soh-yellow-limit and
telemetry-stale), one housekeeping dump, safe, log. 12 objectives, 100 pts.

S4 "State of Health": a wheel transient that self-clears and a battery
heater stuck on after eclipse exit; DEGRADED called with the channel named;
TCS-HTR-OFF inside the window; verification read in the telemetry, not the
ACK (the excursion ends on the command). 10 objectives, 100 pts.

S5 "Ranging Pass": a six-tone ranging arc around an east-west station-
keeping burn (spaceEvents initialTle on a GEO bird), the burn read as a
~1.5 km step in slant range with pointing steady, EPHEMERIS STALE, the
post-burn set loaded, the arc handed to the engineers. 10 objectives, 100 pts.

Registered in the ccs campaign (100-140 min), the scenario registry, and
validation tests per scenario; full-completion specs for all three.
Phase 18 F closes the Signal Hunter arc.

S2 Two Carriers: a terrestrial fixed-wireless sector arriving at the dish directly (3752 MHz, 1398 MHz IF, continuous) beside an uplink interferer through TP-1 (6001 MHz, 1374 MHz IF, 36 s on / 84 s off). The correlator reports NO CORRELATION on the terrestrial carrier; the path decision is graded on two new evidence facts, transponder-interference-active and terrestrial-interference-active (the envelope read split by event path), with both carriers observed as evidence. Uplink carrier fixed inside 25 km, refined to 15 km, both dispositions filed.

S3 Cold Trail: first carrier fixed and briefed to the field team as an ellipse; its envelope closes at T+1520 and the cold-trail call is gated on the new interference-event-ended condition; the second carrier appears at T+1800 from ~80 km away; CLEAR, retune, refix on its own captures; mobility and current-fix fields filed.

S4 Prove It: the correlator calibrated on the Cannon AFB terminal's authorised carrier (fix inside 20 km of the surveyed site), CLEAR, hostile fixed alone inside 20 km, ellipse closed with the new geolocation-ellipse-within condition (graded on the console's own 95% ellipse, 20 km semi-major), the call graded on transponder-interference-active, basis and confidence fields filed.

Engine: InterferenceManager.isAnyEventInEnvelopeVia(path) and hasEventEnded(id); the two facts; the two conditions; docs guide fact list. Tests: shared signal-hunter validation helpers solve each scenario's geometry headlessly to prove every fix and ellipse threshold, and check objective titles against every other objective's description. e2e specs for all three (31/31).
Phase 18 G closes the curriculum at 99/99 with four short single-purpose drills at Galway, chained from S16: no pass, no clips, no timers, 100 points each, SYSTEM quizzes and one graded decision where the topic is a call.

S25 Site Orientation: Group Security's ten-stop sheet - segments, lifetime, decision ownership, the soft target, effects, consequence framing, the monitor-and-control path, why OT is not IT and where the boundaries are, the antenna pattern that let the 12 April carrier in, and what stays inside the fence.

S26 Baseline Check: RX modem 1 staged at 1410 against the 1414 baseline with the vendor change in the audit log; the drift is read on the console, the call graded on config-drifted before anything is flagged, the baseline restored, the entries flagged, the account held, the access request confirmed before first transmit, the sweep logged.

S27 Removable Media: a USB mount and an unsigned ACU firmware stage from an escorted technician; the call graded on config-drifted with the media entry as evidence, the wrong branch injecting the reboot it would have caused; verification path, transfer rule, and the OT reasoning.

S28 Vendor Session: an unticketed remote session, an off-baseline outbound flow and an ACU read burst; the call graded on audit-anomaly-present with the session flagged as evidence; account cut, flows flagged, boundary and reach stated.

Engine: DecisionConsequence.auditEvent gains timestampLabel; the density gate gains a drill tier for S25+. Shared drill setup in drills.ts. Validation test covers wiring, staging, grading and chains; e2e specs for all four (28/28).
All sim time now comes from SimClock: the render loop feeds real frame
time and UPDATE runs in fixed 60 Hz steps. Run time drives equipment
dynamics; scenario time (missionNowMs) pauses for the brief freeze,
quizzes, failure and a hidden tab, so scheduled events no longer drift
against the sky by each learner's reading and quiz time.

All randomness draws from seeded Rng streams (seed from ?seed=,
ScenarioData.seed, or the scenario id), so runs replay exactly.
window.advanceClock is the single e2e time hook. determinism.test.ts
gates frame-rate independence, seed replay and no Math.random.
The transit ran T+300..600 s from load, so a player reaching Confirm Predicted Onset late found it over and Sky Noise Rising never ticked. Weather events take startAfterObjectiveId; Sky Temp now includes the transit noise. scenario17-late-onset.spec.ts covers the slow-player case.
@github-actions

github-actions Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 1 package(s) with unknown licenses.
  • ⚠️ 67 packages with OpenSSF Scorecard issues.

View full job summary

Comment thread scripts/plugin/lib/render-template.ts Fixed
Comment thread scripts/vo-manifest.mjs Fixed
Comment thread scripts/plugin/lib/codegen.ts Fixed
Comment thread scripts/plugin/lib/codegen.ts Fixed
Comment thread src/locales/i18n.ts Fixed
render-template walks with Dirent types instead of stat-then-read. vo-manifest strips tags until none remain (output unchanged). Plugin manifests must carry a semver version, an identifier className and entry/localesDir paths inside the plugin, and codegen escapes angle brackets and line separators. i18n skips __proto__/constructor/prototype when merging bundles. Adds a test that a stuck pre-fix S17 checkpoint restores into a winnable run.
@thkruz
thkruz merged commit eae2bf5 into main Sep 26, 2026
12 of 13 checks passed
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.

2 participants