Commit Graph
131 Commits
Author SHA1 Message Date
Martino Ferrari d90903d229 docs: the old reorder signature is history, not the delayed case's reading 2026-10-08 23:17:49 +02:00
Martino FerrariandClaude Sonnet 4.6 eacb3f5efc docs: the reseq probe's readings, and the invariants resequencing changes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-08 23:14:38 +02:00
Martino FerrariandClaude Opus 4.6 82180f45f7 feat: the mock can drop and invert packets, and carries a noisy channel
--drop-every/--drop-burst withhold datagrams the counter and stream clock
have already advanced over, and --reorder-every inverts a pair on the
wire; both are deterministic for the reason --glitch-at is, since a probe
asserts the spacing of what it finds and a random count is not a figure.
A `noise` signal gives the dense-trace and intensity-floor paths a input
that is not piecewise smooth.

verification.md records the measured readings, including the one that is
a finding rather than a baseline: a burst past kGlitchPackets (256) is
read as a producer clock glitch, each bridge advances the anchor by one
packet span against a timeline 300 ahead, and four consecutive failures
then throw the stream onto the arrival clock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-08 16:56:36 +02:00
Martino FerrariandClaude Opus 4.6 219ae91fd8 docs: a spec for the square wave's riser being drawn only sometimes
Two complaints, not one: the glitch is a row dropped before any style
knob is consulted, and the thinness is a choice udpscope made and never
revisited -- it asks deps/plotter for a trace thinner than the library's
own default.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-08 16:56:25 +02:00
Martino FerrariandClaude Opus 4.6 723d1db3d2 docs: implementation plan for resequencing and relative timing
Twelve tasks in five phases, so relative timing ships before any of
the client-side reordering work lands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-08 16:50:50 +02:00
Martino FerrariandClaude Opus 4.6 d5d1295b26 docs: a design for resequencing late packets into a reserved hole, and for relative timing
The two were asked for together and are one design: the packet counter is what
makes a hole addressable, so relative timing is the coordinate system in which
resequencing is arithmetic rather than inference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-08 16:38:18 +02:00
Martino FerrariandClaude Opus 4.6 c8f10f59dc fix: a condition leg is reported once it is held, so a working leg cannot read nan
A finite trigger_time() only says the edge was accepted; the mode is Capturing
until the post window fills. Every field the report prints is read from
display_snapshot(), whose stamps span x = 0 only once the event is Held, so
reporting on the latch alone raced the capture: leg 2 printed
`measured=nan at0=nan ... mode=1` on one run and the documented
`at0=-0.500907 earlier=-0.498185 mode=2` on the next. The report now waits for
Held under the same poll budget, and says `latched but never held` when it runs
out -- a different defect from `no latch`, so the line names which.

The readings are corrected against three consecutive leg-2 runs and one leg-3
run: `t0` is a rebased stream stamp and varies every run, so it was never an
assertion; `hits=` likewise tracks the arm phase (12 at one arm, 11 at another).
The mode ordinals were wrong -- `mode=2` is `Held`, not `Capturing`. Shapes 2
and 3 are now measured rather than read from the arm code; shape 1 still is not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-08 11:34:30 +02:00
Martino FerrariandClaude Opus 4.6 728e7fbda5 docs: help pages, probe reference and session docs updated for the three new features
- 08-trigger.txt: names all four condition shapes and the buffer search; fixes the first
  paragraph and 'How to reach it' to match what the panel actually contains (fix 3).
- 10-measurements.txt: adds a Statistics bullet — one observation per recompute, what
  resets the history, what does not (fix 3).
- probe-readings.md: re-quotes condition-probe lines with level2= and mode= fields;
  corrects leg-2 window geometry (0.2 s / 10 % pre, not 1.0 s / 50 %); moves the
  wide-window reason to legs 1 and 3; fixes the search-probe scheduling note
  (search_probe calls condition_probe directly; do not also set CONDITION_MS) (fix 6).
- verification.md: corrects shape-2 window geometry to match the code (fix 7).
- invariants-session-and-export.md, README.md: document the measure_stats session key —
  position, type, default false (fix 8).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-08 11:19:19 +02:00
Martino FerrariandClaude Sonnet 4.6 ffc3ec1b4d docs: the three new features are described where each of their readers looks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-08 10:43:39 +02:00
Martino FerrariandClaude Opus 4.6 9aa1d80158 docs: an implementation plan for the three new scope features
Nine tasks with literal code: the mock's injected faults, the shared
EventDetector the trigger and the search both run, the four condition
shapes and their panel rows, the Welford statistics and what resets
them, the chunked buffer search, its marks, and the documentation.

