Download docs/ds41-dsml-function-map.md from staccs/ds4-winhip: direct link, hf CLI and curl.
- Browser
- Download file 26.8 kB
-
https://huggingface.co/staccs/ds4-winhip/resolve/main/docs/ds41-dsml-function-map.md
- Command line
-
hf download hf://staccs/ds4-winhip/docs/ds41-dsml-function-map.md
-
curl -L -o ds41-dsml-function-map.md https://huggingface.co/staccs/ds4-winhip/resolve/main/docs/ds41-dsml-function-map.md
DS41-DSML function map
One row per function and per named constant of the DeepSeek-V4.1 encoding oracle, then one row per DSML function of ours that the oracle does not define. This is the review list: what the oracle decides, where this repo implements it, which case proves it, and what is still open.
The oracle is the official V4.1 text/vision encoding implementation,
operator-supplied 2026-10-18. It is the authority for the carriage this
project serves; the DSML line serves no other markup for the V4.1 family.
It is vendored byte-identical at tests/ds41_ref/oracle/encoding.py
(sha256 13231c592e958fb39e2af6001dae69d90e7a36b265702b9b390236fb387599e2,
provenance in tests/ds41_ref/oracle/README.md) and pinned by that hash in
tests/ds41_ref/ref_drive.py:46, so a later edit of it fails the run loudly.
Line numbers below are into that vendored file.
How to read a row
- Oracle: the function or constant, with its line in the oracle file.
- Decides: the one thing it fixes about the bytes.
- Ours: the counterpart in this repo,
file:line. "not located in this pass" means this session did not find a counterpart; it is a gap, not a claim that none exists. - Case: the reference case that exercises it, under
tests/ds41_ref/, with its fixture undercrates/ds4-server/tests/fixtures/ds41-ref/. - Verdict:
pass(measured byte-identical to the oracle's fixture today),OPEN(a case exists or is owed and the bytes differ),no case(no case yet; nothing measured),harness(test-side only, not a served rule),out of surface(defined by the oracle but outside the OpenAI chat wire).
Verdicts are measured, not assumed: pass for the encode side means our
prompt bytes equal the oracle's prompt bytes for that case
(crates/ds4-server/tests/ref_encoding.rs), and for the decode side that our
parse yields the oracle's call names and argument values
(crates/ds4-server/tests/ref_decode.rs).
Every file:line in this document was re-verified against the working tree on
2026-10-20 (each symbol grepped in its file); the earlier revision of this map
had been written against older files and carried a uniform 40-line offset on
render.rs and a varying one on parse.rs.
1. Encode side — request to prompt
| Oracle | Decides | Ours | Case | Verdict |
|---|---|---|---|---|
to_json (76) |
the JSON literal shape for non-string values: ensure_ascii=False, python separators |
crates/ds4-server/src/render.rs:336 pyjson_write, :329 pyjson_raw_value, crates/ds4-server/src/json.rs:379 json_minify_raw_value |
enc-argtypes-chat, dec-unicode |
pass |
tools_from_openai_format (84) |
the schema string handed to the model, in the client's declared order | crates/ds4-server/src/parse.rs:2405 parse_tools_value, :2011 openai_function_schema_from_tool, :2221 tool_schema_orders_add_json_wire |
enc-tools-chat |
pass |
_split_tool_name (101) |
the namespace::name split, and the two asserts around it |
not implemented on the chat surface: a tool-level namespace key is not read there and a ::-qualified name is taken as given (crates/ds4-server/src/parse.rs:2405); the Responses surface keeps a namespace for type:"namespace" items (:2282 responses_namespace_function_schema, :2336 append_responses_namespace_tool_schemas) |
tests/parse.rs chat_tool_names_pass_through_verbatim, responses_namespace_tools_keep_both_spellings |
out of surface on the chat wire the oracle decides: the client's model-facing tool spec is name/description/parameters only (conversation.rs:861-869, From<ToolDefinition> for ToolSpec :1039-1045), and its namespace rides the editor-facing ToolCall meta instead (acp_session.rs:1741 asserts the meta, not the wire), so no chat request can carry the field; the oracle's namespace::name qualification belongs to the message dicts it is handed |
_tool_name_for_encoding (116) |
the name printed in the invoke tag, namespace-qualified | crates/ds4-server/src/render.rs:576 append_dsml_tool_calls_text |
enc-argtypes-chat (bare name) |
pass for the bare name; the qualified form is out of surface with the row above |
tool_calls_from_openai_format (124) |
a replayed assistant's tool_calls array into the internal call shape |
crates/ds4-server/src/parse.rs:1079 parse_tool_calls_value, :1023 add_tool_call_id |
enc-argtypes-chat, enc-args-double |
pass |
decode_dsml_to_arguments (156) |
the arguments JSON string: string="true" wraps the body as a JSON string, string="false" takes the body as the value |
crates/ds4-server/src/tools.rs:376 parse_dsml_generated, :1262 json_value_is_complete, crates/ds4-server/src/json.rs:379 |
dec-one-call, dec-complex-args |
pass on names and values; our bytes are minified where the oracle keeps the body verbatim (matrix O-04) |
merge_tool_messages (184) |
a tool role becomes a tool_result block inside a user message, merging into an adjacent merged user |
crates/ds4-server/src/parse.rs:1330 parse_messages, :1411 append_tool_result_text; crates/ds4-server/src/render.rs:1484 |
enc-argtypes-chat |
pass |
sort_tool_results_by_call_order (233) |
tool_result blocks inside one user message are ordered by the preceding assistant's call order | crates/ds4-server/src/render.rs:1442 tool_run_in_call_order with :1421 preceding_call_order |
enc-toolresults-order |
pass (closed 2026-10-18) |
parse_tagged_text (273) |
the <image>path</image> text form expands to image blocks, and a malformed tag raises |
not implemented, by contract: pixels arrive base64-only (AGENTS.md), so the tag is never markup | tests/parse.rs the_image_path_tag_form_is_text_and_not_markup |
out of surface, measured 2026-10-20: no image is registered, the content keeps the tag, and the rendered prompt carries it verbatim |
_is_image_block (300) |
which content blocks count as images (image, image_url) |
crates/ds4-server/src/parse.rs:1134 parse_chat_image_url, :456 add_image_data_uri |
enc-image-chat |
case added 2026-10-20: the placeholder and its position agree with the oracle, the block separator does not (O-06) |
_extract_image (305) |
the url/data/source normalization, and the refusal when none is present | crates/ds4-server/src/parse.rs:415 add_image_base64, :456 |
enc-image-chat (the oracle's own record), tests/parse.rs image_refusals_cover_the_oracle_path_and_url_forms |
the oracle's path and URL forms are refused by name; the data-URI form is measured by enc-image-chat |
_process_image_blocks (323) |
images are replaced by <|deepseek_image|> in place, nested tool-result images included, and the records keep prompt order |
crates/ds4-server/src/render.rs:1388 append_dsml_user_content, crates/ds4-server/src/parse.rs:638 write_tool_image_placeholders |
enc-image-chat; tests/parse.rs openai_tool_message_carries_the_image_placeholder; tests/render.rs dsml_v41_places_tool_result_images_inside_the_envelope |
placeholder and order agree; the separator does not (O-06) |
_validate_no_image_sp_tokens (355) |
a user-supplied image placeholder in content or reasoning_content is refused |
crates/ds4-server/src/parse.rs:567 reject_stray_image_in_content, :578 reject_stray_image_in_reasoning, called from every place the client's own text is read (message content, text blocks, a tool result's text, reasoning_content, the Responses text blocks) |
stray_image_token.rs (9) and the served probes …/probe-placeholder-04a0ab1f-20261019 |
CLOSED at R7 (04a0ab1f): the oracle's two sentences on the wire; the engine does not implement the rule (core_v41_vision.c:189-193, server_parse_tools.c:290), so the port mirrors the oracle, CTR 8.9 |
process_image_messages (370) |
the per-message image pass, and content rebuilt from text blocks with \n\n |
crates/ds4-server/src/parse.rs:1167 parse_openai_message_content, :587 append_text_part |
enc-blocks-chat, enc-image-chat |
O-06 (measured 2026-10-20): the pieces and their order agree, the rebuild's \n\n does not |
_read_until_stop (399) |
the earliest-of-stops scan the whole parser is built on | crates/ds4-server/src/tools.rs:169 split_reasoning_content |
every dec-* case |
pass |
find_last_user_index (562) |
the last user-like index; a mid-conversation system message counts as one; a tool-result-only user message does not start a turn | crates/ds4-server/src/render.rs:1545 (the loop) with :263 role_is_user_like, :1050 (the drop rule's copy) |
enc-plain-chat, enc-midsystem-chat, enc-toolresult-only |
pass, both halves: mid-system closed 2026-10-18, the tool-result-only tail added 2026-10-19 |
render_reasoning_effort (456) |
the effort header: thinking mode, index 0 only, low/high/max mapped to 50/75/100, an integer in [1,100] taken as itself, None to high |
crates/ds4-server/src/render.rs:98 head_effort_prefix / :105 v41_effort_prefix with crates/ds4-server/src/route/mod.rs:383 RequestedEffort |
enc-think-low, enc-think-max, enc-think-budget |
pass (closed 2026-10-18) |
render_tools (509) |
the tools preamble verbatim, with the schema list appended | crates/ds4-server/src/render.rs:453 append_dsml_tools_prompt_text |
enc-tools-chat |
pass |
encode_arguments_to_dsml (524) |
one parameter element per argument key, in the call's key order, string="true" for a string child and its raw form otherwise; a non-dict arguments falls back to a single arguments key, a JSON string is decoded twice |
crates/ds4-server/src/render.rs:390 append_dsml_arg, :416 append_dsml_arguments_from_json, and the retry at :629 with crates/ds4-server/src/json.rs:476 json_string_contents |
enc-argtypes-chat, enc-args-double, enc-args-nondict, enc-args-plain |
pass on all four, measured 2026-10-20; the stringified-arguments pass was added to the render that day and the C engine has no such pass (ds4_server.c:3443-3463), which enc-args-double pins |
render_message (585) |
one message's bytes, the role branches, the transition tokens, and the assistant header rule | crates/ds4-server/src/render.rs:1484 render_dsml_chat_choice_ex |
19 encode cases | pass on every case measured; O-01 and O-02/O-03 closed 2026-10-18, the consecutive-assistant half of the header rule was found open and closed 2026-10-19 (enc-think-two-assistants, below) |
_drop_thinking_messages (749) |
before the last user, an assistant's reasoning_content is dropped, the message kept |
crates/ds4-server/src/render.rs:1634 keep_reasoning (the rule inline in the assistant branch rather than as a message pass) |
enc-think-drop-thinking, enc-drop-thinking, enc-think-tail-reasoning |
pass (closed 2026-10-19): the drop fires only in thinking mode with no tools anywhere, and the chat-mode case pins that the reasoning cannot leak |
_encode_messages_text (770) |
BOS, the loop over messages, and the drop rule forced false when any message carries tools | crates/ds4-server/src/render.rs:1484 (the entry) with its message loop at :1543 |
all encode cases | pass |
encode_messages (810) |
the public entry: merge, sort, image pass, then the text render | crates/ds4-server/src/render.rs:1484 behind the chat route |
all encode cases | pass on the cases measured |
load_cases (847) |
the harness entry: a case-level tools key moves into messages[0], a bare message list is wrapped |
tests/ds41_ref/ref_drive.py:68 |
every encode case | harness |
encode_case (872) |
the harness entry: encode one case and return its images, case thinking_mode winning over the command line |
tests/ds41_ref/ref_drive.py:70 |
every encode case; :83 for the media driver |
harness |
2. Decode side — completion to assistant message
| Oracle | Decides | Ours | Case | Verdict |
|---|---|---|---|---|
parse_tool_calls (890) |
the invoke/parameter grammar: >\n after every open tag, one name per invoke, the type attribute, duplicate parameters refused |
crates/ds4-server/src/tools.rs:376 parse_dsml_generated |
dec-one-call, dec-two-calls, dec-complex-args, dec-unicode, dec-marker-in-value |
pass |
parse_message_from_completion_text (946) |
the thinking split at </think>, content up to EOS or the block start, no content after the block, no special token left in content or reasoning |
crates/ds4-server/src/tools.rs:376, :169 split_reasoning_content |
every dec-* case, incl. dec-thinking |
pass |
tool_calls_to_openai_format (139) |
the wire shape: type: "function", function.name, function.arguments, namespace when present |
crates/ds4-server/src/stream.rs:173 append_tool_calls_json |
every dec-* case (the fixture is the oracle's parse) |
pass on names and values |
3. Constants and templates
Every literal the two sides must agree on, pinned against the oracle's own
source by crates/ds4-server/tests/ref_constants.rs (added 2026-10-20): the
test extracts the value the oracle assigns and compares it with ours, so a
row below is measured even when no case happens to carry the literal. Where
a row says "other families", the constant sits beside ours in the same table
but belongs to a model family outside this line.
| Oracle | Ours | Verdict |
|---|---|---|
bos_token (29) |
crates/ds4-server/src/render.rs:118 DSML_BOS |
pass |
eos_token (30) |
:121 DSML_EOS |
pass |
thinking_start_token (31) |
:126 DSML_THINK_START |
pass |
thinking_end_token (32) |
:127 DSML_THINK_END |
pass |
dsml_token (33) |DSML| |
folded into the tags below | pass |
USER_SP_TOKEN (35) |
:119 DSML_USER |
pass |
ASSISTANT_SP_TOKEN (36) |
:120 DSML_ASSISTANT |
pass |
SYSTEM_SP_TOKEN (426) |
:78 DSML_SYSTEM_TOKEN |
pass |
LATEST_REMINDER_SP_TOKEN (37) |
not located | out of surface: the role is not on the OpenAI wire |
IMAGE_PLACEHOLDER (39) |
:132 DSML_IMAGE |
pass (pinned by the constants leg and carried by enc-image-chat); the refusal beside it is measured by stray_image_token.rs |
IMAGE_TAG_PATTERN (40) |
not located | out of surface with the parse_tagged_text row above |
DS_TASK_SP_TOKENS / VALID_TASKS (43, 51) |
not located | out of surface: internal classification tasks, not on the OpenAI wire |
assistant_msg_template / wo_eos (60, 61) |
crates/ds4-server/src/render.rs:1655 (EOS always written) |
the template body is pinned by the constants leg; out of surface for wo_eos: no OpenAI request field reaches it (wo_eos appears nowhere under crates/), while the oracle reads it from the message dict for its own continuation flow (encoding.py:607, :697-703) |
response_format_template (64) |
crates/ds4-server/src/format.rs:19 parse_output_format_value, called from crates/ds4-server/src/parse.rs:3345, :3515, :3678; no render-side counterpart |
measured refusal, kept by decision: nothing is served that is not supported (see section 5) |
tool_output_template (68) |
crates/ds4-server/src/render.rs:1368 append_tool_result_text, written at :1619/:1621 |
pass via enc-argtypes-chat |
tool_calls_block_name (428) " calls" |
crates/ds4-server/src/tools.rs:44 DSML_TOOL_CALLS_START_SPACED |
pass |
tool_call_tag_name (429) " invoke" |
:46 DSML_INVOKE_START_SPACED, :47 end |
pass |
tool_parameter_tag_name (430) " parameter" |
:48 DSML_PARAM_START_SPACED, :49 end |
pass |
tool_call_template / tool_calls_template (432, 435) |
crates/ds4-server/src/render.rs:576-:652 |
pass |
REASONING_EFFORT_TEMPLATE (443) |
the line shape | crates/ds4-server/src/render.rs:44 V41_EFFORT_TEMPLATE with the 75/100 constants at :36/:38 |
REASONING_EFFORT_MAPPINGS / DEFAULT_REASONING_EFFORT (448, 453) |
low 50, high 75, max 100, None to high |
crates/ds4-server/src/route/mod.rs:368 mode_budget / :395 RequestedEffort::head_budget |
4. Ours with no oracle counterpart
Functions of this repo's DSML line that the oracle does not define. They are kept because a served turn needs them, not because the oracle asks for them.
| Ours | Why it exists | Covered by |
|---|---|---|
crates/ds4-server/src/tools.rs:2157 find_tool_start (with crates/ds4-server/src/dsml.rs:389 dsml_find_tool_start) |
the streaming tracker: the artifact's root spelling (quoted, unquoted, short) is not the oracle's canonical one, so the scan is tolerant where the oracle's grammar refuses | tests/dsml.rs syntax_greedy_spaced_dsml; dsml.rs override_tests::auto_tool_start_forces_function_prefix, ::required_prefix_finishes_after_tool_start |
crates/ds4-server/src/dsml.rs:550 dsml_decode_state_for_text |
the incremental decode state machine that decides when markup is withheld from the stream | tests/dsml.rs syntax_greedy_spaced_dsml; generate.rs disk_sync_tests::continued_decode_gate_stops_after_tool_syntax_begins |
crates/ds4-server/src/tools.rs DSML_*_SHORT, DSML_* (25-36) |
the two older root spellings the artifact still emits; the oracle knows only the spaced one | tests/dsml.rs syntax_greedy_spaced_dsml plus the dec-* cases through ref_decode |
crates/ds4-server/src/parse.rs:2282 responses_namespace_function_schema, :2336 append_responses_namespace_tool_schemas |
the Responses type:"namespace" item: the prompt advertises namespace + function and the order keeps both spellings so the answer can be emitted under the client's own name (crates/ds4-server/src/stream.rs:234 responses_tool_wire_name, :287 the namespace field) |
tests/parse.rs responses_namespace_tools_keep_both_spellings |
crates/ds4-server/src/retry.rs:144 try_repair_dsml |
truncated-call repair; the oracle has no repair path (matrix G8, P3) | tests/retry.rs dsml_repair_matches_the_reference_balance_contract, repair_and_decide_match_c |
crates/ds4-server/src/render.rs:1681 responses_visible_assistant_suffix |
the visible assistant tail a Responses client can replay (ds4_server.c:12821); the store that consumes it (responses_live_remember :14559) is the reserved checkpoint unit |
tests/render.rs responses_visible_suffix_matches_client_replay |
crates/ds4-server/src/trace.rs |
C --trace FILE (ds4_help.c:323): one section per request with the prompt, its token ids and the parsed message (unit 3.2, TP-12/TP-15) |
trace.rs tests::a_section_carries_the_prompt_tokens_and_parsed_message |
crates/ds4-server/src/retry.rs:260 find_any_tool_start, :277 find_last_structural |
the repair path's style choice and think scope (ds4_server.c:5842, :5890), ported with G8 | tests/retry.rs repair_and_decide_match_c |
crates/ds4-server/src/render.rs:133-:179, other-family markers |
Motif, EXAONE, Solar, Qwen, GLM, K2 tag sets: other families, outside this line | tests/render.rs glm53_chat_and_image_protocol_match_official_template, ling31_uses_bailing_syntax; tests/parse.rs qwen_image_inputs_normalize_across_all_three_surfaces |
crates/ds4-server/src/render.rs:202 syntax_for_model_id |
family dispatch; the oracle covers V4.1 only | tests/render.rs syntax_for_model_id_matches_c |
5. The open rows
Measured on 2026-10-20, in order of what decides them. Every row above now carries a verdict; these are the ones that needed a judgement rather than a fixture.
- O-06, the block separator (rows
process_image_messages,_process_image_blocks,_is_image_block,process_image_messages): the oracle joins the pieces a client sent as separate content blocks with a blank line --_process_image_blocksturns an image into a<|deepseek_image|>text block and the user branch writes"\n\n".join(parts)(encoding.py:323-353, :631-656, and thecontentrebuild at :386-393) -- while this port concatenates them: at parse time for text pieces (append_text_part, parse.rs:587) and at render time for the placeholder (append_dsml_user_content, render.rs:1388). The C lineage concatenates too (ds4_server.c:5865), so the port inherited it. Measured the same day onenc-image-chatandenc-blocks-chat: the two prompts carry the same pieces in the same order and differ by that one separator, which is exactly what those two cases pin (check_block_join). A third place shares the rule and has no case yet: a tool-result message's mixed blocks (write_tool_image_placeholders, parse.rs:638), asserted today as"shot:<placeholder>done"bytests/parse.rsopenai_tool_message_carries_the_image_placeholder. The fix is one join, gated to the V4.1 head, in the render's parts loop plus the parse's text pieces; it is the operator's call because it moves every image-bearing prompt and every multi-block message on that head. - The image surface's oracle leg (rows
parse_tagged_text,_is_image_block,_extract_image,_process_image_blocks,_validate_no_image_sp_tokens,process_image_messages):ref_drive.pyrefused image cases by construction (assert not images, kept for the text-only legs), so the leg was rebuilt on 2026-10-20 asencode-media(ref_drive.py:75), which prints the prompt and reports the oracle's own image records on stderr._validate_no_image_sp_tokensis CLOSED at R7; the other five now have the oracle's bytes, with O-06 the only difference. - The namespace surface (rows
_split_tool_name,_tool_name_for_encoding): out of surface. The client's model-facing tool spec is name/description/parameters only (conversation.rs:861-869, the conversion at:1039-1045) and itsnamespacerides the editor-facing ToolCall meta (acp_session.rs:1741), so no chat request can carry the field and a::-qualified name is never sent. Both halves are pinned bytests/parse.rs(chat_tool_names_pass_through_verbatim,responses_namespace_tools_keep_both_spellings). wo_eos(assistant_msg_template/wo_eos): out of surface.wo_eosappears nowhere undercrates/; the oracle reads it from the message dict for its own continuation flow (encoding.py:607,:697-703).IMAGE_TAG_PATTERN, the<image>path</image>text form, andLATEST_REMINDER_SP_TOKEN/DS_TASK_SP_TOKENS/VALID_TASKS: out of surface. The carriage is base64-only by design (AGENTS.md) and the rest are internal classification roles, not on the OpenAI wire. The tag form is measured rather than assumed (tests/parse.rsthe_image_path_tag_form_is_text_and_not_markup).response_formatrendering (rowresponse_format_template): the one row whose verdict is a decision and not a measurement. Ours refusesjson_objectandjson_schemawith 400 "structured output is unsupported" (format.rs:6-15, the v0.6.3 refusal of ds4-on-spark#10), measured on the served binary today; the oracle renders the schema into the prompt instead (encoding.py:628-629with the template at:64). The client of record can putresponse_format: json_schemaon the chat wire, fromConversationRequest.json_schema(conversation.rs:4107), at two production sites -- the auto-mode permission classifier (sampler_turn.rs:862) and the goal evaluator (goal_evaluator.rs:172) -- while its main turns hardcode None (request_builder.rs:148, samplerclient.rs:4071), and no capability gate guards those two sites. The operator decided 2026-10-20 that the refusal stands: the field carriesstrict: true, i.e. a guarantee that the served text conforms to the schema, and only a decode-time constraint can give that guarantee. A prompt-side instruction would be an invented approximation of a guarantee, so nothing is served for this field, and the client's own error branch records the side query as failed (session_summary.rs:131-137for the neighbouring title query). Serving it for real means a JSON-schema-constrained decoder, a unit of its own.- O-04 arguments bytes: ours minify, the oracle keeps the parameter body verbatim. Values agree; the strings differ. Recorded, not a defect against the wire, since the client echoes our string.
Closed since the map was written: O-01 (the effort budget, both halves), O-02
(a mid-conversation system message renders in place), O-03 (tool results
ordered by call order), the repair rows of G8/G9, the image-placeholder
refusal (R7), the drop-thinking rule together with its chat-mode counterpart,
the stringified-arguments fallback (2026-10-20, enc-args-double), and the
find_last_user_index tool-result-only half (enc-toolresult-only).
One divergence was found by the 2026-10-19 pass and closed in code. Two
assistant messages in a row share one <Assistant> header, so the second has
no header to open its thinking, and the oracle still writes that message's
reasoning and its closer as part of the body (encoding.py:678-712).
Measured before the fix: ours rendered 275 bytes against the oracle's 304,
dropping SECOND-THOUGHT-MARKER whole (enc-think-two-assistants). The
assistant branch now writes the thinking body when it has no header, gated to
the V4.1 head so the port's own head keeps its bytes.
One divergence was found by the 2026-10-20 pass and closed in code. A
replayed call whose arguments is itself a JSON string still renders that
object's parameters, because the oracle decodes the field twice before it
gives up (encoding.py:528-537). Measured before the fix: ours wrote one
arguments parameter carrying the quoted text (1534 bytes against the
oracle's 1511, enc-args-double). append_dsml_tool_calls_text now retries
the parse on the unwrapped string (json_string_contents, json.rs:476), and
the C engine's lack of that pass is pinned by the same case.
6. Reproducing
# the oracle's own bytes for a case, and its parse of a completion
python3 tests/ds41_ref/ref_drive.py encode tests/ds41_ref/cases/enc-plain-chat.json chat
python3 tests/ds41_ref/ref_drive.py encode-media tests/ds41_ref/cases/enc-image-chat.json chat
python3 tests/ds41_ref/ref_drive.py parse tests/ds41_ref/decode/dec-one-call.txt chat
# the three reference legs against the pinned fixtures
cargo test -p ds4-server --test ref_encoding
cargo test -p ds4-server --test ref_decode
cargo test -p ds4-server --test ref_constants
Regenerate the fixtures only when the oracle or a case changes:
bash tests/ds41_ref/regen_fixtures.sh.