Skip to content

fix(figc4.4, figc4.5): DL-FLOW-OFF acts on the not-busy branch, and the busy arms stage N(r) (#43) - #109

Draft
M0LTE wants to merge 3 commits into
fix/47-timer-recovery-drain-incrementfrom
fix/43-dl-flow-off-branch
Draft

M0LTE wants to merge 3 commits into
fix/47-timer-recovery-drain-incrementfrom
fix/43-dl-flow-off-branch

Conversation

@M0LTE

@M0LTE M0LTE commented Sep 10, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #43. Stacked on PR #107 (#47): base is fix/47-timer-recovery-drain-increment because both touch figc4.5; retarget to main once #107 merges (the three commits here are the only delta).

What changed (both figc4.4 Connected and figc4.5 Timer Recovery).

  1. The figc4.4 DL-FLOW-OFF: Set-Own-Receiver-Busy/RNR on the wrong branch — a not-busy station can never enter busy via DL-FLOW-OFF #43 branch swap. The "DL-FLOW-OFF Request" column asks "Own Receiver Busy?" and hung "Set Own Receiver Busy, RNR Response, Clear Acknowledge Pending" off the Yes arm, the No arm returning straight to the state, so a not-busy station could never enter the busy condition through DL-FLOW-OFF, which is the primitive's whole purpose. The Yes and No labels on that decision's two edges are swapped, so the actions run when the station is NOT busy and an already-busy station takes the no-op arm. The mirror DL-FLOW-ON column, which acts on its Yes (busy) arm, is unchanged.
  2. The H3 staging box. A N(r) <- V(r) processing box is inserted immediately before "RNR Response" on the DL-FLOW-OFF action arm and immediately before "RR Command" on the DL-FLOW-ON busy arm, on both pages, matching how every other RR/RNR emission on these pages stages N(r) first (Transmit Enquiry and Enquiry Response on figc4.7, the I-frame arms). Per ax25sdl docs/explorer.md "Novel findings" H3, the interpreter treats an emission without a staged N(r) as a machine error, so the arm had to become executable in the same change that makes it reachable; every implementation sends N(r) = V(r) on that RNR.

Layout: the boxes below each insertion, the shared terminal state and the second bend point of the two decision-to-state edges move down by one box plus the column's existing gap (90 on figc4.4, 95.62 on figc4.5), so every original gap is preserved. Checked mechanically against main: no node overlaps and no edge/box crossings introduced on either page.

Why. §5.3 defines DL-FLOW-OFF.Request as "temporarily suspend the flow of incoming information"; §6.4.10: "Whenever a TNC enters a busy condition, it indicates this by sending an RNR response at the next opportunity." The figure gated the busy entry on already being busy, contradicting its own primitive, and the DL-FLOW-ON symmetry (act only when there is a busy condition to clear) says FLOW-OFF must act on not-busy. The prose already states the behaviour, so the v2.3 draft only gains a "Changes from Version 2.2" table row.

Evidence (#93 rule 1: the fix flips its evidence).

  • ax25sdl explorer calibration row figc4.4 DL-FLOW-OFF: Set-Own-Receiver-Busy/RNR on the wrong branch — a not-busy station can never enter busy via DL-FLOW-OFF #43 (docs/explorer.md, current tables): Connected seed, A sends 2, B's layer 3 turns flow off after its first delivery, no faults; the busy-tracks-flow invariant fires at 3 steps with B taking t05_dl_flow_off_request_no (the empty not-busy arm) and staying not-busy and silent; with that check off, flow-off delivery fires at 5 (B delivers a1 upward with flow off). Tests Defect43_* in CalibrationTests.cs, including Defect43_Note_The_Busy_Arms_Emit_RNR_And_RR_Without_N_r_Staging for H3.
  • Golden trace traces/dl-flow-off-sets-busy.trace.yaml (expected_failure on figc4.4 DL-FLOW-OFF: Set-Own-Receiver-Busy/RNR on the wrong branch — a not-busy station can never enter busy via DL-FLOW-OFF #43): Connected, not busy, DL_FLOW_OFF_request should leave own_receiver_busy: true and emit an RNR response carrying V(r).
  • packet.net quirk Ax25Spec43DlFlowOffEntersBusy (src/Packet.Ax25/Session/Ax25SessionQuirks.cs, default on): inverts the own_receiver_busy guard for DL_FLOW_OFF_request only.
  • No implementation corroboration exists, as the issue says: neither direwolf (src/ax25_link.c: "dl_flow_off - not implemented", "we currently don't have a way to set own_receiver_busy") nor linbpq exposes the primitive. The fix rests on the prose, the figure's self-contradiction and the FLOW-ON symmetry.

Regeneration. connected.sdl.yaml and timer_recovery.sdl.yaml regenerated with the Packet.Sdl.Transcribe walker at the pinned tooling ref (68/39 and 92/47 transitions/decisions, unchanged counts). Because the walker's suffix follows the edge label, t05_dl_flow_off_request_yes and _no swap meaning on both pages (_no now carries the actions, _yes is the no-op); t05_..._no, t06_dl_flow_on_request_yes_yes and _yes_no gain the N(r) := V(r) step. Both *.citations.yaml sidecars are re-keyed accordingly (every key still names a transition; checked) and annotated with CORRECTION APPLIED (#43) headers, ERRATUM (#43) notes on both t05 arms and the H3 note on both t06 busy arms. Mermaid and SVG regenerated; totality lint clean (8051 feasible valuations, 0 holes, 0 overlaps).

svgdiff (python3 tools/svgdiff/svg_diff.py fix/47-timer-recovery-drain-increment fix/43-dl-flow-off-branch --all --merge-base --format markdown):

spec-sdl/v2.2-errata/data-link/svg/DataLink_Connected.svg

nodes 182→184, edges 211→213, labels 73→73

what change
+ node N(r) <- V(r)
+ node N(r) <- V(r)
+ edge Clear Own Receiver Busy → N(r) <- V(r)
+ edge N(r) <- V(r) → RNR Response
+ edge N(r) <- V(r) → RR Command
+ edge Set Own Receiver Busy → N(r) <- V(r)
− edge Clear Own Receiver Busy → RR Command
− edge Set Own Receiver Busy → RNR Response

spec-sdl/v2.2-errata/data-link/svg/DataLink_TimerRecovery.svg

nodes 241→243, edges 272→274, labels 113→113

what change
+ node N(r) <- V(r)
+ node N(r) <- V(r)
+ edge Clear Own Receiver Busy → N(r) <- V(r)
+ edge N(r) <- V(r) → RNR Response (F = 0)
+ edge N(r) <- V(r) → RR Command (P = 0)
+ edge Set Own Receiver Busy → N(r) <- V(r)
− edge Clear Own Receiver Busy → RR Command (P = 0)
− edge Set Own Receiver Busy → RNR Response (F = 0)

Note that svgdiff's edge signature does not carry the edge label, so the Yes/No swap is invisible to it (label counts 73 to 73 and 113 to 113 because both words still exist). The graphml multiset diff against the base branch (source label, edge label, target label) shows the swap explicitly, and nothing else:

DataLink_Connected.graphml
  + node x2  N(r) <- V(r)
  + edge x1  Clear Own Receiver Busy ----> N(r) <- V(r)
  + edge x1  N(r) <- V(r) ----> RNR Response
  + edge x1  N(r) <- V(r) ----> RR Command
  + edge x1  Own Receiver Busy? --No--> Set Own Receiver Busy
  + edge x1  Own Receiver Busy? --Yes--> 3 Connected
  + edge x1  Set Own Receiver Busy ----> N(r) <- V(r)
  - edge x1  Clear Own Receiver Busy ----> RR Command
  - edge x1  Own Receiver Busy? --No--> 3 Connected
  - edge x1  Own Receiver Busy? --Yes--> Set Own Receiver Busy
  - edge x1  Set Own Receiver Busy ----> RNR Response
DataLink_TimerRecovery.graphml
  + node x2  N(r) <- V(r)
  + edge x1  Clear Own Receiver Busy ----> N(r) <- V(r)
  + edge x1  N(r) <- V(r) ----> RNR Response (F = 0)
  + edge x1  N(r) <- V(r) ----> RR Command (P = 0)
  + edge x1  Own Receiver Busy? --No--> Set Own Receiver Busy
  + edge x1  Own Receiver Busy? --Yes--> 4 Timer Recovery
  + edge x1  Set Own Receiver Busy ----> N(r) <- V(r)
  - edge x1  Clear Own Receiver Busy ----> RR Command (P = 0)
  - edge x1  Own Receiver Busy? --No--> 4 Timer Recovery
  - edge x1  Own Receiver Busy? --Yes--> Set Own Receiver Busy
  - edge x1  Set Own Receiver Busy ----> RNR Response (F = 0)

ax25sdl follow-up: remove the expected_failure marker from traces/dl-flow-off-sets-busy.trace.yaml, turn the #43 explorer calibration row clean, delete the tables-43fixed fixture once the pin lands.

🤖 Generated with Claude Code

https://claude.ai/code/session_014qyx5dT8rmRTb8szT4SpEQ


Status (paused on Tom's order, 2026-09-10). Everything for #43 is finished and pushed: both graphml edits (label swap + N(r) staging box on figc4.4 and figc4.5), yaml/mermaid/SVG regeneration, both sidecars re-keyed and annotated, draft-doc row. Nothing is half done. CI does not fire on a PR whose base is not main, so instead: (a) the transcribe drift was checked locally with the pinned ax25sdl tooling ref ff5bc7d (no drift), (b) a fresh render matches the committed SVGs, (c) ci.yml was dispatched by hand on this branch (run 34539506948) and svg-figure-diff for this PR (run 34539508708, success). To resume: once #107 merges, gh pr edit 109 --base main, which also makes the normal PR checks run. Worktree: /home/tf/ax25spec-wt/fix43.

M0LTE and others added 3 commits September 10, 2026 22:50
…rms stage N(r) (#43)

The Connected DL-FLOW-OFF column asked "Own Receiver Busy?" and hung "Set Own Receiver Busy, RNR Response, Clear Acknowledge Pending" off the Yes arm, the No arm returning straight to the state, so a not-busy station could never enter the busy condition through DL-FLOW-OFF, which is the primitive's whole purpose (5.3 "temporarily suspend the flow of incoming information"; 6.4.10 "Whenever a TNC enters a busy condition, it indicates this by sending an RNR response at the next opportunity"). The mirror DL-FLOW-ON column acts on its Yes (busy) arm and is untouched. See #43.

- DataLink_Connected.graphml: the Yes and No labels on edges e46 and e50 are swapped. Two new "N(r) <- V(r)" processing boxes: n182 before RNR Response on the DL-FLOW-OFF arm and n183 before RR Command on the DL-FLOW-ON busy arm (ax25sdl explorer note H3: every other RR/RNR emission on the page stages N(r) first and the interpreter treats an unstaged emission as a machine error, so the arm had to become executable in the same change that makes it reachable). e47/e54 retargeted to the new boxes, e211/e212 continue to the emissions. The boxes below each insertion, the shared "3 Connected" terminal and the second bend point of the two decision-to-state edges move down by 90 (one box plus the column's 30 gap), so every original gap is preserved; no node overlaps or edge/box crossings introduced (checked mechanically against main).
- connected.sdl.yaml regenerated by the Packet.Sdl.Transcribe walker: still 68 transitions / 39 decisions; t05_dl_flow_off_request_yes and _no swap meaning (the walker's suffix follows the edge label), and t05_..._no, t06_dl_flow_on_request_yes_yes and _yes_no gain the "N(r) := V(r)" step.
- connected.citations.yaml re-keyed (the two t05 entries swap keys) and annotated: CORRECTION APPLIED (#43) header, ERRATUM (#43) notes on both t05 arms and the H3 note on both t06 busy arms, with the evidence (ax25sdl explorer calibration row #43, golden trace dl-flow-off-sets-busy, packet.net quirk Ax25Spec43DlFlowOffEntersBusy, the direwolf and linbpq negative evidence); the legacy-id mapping table gains a note.
- connected.g.mmd + DataLink_Connected.svg regenerated. Totality lint clean (8051 feasible valuations, 0 holes, 0 overlaps).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qyx5dT8rmRTb8szT4SpEQ
…rms stage N(r) (#43)

Timer Recovery carries the same pair of flow-control columns as figc4.4, with the emissions written out as "RNR Response (F = 0)" and "RR Command (P = 0)". Same correction as the figc4.4 commit, kept separate so the two figures can be reviewed and reverted independently.

- DataLink_TimerRecovery.graphml: Yes/No labels on e33 and e43 swapped; new "N(r) <- V(r)" boxes n244 (before RNR Response (F = 0)) and n245 (before RR Command (P = 0)); e35/e36 retargeted, e274/e275 continue to the emissions. Boxes below each insertion, the shared "4 Timer Recovery" terminal and the second bend point of the two decision-to-state edges move down by 95.62 (one box plus this column's 35.6 gap), preserving every original gap; no node overlaps or edge/box crossings introduced (checked mechanically against main).
- timer_recovery.sdl.yaml regenerated: still 92 transitions / 47 decisions; t05_dl_flow_off_request_yes and _no swap meaning; t05_..._no, t06_dl_flow_on_request_yes_yes and _yes_no gain the "N(r) := V(r)" step.
- timer_recovery.citations.yaml: the t05 entry re-keyed to _no and annotated (ERRATUM (#43)); a t05_..._yes entry added for the now-empty arm; H3 note on t06_..._yes_no and a new t06_..._yes_yes entry carrying it; CORRECTION APPLIED (#43) header.
- timer_recovery.g.mmd + DataLink_TimerRecovery.svg regenerated (the srej-received viewport is untouched).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qyx5dT8rmRTb8szT4SpEQ
…r) staging (#43)

"Changes from Version 2.2" table: row appended for the Figure C4.4 / C4.5 DL-FLOW-OFF decision (actions now on the not-busy arm) and the "N(r) := V(r)" box inserted before the RNR and RR emissions on the flow-control busy arms. No prose change: 5.3 defines DL-FLOW-OFF as suspending the flow of incoming information and 6.4.10 already says a TNC entering the busy condition sends an RNR response at the next opportunity.

Refs #43.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014qyx5dT8rmRTb8szT4SpEQ
@github-actions

Copy link
Copy Markdown

SVG figure changes (451778f7 → 4f032d64)

spec-sdl/v2.2-errata/data-link/svg/DataLink_Connected.svg

nodes 182→184, edges 211→213, labels 73→73

what change
+ node N(r) <- V(r)
+ node N(r) <- V(r)
+ edge Clear Own Receiver Busy → N(r) <- V(r)
+ edge N(r) <- V(r) → RNR Response
+ edge N(r) <- V(r) → RR Command
+ edge Set Own Receiver Busy → N(r) <- V(r)
− edge Clear Own Receiver Busy → RR Command
− edge Set Own Receiver Busy → RNR Response

spec-sdl/v2.2-errata/data-link/svg/DataLink_TimerRecovery.svg

nodes 241→243, edges 272→274, labels 113→113

what change
+ node N(r) <- V(r)
+ node N(r) <- V(r)
+ edge Clear Own Receiver Busy → N(r) <- V(r)
+ edge N(r) <- V(r) → RNR Response (F = 0)
+ edge N(r) <- V(r) → RR Command (P = 0)
+ edge Set Own Receiver Busy → N(r) <- V(r)
− edge Clear Own Receiver Busy → RR Command (P = 0)
− edge Set Own Receiver Busy → RNR Response (F = 0)

Interactive viewers — swipe, onion-skin, blink, and a clickable change list that zooms to each change — are attached to this run as the svg-figure-diff artifact: download, unzip, open the HTML.

Locally: python3 tools/svgdiff/svg_diff.py main HEAD --all --merge-base

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.

1 participant