Every probe's expected reading is written as a shape with the measured
figure left to be recorded from a run, never assumed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 12:27:30 +02:00
Martino FerrariandClaude Opus 4.6 a5ca841603 docs: the spec names the formatter the code actually has
format_measure_value() does not exist; the shared SI-prefix formatter is
format_measure(double, const std::string&) at src/ui.cpp:1004.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 11:21:36 +02:00
Martino FerrariandClaude Opus 4.6 19aad492eb docs: the trigger is not persisted, so the condition is not either
The spec described the new condition fields as appended session keys. LayoutState
carries no trigger field at all -- the trigger is built from --holdoff and the
panel's defaults on every launch -- so there was nothing to append to. Persisting it
is a separate feature nobody asked for, and a session that silently restored an armed
pulse-width condition would surprise rather than help. The statistics toggle stays as
the one session key this design adds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 11:11:30 +02:00
Martino FerrariandClaude Opus 4.6 6770c6a648 docs: a zero-width trigger band is refused, not degraded to an edge
The spec claimed a window condition with equal levels degrades to an edge trigger on
that level. It does not: the band test is a change of side, so equal levels make the
signal "inside" only on exact float equality and the condition silently never fires.
Special-casing the detector into an edge trigger would conflate two state machines
that answer different questions -- the divergence the shared evaluator exists to
prevent. The panel refuses the degenerate band instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 11:09:52 +02:00
Martino FerrariandClaude Opus 4.6 98be03e00d docs: three scope features are specified around one shared event condition
Qualified trigger conditions (pulse width with glitch as a comparison, window,
dropout), per-measurement statistics with a reset, and a search over the retained
ring. The first and third are the same question asked of live and of recorded data,
so they share one allocation-free EventDetector extracted out of TriggerEngine --
and the probe that proves the two paths agree on t0 is part of the scope.

