Responses grow server-first: align rules 1 and 6 with already_present - #6
Merged
Merged
Conversation
…sent answer The schema has carried the already_present ticket since v0.1.0, yet rules 1 and 6 still required the shipper to reject unknown response fields. Port the wording from trajectories-research #333: the server decodes requests strictly, the client ignores response fields it does not know and validates every field it acts on. Documentation only.
jakozaur
force-pushed
the
responses-grow-server-first
branch
from
September 16, 2026 14:50
6baabf1 to
6f4015f
Compare
jakozaur
marked this pull request as ready for review
September 16, 2026 14:52
constantfold
approved these changes
Sep 16, 2026
constantfold
force-pushed
the
responses-grow-server-first
branch
from
September 16, 2026 15:04
15aebc7 to
6f4015f
Compare
mieciu
pushed a commit
to QuesmaOrg/quesma-shipper
that referenced
this pull request
Sep 21, 2026
…he archive (#27) Upstream: [QuesmaOrg/trajectories-research#333](QuesmaOrg/trajectories-research#333) Port of trajectories-research #333, the first of four upstream PRs that were merged on 2026-09-02 and 2026-09-03 but missed the public import (the snapshot was cut between #339 and #333). Stack, in upstream merge order: this PR, then #28 (#321), #29 (#318), #30 (#340). ## What changes - Any `fingerprints.json` that cannot be loaded (parse, schema, checksum, another install, oversize, unreadable) is discarded with one warning and the run continues from an empty store. The first flush replaces the file. Before, the daemon refused to run and pointed at `state prune` or `state reset`. - Every authorize request is answered against the archive: when the control plane reports `already_present: true` for a stored source hash, the shipper skips the PUT. A lost store now costs a re-hash, not a re-upload. - The authorize response is no longer strict-decoded, so it can grow server-first. The closed required-header set is unchanged. ## Example An install whose state file was truncated by a full disk: ``` state: fingerprints.json unloadable (parse: unexpected end of JSON input); starting from an empty store ``` The next flush authorizes every object; the control plane answers `already_present` for the ones the archive holds under the same hash, and only the new ones are uploaded. ## Port notes - The wire-side `AlreadyPresent` ticket field already came in with the shipper-protocol module, so this PR adds only the client behaviour around it. - Needs the control plane to answer `already_present`; without it the client still works, it just uploads everything as before. ## Compatibility Server side: the control plane and the fleet manager both gained the `already_present` answer in the same upstream PR, and that code is on trajectories-research main. The fleet manager HEADs each key and answers `already_present` only when the stored `source-hash` metadata matches; a failed HEAD logs once and authorizes the object as new. HeadObject is covered by the existing `s3:GetObject` grant. Whether the deployed dogfooding instances run a build with it is not verified here. Client matrix: | client | server | result | |---|---|---| | main today | answers `already_present` | works: main already decodes that shape and skips the PUT, but does not mark the audit line | | this PR | never answers `already_present` | works: full tickets, unchanged path | | main today | adds any other new response field | every authorize fails: main decodes with unknown fields disallowed | | this PR | adds any other new response field | works: unknown fields ignored, every field acted on is still validated | Protocol text gap: the public shipper-protocol v0.1.0 `PROTOCOL.md` still states rule 1, "the client decodes every response with unknown fields disallowed", and rule 6, "V2 upload messages are strict both ways", while its schema already carries the `already_present` ticket. Upstream #333 amended both rules to "responses grow server-first, requests client-first"; that amendment is ported in [QuesmaOrg/shipper-protocol#6](QuesmaOrg/shipper-protocol#6), a docs-only change with no release needed. ## Verification `make check` (fmt, vet, deadcode, licenses, race) passes on this branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
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.
Upstream: QuesmaOrg/trajectories-research#333, the protocol-text half. The client half is QuesmaOrg/quesma-shipper#27.
What changes
Docs only. No schema or fixture bytes change, and
go test ./...passes./v2/uploads/authorizeresponse description and the rules intro say the same thing in one sentence each.already_presentas its single exception.Why
The v0.1.0 schema already defines the
already_presentticket, but the published rules 1 and 6 still say the client rejects unknown response fields. quesma-shipper#27 makes the client tolerant, so once it merges the client contradicts the published text. Upstream fixed the wording in the same PR that added the field; that edit never reached this repo.Example
A server may now add a field to a ticket, say
"region": "eu-west-1", before the fleet updates. An old shipper on v0.1.0 semantics would fail every authorize call; a shipper on these semantics ignores the field and still validates the URL, key, headers and length before sending a byte.Release
None needed. Documentation only; consumers keep v0.1.0 and no fixture or schema moves.
🤖 Generated with Claude Code