aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* fix(installer): harden the disk-space pre-flight gateCraig Jennings3 days2-9/+118
| | | | | | | | `df /` wraps to two lines when the root device name is long -- a device-mapper or live-ISO root -- so `awk 'NR==2 {print $4}'` read the wrapped device line, found an empty Available field, and aborted a valid install with "Insufficient disk space." `df -P` forces POSIX single-line output. The gate now compares available KB against the minimum in KB rather than truncating to GB first, which rejected a disk sitting just under a whole-GB boundary. Non-numeric df output falls back to zero so a malformed read aborts loudly instead of crashing the arithmetic test. Extracted the logic into check_disk_space so it can be exercised directly.
* docs: close chpasswd guard taskCraig Jennings3 days1-1/+3
|
* fix(installer): guard the user-password chpasswdCraig Jennings3 days2-1/+65
| | | | set -e is off, so a chpasswd failure silently left the primary user with no password and no log entry, leaving an unloggable account on a fresh install. I extracted set_user_password, which aborts via error_fatal on a chpasswd failure. A fake-chpasswd test pins that the guard fires.
* chore: reorder open-work tasks in todo.orgCraig Jennings3 days1-244/+244
|
* docs: file waybar module-separator taskCraig Jennings3 days1-0/+4
|
* fix(security): decrypt the cmail password under a tight umaskCraig Jennings3 days2-2/+89
| | | | gpg writes its output at the process umask (often 0644), so the plaintext mail password was world-readable between the decrypt and the chmod. I extracted decrypt_to_secure, which runs the decrypt in a 0077 umask subshell so the file is 0600 from creation. A fake-gpg test pins that the write runs under the tight umask.
* feat: add inetutils to the install baseCraig Jennings3 days2-1/+59
| | | | TRAMP's /ftp: method shells out to a command-line ftp client, and Arch ships none by default. inetutils provides /usr/bin/ftp, so I added it to install_required_software. A static test asserts it stays in the base set.
* docs: file archsetup move off cgit to private remoteCraig Jennings4 days1-1/+4
|
* docs: close WireGuard leak task, note cgit follow-upCraig Jennings4 days2-17/+23
| | | | | | Record the resolution (keys expired, all 10 configs purged from history, server gc'd, verified via anonymous clone) and file the cgit-exposure audit as the systemic follow-up. Update the config README to the out-of-band workflow.
* fix(security): stop tracking plaintext WireGuard configsCraig Jennings4 days2-0/+24
| | | | | | The repo is public via cgit, so the three plaintext configs (added 849c3fa) exposed live Proton PrivateKeys. Remove them, gitignore plaintext, and document the encrypted-only workflow. Keys rotated at Proton; history purged separately.
* docs: log overnight code audit and file triaged installer bugsCraig Jennings4 days2-80/+286
| | | | | | Overnight sentry ran its hygiene passes plus a code-inspection pass over the installer and the scripts/ directory. It archived 6 completed tasks to Resolved and logged 19 bugs and 12 refactors in docs/design/2026-07-19-sentry-code-findings.org. I filed the bugs as tasks graded by severity times frequency. Nothing in the codebase changed, this was detection only. Two things stand out. The installer does no partitioning (it's a post-install config layer), so the real data-loss surface is boot config, and it overwrites GRUB_CMDLINE wholesale, which can drop a cryptdevice or resume param and leave a machine unbootable. Three WireGuard configs with live private keys are tracked in git, flagged for a rotate-or-keep decision.
* docs: expand desktop-settings spec and add panel prototypesCraig Jennings4 days10-46/+1440
| | | | | | | | I folded the home-handoff control-set decisions into the desktop-settings panel spec. The controls table gains night-light, Do Not Disturb, power profile, lock/suspend actions, scenes, and a wallpaper sub-view. Volume and theme are out of scope, with reasons, and the format pickers move to their own display-format sibling spec (stubbed). The scenes and wallpaper-shape decisions close. The spec stays DRAFT pending UI prototyping. I added five direction prototypes over a shared engine (tile grid, sectioned rack, scenes-first, instrument cluster, expandable rows) so the layout gets picked against working behavior. I closed the shipped Next Session Focus work in todo.org: notification loudness, clock right-click dismiss, date-only scrolling, the wired-interface indicator, and best-saved WiFi connect, plus velox boot recovery. The three design-blocked network and audio items stay TODO with their open question recorded.
* docs: set next session focusCraig Jennings4 days1-0/+14
| | | | Prioritize autonomous Waybar, panel, and audio work.
* docs: clarify clock panel dismissalCraig Jennings4 days1-3/+4
| | | | Reserve left click and use right click to close the open panel.
* docs: file interface design referencesCraig Jennings4 days44-0/+1980
| | | | Record the reconciled weather work and future component direction.\nFile the clock and retro-interface reference materials for later design work.
* docs: record weather comfort signalsCraig Jennings4 days1-0/+2
|
* docs: record weather baseline correctionCraig Jennings4 days1-0/+2
|
* docs: record weather bottom alignmentCraig Jennings4 days1-0/+2
|
* docs: record weather chip groupingCraig Jennings4 days1-0/+2
|
* docs: record weather alignmentCraig Jennings4 days1-0/+2
|
* docs: record weather glyph polishCraig Jennings4 days1-0/+2
|
* docs: record weather glyph sizingCraig Jennings4 days1-0/+2
|
* docs: record weather glyph updateCraig Jennings4 days1-0/+2
|
* docs: record weather location refreshCraig Jennings5 days1-0/+2
|
* docs: record weather forecast deliveryCraig Jennings5 days1-0/+3
|
* docs: clarify weather forecast toggleCraig Jennings5 days1-1/+1
|
* docs: define weather forecast click actionCraig Jennings5 days1-1/+1
|
* docs: define weather forecast viewsCraig Jennings5 days1-1/+1
|
* chore: track weather integrationCraig Jennings5 days1-1/+12
|
* docs: document single Dupre themeCraig Jennings5 days6-25/+14
| | | | Remove retired theme-switching guidance and Hudson references from active documentation.
* chore: track package and notification workCraig Jennings5 days1-0/+17
| | | | Add the installed-package drift audit and notification sound loudness tasks.
* fix: order pacman safety hooksCraig Jennings5 days6-10/+49
| | | | Install snapshot and live-update hooks before mkinitcpio removal, migrate legacy paths, and cover the ordering invariant.
* chore: archive resolved backlog tasks and note probe-flake recurrenceCraig Jennings5 days2-145/+146
| | | | Moved the completed maintenance, audio, and timer tasks into the archive, applied the lint counter fixes, and logged the batch-6/batch-9 fader-drag probe-flake recurrences on the existing tracking task.
* feat(gallery): bring the well-through-response ten to the extraction barCraig Jennings5 days2-79/+240
| | | | | | | | outputWell, toast, annunciator, tapeCounter, freqScale, patchBay, dataMatrix, warningFlag, seg14, and responseGraph each get the contract comment. The six DOM builders take dupre- prefixes across their markup and CSS blocks. patchBay's live jack and the annunciator's master caution adopt the kit-wide dupre-hot and dupre-on modifiers, and the annunciator board takes dupre-warn and dupre-fault. freqScale reuses the shared scoped dupre-tick and dupre-mk (as the tuner and dualknob do) and compounds its unit label to dupre-fs-band, since dupre-band is the EQ's. The four SVG-stage builders style through the shared .rsvg stage and inline attributes, so they get the SVG-built contract comment and no prefixes. The annunciator documented a 0..2 state per cell but painted an undefined class on an out-of-range initial state or set(). It now clamps at build and at set. Two probe checks cover the batch: one fails red without that clamp, one drives each renamed DOM builder through its handle to catch a class-name mismatch the exception scan can't see. Extraction audit: 104/111 contract comments.
* feat(gallery): bring the CRT-through-engraved ten to the extraction barCraig Jennings5 days2-35/+213
| | | | | | | | | | roundCrt, attitudeIndicator, headingBug, chartRecorder, verticalTape, twinNeedle, comfortMeter, badges, tabularReadout, and engravedLabel each get the contract comment. The three DOM builders take dupre- prefixes across their markup and CSS blocks: badges (dupre-badge with the kit-wide dupre-red plus a new dupre-ghost), tabularReadout (dupre-readout and dupre-unit), and engravedLabel (dupre-engrave and dupre-cnt). The rename surfaced a dead rule: .readout .u selects a descendant, but the unit caption is a sibling, so its style never applied. The caption now styles through a standalone .dupre-unit rule. The builders now enforce their documented domains at build, not only in set(): twinNeedle and comfortMeter clamp their initial opts through set(), headingBug normalizes opts.value to 0..360, and badges coerces junk variants through one norm() shared by build and set(). Two probe checks cover the gates and drive the renamed DOM. Extraction audit: 94/111 contract comments.
* feat(gallery): bring the needle-through-edge ten to the extraction barCraig Jennings5 days2-77/+232
| | | | | | | | crossNeedle, thermometer, bourdon, stripChart, corrMeter, battCells, mcVu, chromeMinMax, aviationGauge, and edgeMeter each get the contract comment. The six DOM builders take dupre- prefixes across their markup and CSS blocks. Internals go compound where gauge, fader, and nixie already hold the plain names: bourdon's needle, hub, and cap, corrMeter's face, arc, needle, and label, crossNeedle's arc, and thermometer's tube and fill. crossNeedle's nA and nB become dupre-fwd and dupre-rfl, and battCells moves its cell modifiers to the shared dupre-on and dupre-warn. The handles now enforce their documented domains: the needle meters clamp set() to 0-100, stripChart floors its history at two samples and survives a non-array set(), battCells floors its cell count at one, and mcVu clamps and paints opts.value at build instead of ignoring it for a hard-coded .35. Two probe checks cover the gates and drive the renamed DOM. Extraction audit: 84/111 contract comments.
* feat(gallery): bring the decade-through-EQ ten to the extraction barCraig Jennings5 days3-89/+260
| | | | | | | | decadeBox, twoHandSafety, voiceLoop, miniSig, fuelBar, radialRing, sparkline, waveStrip, scope, and eqBars each get the contract comment. The eight DOM builders take dupre- prefixes across their markup and CSS blocks. voiceLoop's inner bar becomes dupre-vk-bar to stay clear of fuelBar's dupre-bar, and miniSig and eqBars move their cell modifiers to the shared dupre-on, dupre-hot, and dupre-clip. Domain gates land where a handle could wedge: decadeBox coerces and clamps its digits, twoHandSafety ignores sides other than L and R, voiceLoop keeps the demo state to the stock loop set (a caller's shorter list used to crash on it), the meters clamp set() to their documented ranges, and sparkline and scope guard the one-sample case that drew NaN coordinates. Two probe checks cover the gates and drive voiceLoop's grammar through the prefixed DOM. probe-fams follows scope's rename. Extraction audit: 74/111 contract comments.
* feat(gallery): bring the timer-through-knife ten to the extraction barCraig Jennings5 days2-41/+177
| | | | | | | | timerDial, rockerPad, fourWayToggle, pinMatrix, deadMan, telephoneDial, breakerPanel, dsky, camTimer, and knifeSwitch each get the contract comment. dsky, the one DOM builder, takes dupre- prefixes across its markup and CSS block. That resolves the batch-1 deferral on the shared .win name, and its lamp and window modifiers move to the shared dupre-on and dupre-hot. Domain gates land where a handle could wedge: fourWayToggle ignores anything outside its four quadrants (prototype-inherited keys included) and falls back to C on a bad initial position, camTimer's set() clamps to the ring, and pinMatrix drops initial pins that name no intersection. Two probe checks cover the gates and drive dsky's grammar through the prefixed DOM. Extraction audit: 64/111 contract comments.
* feat(gallery): bring the keypad-through-guarded ten to the extraction barCraig Jennings5 days2-23/+166
| | | | | | | | entryKeypad, thumbSlide, waveRegion, ledRow, pillSlide, spunKnob, stompSwitch, wingSelector, discSwitch, and guardedToggle each get the contract comment. All ten are SVG-stage builders, so there were no classes to prefix. set() hardening lands where a handle could wedge: ledRow, pillSlide, and discSwitch clamp their index (discSwitch previously threw), waveRegion keeps E at least 4 above S from set() itself rather than only the drag path, and entryKeypad ignores keys outside its documented 0-9/confirm/clear set. ledRow's default selection is index 5 only for the default program list. A caller's own items start at 0. Extraction audit: 54/111 contract comments.
* feat(gallery): bring the DIP-through-multiband ten to the extraction barCraig Jennings5 days2-43/+144
| | | | | | | | dipBank, jogWheel, vernierDial, batToggle, flutedKnob, filterBank, chickenHead, slotFader, spadeKnob, and multiBandDial each get the contract comment. The two DOM builders also get dupre- prefixes on their classes (dip/dipsw and the on modifier, jog/shuttle/inner/dimple). jogWheel caches its inner disc instead of querying it on every move. set() hardening lands where a handle could wedge: vernierDial and flutedKnob clamp 0-100, filterBank clamps the band index and its initial values, and multiBandDial clamps the band and keeps the current one when set() omits it (a bare set(v) previously threw). Extraction audit: 44/111 contract comments.
* feat(gallery): bring ten more controls to the extraction barCraig Jennings5 days3-126/+212
| | | | rotarySelector, slideRule, rocker, transport, presetBank, dualKnob, encoder, keySwitch, crossfader, and thumbwheel get contract comments, dupre- prefixed CSS, and cached element refs. presetBank and dualKnob set() now clamp out-of-range input, with a probe check covering both. rotarySelector and keySwitch cap their inputs at the five- and three-stop plates they draw. The audit's contract count moves to 34/111.
* feat(gallery): bring ten core controls to the extraction barCraig Jennings5 days4-152/+229
| | | | | | | | Batch 2 of the extraction sweep: consoleKeys, abcKeypad, indexPlate, faderH, faderV, knob, segmented, chipToggle, armButton and lampRow now carry the split-flap-shape contract comment. Every class they own wears the dupre- prefix, internals and modifiers included (dupre-key dupre-green, dupre-fader dupre-cap), with every in-kit consumer updated in the same pass: the dsky and annunciator pads, the rotarySelector and encoder spindles, the page's size bar, and the probe selectors. I also removed indexPlate's dead '#dupre-defs' lookup (nothing ever created that id) and dropped the unused .key.off rule. The faders, knob and lampRow now cache their element refs instead of re-querying every set(), and segmented and lampRow clamp set() the way consoleKeys does. The audit's contract count moves from 14 to 24 of 111.
* feat(gallery): bring the 12 waybar-bound builders to the extraction barCraig Jennings5 days3-96/+304
| | | | | | | | | | Each builder now carries a contract comment in the splitFlap shape (opts, handle, where its CSS lives). I prefixed every batch-owned class with dupre- so page chrome can't collide with instrument internals. The audit's C1 moves to 14/111, the .txt collision is gone, and .on clears as its owners' batches land. jewels also gets a real handle. Its state lived only in classList/--jc, so nothing could read or drive the lamps (a dead end for the waybar port). I moved state into the builder, made the DOM a paint of it, and added get/set. lampRow and outputWell consume the shared lamp class, so they map their state fragments to the prefixed names internally. Their opts APIs are unchanged. audit-extraction.mjs is the report-only grader for the bar (contract comments, page-styled internals, page reach-ins). It gates nothing until the sweep reaches 100%.
* refactor(gallery): rename GW namespace to DUPRE, kit to The Dupre KitCraig Jennings5 days8-465/+465
| | | | | | The gallery and its library are now The Dupre Kit, named for the dupre color theme. GW was too opaque to remember. The stylesheet id is now dupre-css, the card handle property is .dupre, and page prose says instrument instead of widget. File names stay put. Renaming them is a separate decision. Historical log entries in todo.org keep their GW references as a record.
* fix(gallery): split-flap defaults are paper cards, Helvetica, 100msCraig Jennings5 days3-30/+31
| | | | The default skin is the white card with dark lettering, not white ink on black, and the default face is Helvetica. Both list first in their chip rows with the alternatives a click away. The base flap rate moves to 100ms. The probe pins all three defaults, and its cascade windows widen to match the slower base so worst-case travel keeps its margin.
* feat(gallery): white-ink default, 85ms base rate, ID badges as anchorsCraig Jennings6 days3-36/+55
| | | | | | | | The split-flap's default skin becomes the white ink, listed first in the chip row, with the kit cream one click away. The base flap rate moves to 85ms, and the probe pins both defaults so a drive-by change can't silently flip them. Every card's ID badge is now a real anchor to its own card, so a click deep-links the URL and a right-click copies it. One shared rule keeps the badge look. The refactor sweep for extraction-readiness closes out: the flip duration and the ms clamp each live in one place, grid normalization is one helper shared by setText and the silent first paint (which now records itself as the last commanded grid, ending the null fallback), the state declarations sit above everything that references them (the same declared-later shape that caused a load crash earlier in the branch), and a stale two-skin comment caught up with the four skins. Behavior-preserving: all 79 probe checks stay green.
* feat(gallery): grow the split-flap into a randomized multi-row board with ↵Craig Jennings6 days4-47/+354
| | | | | | | | | | | | skins, faces and a flap-rate slider The N20 card becomes a three-row, six-cell departure board. Every advance sends each row to a completely different word, drawn at random from a 24-word pool with rows kept mutually distinct, and each cell runs at its own mechanical rate (a fixed 0.8x-1.35x of the base flap time), so letters and whole words finish at genuinely different times. A slider on the card sets the base rate (setFlapMs on the handle). The board advances only after it has settled and dwelt two seconds on the reading: the builder announces settle once per command (onSettle) and the page schedules the next advance from that signal, since a fixed-interval ticker would retarget mid-cascade and the panel would never rest. The chain stays gated off under reduced motion, where the board paints once and rests. Customization lands as two construction axes on the chip rig: skin (cream-on-black default, true-white ink, the ivory board, or white cards with dark lettering) and face (Berkeley Mono or Helvetica, the grotesque real boards wore). The skin chips are miniature flap cells, a letter in the skin's real ink on its real board colour, and the font chips letter themselves in their actual face, so every choice previews itself. The flap-rate slider rides in the chips row, and the card's amber word readout is gone: the board is the display. The deterministic seam for callers and tests is setText; randomness lives only in the demo path (next/scramble), which re-aims against the last commanded grid rather than in-flight glyphs. The settle work caught a real ordering bug: setText assigned each cell's target and ran it in one pass, so a cell finishing synchronously saw its neighbours' stale, already-satisfied targets and announced a partial board as settled. Targets now all land before any cell runs. Review caught two more: the dwell chain had escaped the reduced-motion gate (the board self-advanced every two seconds forever under reduced motion), and the skin chips rendered blank because the STYLES entries were strings where the rig paints an object's dot swatch. Since this is heading for extraction as a regular component, the builder got the full refactor pass: a contract comment covering every opt and the handle surface, small named helpers, no page globals, and all CSS in the one split-flap block. The same bar is filed as a sweep task for every other builder. Probe coverage grows to 78 checks: the reduced-motion rest, the live dwell measurement, the scramble contract, both style axes, the speed api and the swatch legibility.
* feat(gallery): make the N20 split-flap an honest Solari mechanismCraig Jennings6 days4-25/+166
| | | | | | | | The card's flip was a 0.3s drop-fade that teleported each cell straight to its target character — a suggestion of a split-flap, not one. Since the gallery card is the spec the ports get measured against, the fake would have propagated. Three behaviors replace it, studied from the HotFX split-flap component and re-derived (its repo carries no license, so no code was copied). The charset is the drum: opts.chars declares the flap order and a changed cell steps one flap at a time through intermediates, never jumping, so cells arrive staggered by travel distance. A set() mid-cascade re-aims the running cells at the new target. The fold is real: two half-panel animations per flip — the current top falls, the next bottom lands — over four clipped layers with backfaces hidden, replacing the drop-fade keyframe. animate:false (the reduced-motion gate) collapses every move to an instant jump. The handle grows setText, chars, and reading() so probes can sample the displayed state mid-cascade; the default width goes 3 to 4 cells, which stops truncating every demo word. Probe checks were written red first: cascade arrival, intermediate readings, one-flap-at-a-time stepping in charset order, mid-cascade retarget, and the instant animate:false path.
* chore(clock): file absorption follow-ups and fix the install-dep commentCraig Jennings6 days2-2/+9
|
* refactor(gallery): extract the shared tally-row markupCraig Jennings6 days1-11/+19
| | | | | | | | The validation and policy tallies were built from the same dot/label/count row template, written out per bucket in both. That template is now tallyRow() and tallyTotal(), so the markup has one source and the next index tally reuses it. The differing count and jump logic stays in each function, since those genuinely diverge: the validation tally drives the floating audit stepper, the policy tally a plain hash cycle. Also hoisted GW.POLICIES[kind] to a local in card(), where it was read three times. Behaviour-preserving: the output HTML is byte-identical.