docs: disposition the offline enqueue seam as a named rule-1 carve-out - #301
Merged
Conversation
examples/IMPLEMENTATION.md rule 1 forbids domain-shaped logic outside a model, and docs/spec/offline/offline.md's worked example puts the enqueue-on-offline decision in a free function. The placement was never dispositioned, so a reader could not tell an oversight from a sanctioned exception. Rule 1's own last clause -- "If logic can't be expressed in a model, that is a finding" -- already anticipates this outcome. Record the finding's disposition instead of moving the seam: the queue stays passive and the seam stays on the write path. Closes #197
The disposition reads as the closing subsection of "Ownership: who enqueues", and the worked example now names it inline, so a reader who stops at the code block still sees the exception is recorded.
field_outbox.hpp justified its placement with rule 4's WASM clause, which does not apply -- the offline stack is desktop-first by both kanban's and this rung's own scoping. The honest reason is narrower: this rung deploys its field client remotely, so the machine making the enqueue decision has no model on it. Point both the header and the rung's gap-ledger entry at rule 1's named carve-out.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
Takes the disposition #197 asks for: document the carve-out. The seam does
not move, the queue stays passive, and no framework primitive is built.
What the carve-out says
docs/spec/offline/offline.mdgains a closing subsection to "Ownership: whoenqueues" — "Disposition: app-layer by design (the rule-1 carve-out)" — and
the worked example now names it inline, so a reader who stops at the code block
still sees the exception is recorded:
The subsection makes four claims:
be expressed in a model, that is a finding, and that document's prime
directive says the same of the framework itself. This carve-out is that
finding's outcome, not an argument that the rule is wrong."
retracted WASM one:
enqueue()is the write path's last act before the wire,taken when the wire is unavailable. In the canonical wiring the client keeps
an in-process
LocalBackend, so a client-side model does exist and couldown the decision; on a remote deployment it does not, and "the one machine
that must decide 'queue this instead of sending it' is the one machine with no
model on it."
isOnline()/catching afailed
execute(), minting the idempotency key, serialising,enqueue(),surfacing queue depth, and client-local bookkeeping that keeps this client's
own queued items consistent. Model: validation, authorization, journal dedup,
conflict classification — everything the payload means once it lands. The last
seam row "is the sharp edge: it is genuinely domain-shaped, and the carve-out
sanctions it only in a dedicated app-layer write-path class — never in a
presenter, a QML bridge, or
main()."dispositioned in the spec as app-layer by design" branch, taken at two
occurrences of the transport-shaped seam and one of the domain-shaped
version chaining. "A third rung independently growing its own
enqueue-on-offline path is the trigger to reopen the question of a
framework-owned outbox dispatcher — a standing disposition must not become the
reason a third reinvention goes unexamined."
Reference examples — named, not invented
Both already exist on master, so the carve-out points at shipped code:
examples/lims/include/lims/offline/field_outbox.hpp— the domain-shaped half(local version ledger, base-version stamping, chaining a client's own second
offline edit onto its first). Replay still goes through
SampleModel::execute(QueuedCapture).examples/kanban/gui_lib/board_qml_bridge.cpp— the transport-shaped halfonly. It lives in a presenter, which rule 1 names as forbidden for
domain-shaped code; nothing there is domain-shaped, so it stands as glue
under rule 2's "pure glue with no domain logic" justification. Anything
carrying a domain invariant belongs in a
FieldOutbox-shaped class instead.Where it is placed
examples/IMPLEMENTATION.mddocs/spec/offline/offline.md###closing "Ownership: who enqueues"; the worked example points at it.examples/lims/…/field_outbox.hpp,examples/lims/README.mdQuotes verified against master (e8c8364)
examples/IMPLEMENTATION.mdrule 1 — verbatim, including the finalclause the issue leans on.
docs/spec/offline/offline.md, "Ownership: who enqueues" — "The queueis passive", "detecting an offline/failed
execute()and callingenqueue()is the application's job", "The seam is on the write path, not inside
morph::offline" — all verbatim, and the worked example is still the freefunction
submit()the issue describes.README: "inherits kanban's WASM-offline scope limits — desktop-first". The
issue is right to drop rule 4's WASM clause.
board_qml_bridge.cpp(now line 310), doingexactly probe →
opId→ serialise →enqueue→ queue depth.Drift found
field_outbox.hppis described as unmerged (origin/ladder-lims-rung6,lims: rung 6 — a LIMS, and the forms subsystem at full depth #177). It landed on master in 93a6168. This does not change the arithmetic —
still two transport-shaped occurrences, one domain-shaped — but the disposition
is about shipped code, not a branch.
grep -rn onBackendChanged examples/returns nothing — noshipped rung uses this seam at all" is stale: lims'
SampleModelimplementsit and
test_backend_matrix.cppdrives it. The issue's conclusion survives(lims README §11 calls it a fail-closed backstop, not the primary replay path,
and Model::onBackendChanged() runs with no session, so the documented rich-outcome replay seam cannot perform an authenticated replay #201 records that it cannot carry a session), but the "no rung uses it"
phrasing is no longer true. Read path only; nothing in this PR depends on it.
field_outbox.hpp's own header comment justified its placement with"rule 4's WASM clause" — the argument The offline enqueue seam is app-layer by default and has never been dispositioned #197 explicitly retracts. Corrected
here to the narrower, honest one (this rung deploys its field client remotely),
and repointed at the carve-out. The rung README's gap-ledger entry for The offline enqueue seam is app-layer by default and has never been dispositioned #197 is
likewise marked dispositioned.
Deliberately not done
OutboxDispatcherand no change toIOfflineQueue: the queuestays passive and the seam stays on the write path, exactly as the spec says.
the rule-1 conflict there is soft, as The offline enqueue seam is app-layer by default and has never been dispositioned #197 itself concludes.
include/touched, so no spec-sync obligation.bash scripts/check_spec_citations.shpasses (547 references scanned);clang-format --dry-run -Werrorclean on the touched header.Closes #197