Reference waveforms, PNG export, FFT and a byte-bounded event history are out, each
for a stated reason.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 11:07:00 +02:00
Martino FerrariandClaude Opus 4.6 f179fbbb44 fix: a clock glitch is bridged and a channel is known by name, so a capture keeps its t0
Two defects behind one report ("a trigger during a capture overrides the first,
and the hold-off does not apply"); the second is also what made a spike in one
channel look like the acquisition resetting.

The trigger engine keyed its invalidation on the trigger channel's *index*, which
every rebuild of the channel list renumbers -- enabling a signal that sorts before
it is enough. Any rebuild therefore abandoned an open capture and set no hold-off,
so the next edge inside the post window became t0. It keys on the channel's name
now, which changes only when the source genuinely does.

A packet whose stamps jump more than 256 packet spans ahead is now rejected as a
fault and *bridged* at the declared cadence, instead of poisoning the monotonic
mark (which pinned every later packet flat at the glitched value) and condemning
the next good packet. Only four consecutive failures abandon the declared time
signal, and that fallback no longer wipes the rings: the arrival clock is anchored
to continue from the monotonic mark, so the history is still on one timeline --
the wipe is what made one glitching packet look like the stream restarting from 0.
The declared unit is resolved before those checks, since they weigh the stamps
against a cadence in seconds.

Guards: UDPSCOPE_PROBE_TRIGRENUM_MS prints t0_held=1 through the real widgets,
and the mock's --time-glitch-at/--time-glitch-by injects the clock fault.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 10:29:05 +02:00
Martino FerrariandClaude Opus 4.6 4003084648 feat: a signal's chip is clickable, so a trace's colour is the user's to choose
Clicking the coloured chip in the signal list opens a GtkColorDialog and the
colour it returns overrides that signal's palette entry. Held by name, like
every other identity here, and persisted as a session `[[color]]` table.

RowSet::signal_color() becomes the one resolver: install() builds the trace
through it and all three chip sites read it, because a chip exists to say which
trace a row is about and an override is exactly the state in which two routes to
a colour would disagree. A grey chip names no trace and so is not clickable, and
the way back is `Default colour` in the signal's menu -- a setting with no undo
is a trap.

The gesture sits on the existing label rather than in a GtkButton: padding on
this row is what made the signal list twice as tall once. It claims its sequence
so a click on the chip cannot become the start of a row drag.

UDPSCOPE_PROBE_COLOR_MS reads the result out of plot::Plot::trace() and out of
the chip's own markup, with an override in no palette slot, so neither leg can
pass by asking the resolver what it already said.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 08:22:32 +02:00
Martino Ferrari 8bc1fe8b5c fix: a repaired source is reconciled above the validation line, not below it
`math_dialog_sync_form()` wrote the label and then moved a node whose source
names an unacquired channel -- a mutation that changes what
`math_dialog_problem()` reports. With the sync re-entry now guarded, the nested
call that used to repair the label is suppressed, so the line kept describing a
source that had just been fixed and left `Apply` insensitive until the next
mutation. The reconcile moves above the write and the dropdown reuses its list.
2026-10-07 07:17:46 +02:00
Martino Ferrari f238e717b2 fix: a wire is picked by distance and a refusal is spoken, so the editor answers
A review of the whole node editor. The hit test measured the distance to the
24 curve samples rather than to the segments between them, so a wire spanning
several rows was unpickable between two samples -- the probe now places its
boxes 280 units apart and prints the nearest sample's own 8.4 against a 3.0
tolerance, which the point test would have missed. A refused connection said
nothing: the message is now carried in `MathDialog::refusal` and shown once by
the single writer of the label, and a self-wire is refused through the real
drag-end path instead of only through `math_connect()`.

Two defects were found only by the new assertions. `math_dialog_sync()`
re-entered itself -- `gtk_drop_down_set_selected()` emits `notify::selected`
for a programmatic set too, and the handler answers by syncing again, which
consumed the refusal before it could be drawn -- so the body is now guarded
one level deep. And a deleted node left `selected_wire` naming an index that
afterwards belongs to a different wire, so `Delete` cleared an input on the
wrong node.

A node is placed in the first free grid cell rather than at the end of the
list's own cell, so a node added after a delete no longer lands on top of a
survivor; the label ellipsizes with a tooltip rather than wrapping, because a
wrapped line makes the dialog's height depend on the text in it.
2026-10-07 07:09:49 +02:00
Martino Ferrari 300b33159e docs: the node editor's gestures, its new probe assertions and its invariants
Each invariant records what went wrong rather than only the rule: a port hit
that lost its node, a hit test at the identity view that asserts nothing, a
`Delete` that must not fall through to the selected node, and a column pitch
too narrow for a wire's own tread. The warning baseline drops to four.
2026-10-07 02:56:36 +02:00
Martino FerrariandClaude Opus 4.6 a7702acb63 docs: CLAUDE.md is a goal and a table of contents, so a context pays for what it reads
The file was 1967 lines and is auto-loaded into every context, so every
session paid for the whole of it to reach one invariant. It now carries the
project's goal, the three facts that are always relevant, and a table of
contents; the body is fourteen files under docs/claude/ reached by link
rather than by import, so nothing is loaded until it is asked for.

The content is moved verbatim -- the only edits are each file's own header
and the cross-references that became cross-file ("see the invariant below"
is now a link to the file that owns it).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-07 01:49:28 +02:00
Martino FerrariandClaude Opus 4.6 0866b744ad docs: a plan for the node editor, so each defect ships with the probe that saw it
Nine tasks, each ending in a commit: the silent disarm in the port hit test,
the view transform, the drag-based wiring, wire deletion, the topological
re-sort that makes a backwards wire legal, the live validation line, the op
picker, tidy, and the docs with the full verification run.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-06 23:24:15 +02:00
Martino FerrariandClaude Opus 4.6 96ee35da25 docs: a design for a node editor whose wires can be drawn, deleted and navigated
The reported "cannot add edges" has a specific cause: the output port's hit
radius reaches past the box it belongs to, and the press handler re-derives the
owning node with a box test, so clicking the visible dot's outer half sets
pending_out to -1 and silently disarms. Edge deletion has no code path at all.

The design replaces the two-click protocol with a drag gesture, makes the list
order the editor's own invariant via a topological re-sort rather than a rule the
user must obey, adds a view transform with every hit test routed through its
inverse, and trades the modal alert on Apply for a live validation line. It also
records that the Cancel button still calls gtk_window_destroy -- the dangling
dialog pointer CLAUDE.md documents as fixed was only fixed on the Apply path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-06 22:26:31 +02:00
Martino Ferrari 1fc2a3afe2 improving doc 2026-10-05 00:28:10 +02:00
Martino FerrariandClaude Opus 4.6 f2978fe742 fix: guard unset HOME in desktop-entry cmake script and correct plan verification
Two code-review findings:

1. Add a guard in cmake/install_desktop_entry.cmake that fails visibly when both
   XDG_DATA_HOME and HOME are unset or empty, preventing silent installation to
   the filesystem root in minimal CI sandboxes.

2. Replace the plan's tail-based verification with grep, because cmake's
   FATAL_ERROR message and ninja's "build stopped" lines push the actual
   rejection message out of the last four lines. The grep command reliably finds
   the expected "desktop-file-validate rejected" string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-04 20:50:42 +02:00
Martino Ferrari 69eab02729 docs: the implementation plan for the app icon, AdwAboutDialog and desktop entry
Five tasks, each ending in a commit: the two SVGs with build-time xmllint
validation, the window icon plus the registration move and the app icons:
assertion, the AdwAboutDialog migration, the opt-in desktop-entry target, and
the documentation with the full regression run.

Written assertion-first, since the project has no unit suite: each task's first
step adds the probe or dump line and watches it read MISSING. Both negative
tests are real -- one breaks an SVG comment and requires the build to fail, the
other breaks Type= and requires the target to FATAL_ERROR.
2026-10-04 20:24:10 +02:00
Martino FerrariandClaude Opus 4.6 e17c650ea7 docs: correct the spec's icon verification to a build-time check
The spec asserted that a paintable's intrinsic size reads 0x0 for an SVG
librsvg refuses. It does not: gtk_icon_theme_lookup_icon() never returns NULL
and always reports the requested size, and a valid icon, a malformed one and an
absent name all snapshot to the same texture node. GMarkup accepts the double
hyphen, and gdk-pixbuf goes through glycin here and refuses our own icons.

So the malformed file is caught by preprocess="xml-stripblanks", which runs
xmllint during glib-compile-resources and fails the build with file, line and
column. The runtime line keeps its format but asserts only what it can: the
name resolves, the resource is present, and the root element declares the size.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-04 20:11:42 +02:00
Martino FerrariandClaude Opus 4.6 7816f11c81 docs: spec for the app icon, AdwAboutDialog and desktop entry
Records the decisions taken before design — the application ID stays
org.udpscope.app, the icon is the instrument face, and the desktop entry is an
opt-in CMake target rather than an install() rule — along with the HIG
constraints the artwork is built to and the two silent failures the new
verification line has to tell apart.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-04 20:07:04 +02:00
Martino FerrariandClaude Opus 4.6 82f91d00c0 docs: implementation plan for the help/shortcuts/status work
Eleven tasks over the approved spec: build info and About, the plotter's
ctrl/alt axis-restricted zoom, 14 help pages, the accelerator table and
overview window, the app key controller, and the two-tier status bar.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-04 11:48:06 +02:00
Martino FerrariandClaude Opus 4.6 6b148f82a6 docs: design for the About/shortcuts/help windows and a two-tier status bar
Four features the user asked for together: an About dialog (version, build
timestamp, settings path), a shortcut overview covering the keyboard and the
mouse, a fourteen-page help window, and a status bar split into a short glance
line plus a details popover so the plot's own row stops being crowded.

Records one deviation from the approved design: keyboard zoom-x/zoom-y use
ctrl/alt rather than shift, because `+` *is* shift+`=` on the common layouts and
a bare zoom-in therefore already arrives with Shift set.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-04 11:08:23 +02:00
Martino Ferrari a066d48a3a plan: source chooser at startup
Seven tasks, each ending in a probe assertion rather than a unit test -- this
project's regression net is the headless probes, not a suite. The spec's
multicast round trip is split across three tasks so nothing in the plan needs a
group this machine can join.
2026-10-04 00:00:42 +02:00
Martino FerrariandClaude Opus 4.6 78065715b6 design: source chooser at startup
Launched with no --server, udpscope silently connects to 127.0.0.1:8000 and
offers no way to pick a source or see which ones it already knows. The design
is a pre-flight dialog: it runs before ScopeModel exists, so a cancel costs no
buffer and no socket and the existing pipeline is untouched.

The list is built from the stored session files, filtered to those naming at
least one signal -- a development machine accumulates one file per probe run on
a throwaway port, 140 of them here -- and the session format gains multicast and
interface keys so a row can reconnect exactly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-10-03 23:47:53 +02:00