dexd: the player, extracted and documented for outsiders - #5
Open
eins78 wants to merge 24 commits into
Open
Conversation
… honest dexd plays one HEVC video artwork in an endless loop on a Raspberry Pi, with no visible break at the loop point, on a machine that is switched off at the mains. It feeds libmpv one stream that never ends, so playback never reaches the end of the file and never seeks. The player. The video, its sidecar and the exhibit config live together in /opt/dex on the card's data partition; the package installs no config. The sidecar binds the frame rate and a checksum to the video, and dexd refuses to start rather than guess either. Before opening anything it checks the display mode, the boot options and the start of the video, and every refusal names the fix. While playing it checks that playback advances, recovers in place, writes a heartbeat to the system log, and lets the service manager restart it if it stops responding. It ships as a Debian package for arm64 with four commands — dexd(1), dex-sidecar(1), dex-exhibit-apply(1), dex-wait-hdmi(1) — and their man pages. Source under MIT-0; the shipped binary links libmpv and is a GPL-3+ combined work. The empty packages/dexd submodule, which pointed at a repository that no longer exists, is replaced by the crate itself. The documentation. docs/guides is for a venue technician who can use a terminal: what dexd is, building a card, preparing a video, configuring the exhibit, running and troubleshooting, and a reference of every key, option, exit code and message. docs/design is for a developer who has never seen the project: the architecture, the endless stream, the startup checks, failure handling, the service unit, the sidecar, the exhibit config, packaging, continuous integration, building and testing, what Raspberry Pi hardware can do, the roadmap, and the measurement record the other pages cite. docs/glossary.md holds the terms the documentation may use without explaining them, each one reviewed and approved. The rules and the checks. AGENTS.md says how anything an outsider reads is written — the two audiences, the tone, what a comment is for, the names that were decided and the words that were retired. scripts/docs-lint.mjs checks what a machine can check, against the glossary, a table of retired words and an allowlist where every exception carries a reason; it has 83 tests of its own and runs in CI over the documentation, the crate, the man pages and the READMEs. CODEOWNERS routes the glossary and the rules to the project owner. One CI workflow with no path filters builds the package in a debian:trixie container on an arm64 runner, runs the test suite, cargo-deny, the shell and unit checks, REUSE, the writing check and an install, remove and purge cycle, and a single required check reads their results and decides.
Both packages ship the dex-exhibit-apply and dex-wait-hdmi commands and their man pages, four files in common. Undeclared, dpkg stops on the first of them part-way through the install, and the card is left with neither player configured until someone purges by hand. Conflicts states the pair is exclusive; Replaces lets apt do the rename in one step on a card already running dex-loop, which is the upgrade every existing installation takes. The allowlist gains the one place the retired name may still be written: a dpkg field holds a package name, not prose.
The warning told the reader to expect one outcome: the artwork playing at the wrong resolution with every metric nominal. There is a second, and it is the one a mismatched exhibit reaches first — a video larger than every mode the connector offers never reaches the plane at all, the clock does not advance, and recovery escalates to a restart. An operator reading the old text would look for a picture that is merely wrong and find a player that keeps starting. The repair differs too, so the message now names both. Forcing the mode helps a display that can build the geometry once forced; it does nothing for a display that cannot build it at all, where the video is the thing to change. Still a warning and not a refusal. The note above it now says why in terms that hold: "auto" is also the only working value for a panel whose one refresh is fractional, and refusing here would reject that too.
A player running in an exhibition turned up two things the documentation did not have, and one it had backwards. The guide told a technician to set display_mode to 2560x1440@60 or auto for a 1440p monitor, as though the two were equivalent. On a monitor that offers its 2560×1440 at 59.95 Hz and nothing else — a common panel, and the one this was found on — the integer value matches no mode and the player restarts, with no forced mode involved. A connector whose only timing is fractional has no working integer display_mode, so auto is not one option of two but the only one. The guide now says so, the design doc carries the grammar reason, and the measurement record carries the panel. The second: a 4K video does not appear at all on that monitor. The measurement record already held a 4K-file-into-a-1440p-monitor reading from the detiling path, which does resize; the plane path this design uses does not, and the two readings look contradictory side by side. The record now says which path each belongs to. The troubleshooting table gains the restart-loop case with no mode error, which is what an operator actually sees, and the message reference gives both outcomes of the auto warning rather than the milder one. The roadmap's open question — whether auto should refuse — now carries the evidence, which argues both ways and so does not close it.
The guides teach artwork.265; the refusal messages and doc-comments still said loop.265, which is also the file name the superseded package used. A technician who copied the line dexd printed got a different name from the one the guide had just given, and neither text said they were the same thing. The measurement record keeps loop.265 in the shell pipeline it records: that is a command someone ran against a file of that name, not an example to follow.
Nineteen pages that cite each other by name, and nothing checked that the names still pointed anywhere. One did not: a guide linked measurements.md#two-sinks-two-mode-behaviours, a section renamed to "Mode behaviour by sink", and the link had read as fine ever since. Renaming a heading is the ordinary edit that breaks these, so the check belongs where the rename happens. The rule resolves both forms the project writes: Markdown links, and the bare docs/…md#section references the doc-comments use. Anchors match GitHub's slug, so a repeated heading is reachable as -1 and an underscore survives. Links inside a fence or a code span are examples and are left alone, http and mailto targets are not fetched, and a full stop after a bare reference ends the sentence rather than the anchor. absPath in the file context was the display path, so it resolved against whatever directory the process happened to be in. It is now what its name says, taken from the root the run was given, which is also what let the rule's own tests run against a temporary tree.
A metadata key cargo-deb does not support is dropped without a word. The package would still build, still install on a clean card, and still pass the install, remove and purge cycle — and fail only on the upgrade of a card already running dex-loop, which is the one path this declaration exists for and the one nothing here walks. Same shape as the libmpv floor asserted above it, and for the same reason: the field is invisible until the day it is missing.
Nine documents, two man pages and a glossary entry told a technician that the card carries a FAT partition any computer opens, and that the three files are edited with the card in a laptop. The package's postinst makes /opt/dex an ordinary root-owned directory on the root filesystem, and the card the guides build never gets a second partition — build-player-card copies the files over SSH, which is the only route that works. So the set described the intended card and called it the present one. A technician planning the laptop route finds no such volume, and the page that builds the card cannot tell them why. What each document now says: /opt/dex is the assets directory, reached over the network. The unit keeps RequiresMountsFor=, which costs nothing against a directory on the root filesystem and is what lets a later card put the assets on their own partition without touching the unit. The partition itself is in the roadmap as the arrangement that is meant and not built, and the glossary entry says so where it defines the term.
The card recipe sent the technician to a releases page to download the package. No release workflow exists and no tag has been taken, so the page is empty and the recipe stops on its own instructions. The crate README already gave the route that works — the build artifact — and the guide now gives it too, with the releases page named as planned. Every shipped man page, and the changelog beside them, pointed at dex-sidecar(1). The package installs neither that command nor its page, on purpose: a video is prepared on a workstation. On a player both `man dex-sidecar` and `dex-sidecar` fail. The references now say where the tool runs, and the design page that had this right all along is left alone.
The manual page said mpv rounds @29.97 to 29, carrying the measured label on a number that is not the measurement: the design page and the glossary both say mpv matches on the nearest whole refresh, which is 30. It also offered 2560x1440@60 as an equal alternative to auto for a 1440p monitor, the value three other documents now record matching no mode and restarting the player on a panel that offers 59.95 alone. And its entry for the auto-mode warning still gave one outcome and one repair, where the shipped message gives two of each. The rotation advice was backwards in the place it mattered most. The troubleshooting row said the rotation was dropped on the way out of the container, then prescribed -noautorotate, which is the option that keeps the pixels as stored — so a technician following the cure reproduced the symptom. Both places now say what a .265 can hold, which route stores what, and how to check the geometry before the video reaches a venue.
A step named for a plan code has been on every run's page since the workflow landed, and its failure message carried the same code and a retired word. The rules forbid both in anything a reader outside the project sees, and a step name in the Actions UI is exactly that. The check never caught it because the file was not among the paths CI passes. It is now, which is what keeps this from coming back. The package name in the conflict check is allowlisted the same way it is in the manifest: dpkg reads a package name there, not prose. Two descriptions of the check had also gone stale: the default path list named a file the walk skips, and the CI paragraph still said the crate's comments would join later. They joined.
Eleven places where two documents, or a document and the code, said different things, and the reader had no way to tell which to believe. The rules file put the exhibit config in /etc/dex, which dexd does not read; it is the first text a writer consults. The front page said nothing above 30 fps had been measured, four lines before citing the measured 4K60 result. The capability page stated a shipped limit on video size that no code enforces and the front page denies. Both guides said `off` in the heartbeat means the player never asked for that counter — dexd asks on every start, and `off` is the branch where mpv turned the request down, so the guides taught an operator to read a fault as a setting. The rest are smaller and the same kind: a worked example dropping the trailing D the paragraph is about, an example config naming the project's capture card as the venue's display, a migration command that can leave two configs where the same page says two make dexd refuse, a triage step telling the reader to run a command the player does not have, another restarting the show to read a version that prints without taking the display, a claim that Rust removes leaks, and a manual page calling the health check by a name nothing else uses. Also: the reference now lists the two asset refusals and the watchdog warning it had left to the design pages, while promising every message; the packaging page describes the conflict fields the manifest gained; and the REUSE file states what the setting is for instead of arguing for it.
The front page's next steps skipped the three pages that come next in the only journey it describes — building the card, preparing the video, writing the config — and jumped to troubleshooting, so a reader following it arrived at the fixes before the recipe. The developer walk had no ending either: nothing said what a change must satisfy before it lands. The four commands now sit on the page that owns the checks, pointing at the workflow for what runs when. Also, four claims that carried a qualifier twice or an adverb where the provenance label already stood, and the long-run scorecard, which reported its own scoring rather than the result and left a "disagreement" whose noun was not on the page.
The glossary fixes artwork as the whole installation and asset, or the video, as the file. Four places said the artwork plays at the wrong size, which reads as the installation misbehaving rather than one file being the wrong geometry for the display. The warning message, its manual page entry and the guide now agree with the entry.
Five findings a reader would have been stopped by, all of them written in the last day while correcting earlier findings: A repair that cannot be typed — "give the connector a forced mode with the trailing D alone", where the grammar takes `none` or WxH@R[D] and nothing else. A migration that fails in the case its own guard exists for: with a config already in /opt/dex the move is skipped, /etc/dex stays non-empty, and the rmdir errors with no next step. The one route to the package, a build artifact, needs an account and expires after ninety days, which the page did not say. A design page still claiming a 4K video plays scaled on a 1080p monitor, which the measurement record now says it cannot. And a manual page saying the frame rate cannot be read from the video, on a project whose own tool reads it. Then: two front doors stating as results what the measurement record calls the player reporting on itself; a roadmap claiming players are not networked, on a card the guides give SSH; two developer terms in a message a technician reads in the log; a licence paragraph that covers the whole clone, submodules included; a CI page claiming the check reads the glossary, which every walk skips because it is the check's own input; one observed monitor generalised into "many panels"; the long run's scorecard using two words for one thing; and ten cells in the rules table that had been shipping cut off mid-word.
The sweep that corrected nine documents and two man pages missed the packaging manifest, the crate's own doc-comment and the systemd unit, each of which still described /opt/dex as a partition a computer opens. The unit's comment now says what RequiresMountsFor= actually buys: nothing today, and a card that can move the assets to their own partition without this file changing. With them, the rest of the final read: two decode ratios a user guide asked a reader to draw a conclusion from, "the exhibit" on its own where the names table says exhibit config, Rust vocabulary on the download path, the earlier package name used before the page says what it is, a counter pair called "both" with no nouns, a flag named in a callout that no command on the page carries, and a scope defended that nobody had attacked. The CI comment and its error message lose the drama they had been written with.
The correction that stopped the documentation calling /opt/dex a partition went one step too far and wrote the idea off as unbuilt. dexOS ships a third FAT partition today, mounted at /dexdata, on an image that plays with pi_video_looper. What no dexd card has is the same arrangement at /opt/dex, which is a directory on the root filesystem there. The glossary entry and the roadmap now say which of the two is missing. Also: the artifact caveat reaches the crate README, where the download route is also documented, and both say the retention period is the repository's setting rather than stating 90 days as a fact.
Both earlier versions of this paragraph were wrong. The original told the reader to download dexd_<version>_arm64.deb from the releases page, which has no dexd package on it. The correction then removed the releases page entirely, on the strength of the roadmap's claim that no tag had been taken — and the project has taken one: v1.0.0-rc.1, a 2024 pre-release carrying a dexOS image and its build logs. So the page is real, it is where dex releases live, and it is where dexd's package will go. Until then the build artifact is the route, with what that costs a reader stated: an account, and a retention period. The roadmap line that started this now says which tag exists and that dexd has had no release of its own. Rule 17 makes the general case a rule, since the project is unfinished and this is the second thing to go wrong the same way: say what is not built, mark it (planned) or as a Note, and never write an intended arrangement in the present tense.
Three places said the refresh rounding was measured on a Raspberry Pi 4, and one sent the reader to the measurement record for the conditions. The record holds neither the rounding nor the option-parser error beside it, so a reader who followed the citation to check would find the page refuting it. The mechanism needs no measurement: a display mode carries a whole-number refresh, mpv matches against that, so a decimal names the mode it is nearest to. That is what the glossary's vrefresh entry already says, and the three passages now say it too and point there. The option-parser rejection keeps its error code as an observation against mpv 0.40, without claiming a record entry that does not exist. Found by auditing what this branch asserted about systems outside the repository, after the releases-page mistake. The rounding was corrected here yesterday from 29 to 30 — right by the mechanism, and resting on a provenance nobody had checked.
V1 — the mpv core is unresponsive, not hung, which the retired-words table forbids outright. V2 — the build-identity entry drops the "(stamp file)" gloss rather than tagging it; the filename says what it is. V4 — the VBV entry said "dexd's artwork encodes", where the glossary's own asset entry reserves artwork for the whole installation. The project's video is what gets encoded. V5 — sink stays and gains an entry, so the term the display world uses keeps covering a monitor and a capture device at once, and the rule that every term is either plain English or in the glossary stays true. V7 — the sidecar page takes the name the table gives it: the sidecar check. Two doc-comments pointed at its old heading; they point at the page now, since an anchor to a page's own title says nothing extra. V8 — layer stops meaning two things. The glossary's trio is described without it, and the recovery ladder is four things in order. The stream layer, the KMS layer, the test layers and the NAL layer are other domains and keep the word. V3 is deferred: Max is not settled on the name yet, so player card is left as it stands rather than half-retired.
…ements The word "master" was defined twice and differently, in a project whose own field has settled names for these files. Time-based media conservation distinguishes the artist's supplied master, the archival master and the exhibition format — and the last of those is exactly what a dex card plays, beside the exhibit config it is named by. Three entries now define them, and the two pages that each invented a definition use the named one instead. The word stays where the field puts it. DRM master is the kernel's term and is unrelated; renaming it would make the pages disagree with the logs and with upstream. docs/README.md maps both sets of pages, which the design side had no way into or out of: a technician's six guides in order, and the thirteen design pages grouped by what they answer. Linked from both READMEs. The guides keep their own Related pages lists; the design pages get their signpost here rather than thirteen new tails. The measurement record gains the two readings that were cited from other pages and never recorded: the shared-object count of the installed binary, and the END_FILE that a loadfile replace produces, which is the event the whole in-place recovery rests on absorbing. Both pages now link the record, which is what a measured value is asked to do.
Setting a player up should be choosing dexOS in Raspberry Pi Imager, the way any other operating system is chosen. The guide is renamed to say so, and the roadmap's Distribution section carries the path. The image that would make it true is not built, so the page says that in a note and keeps the manual steps: Raspberry Pi OS Lite, then the package on top. Those steps do not expire when the image ships — running dexd on a system someone chose themselves stays supported, and these are the steps for it. dexOS ships in two lines, which the glossary now states: the buster one that drives HD on older boards with the legacy player, and a trixie one carrying dexd for 4K on a Raspberry Pi 4 or later. Rebasing the first onto the second stays rejected in Alternatives, because that would migrate working 1080p installations; two lines is the plan and always was. A dex card is any card that boots a Pi into a player; one built from the image is a dexOS card. player card is tagged retired, and the allowlist entry that existed only because the old filename contained it is gone. The roadmap's own "Exhibition format" section is renamed to Playlist format. It described a playlist with per-video transforms, which is not what time-based media conservation means by the term, and the glossary now carries the conservation sense.
Two blind spots, one in each direction. A step name is printed on every run's page and an annotation reaches whoever reads a failed build, but the extractor treated every YAML value as data, so neither was ever scanned. That is where a plan code sat unnoticed until a reader found it by eye. The file's own name:, each step's - name: and the text of an ::error::, ::warning:: or ::notice:: command are prose now; a name: indented under another key is an artifact or an input and stays an identifier. Turning it on immediately found a retired word in a shipped error message, which is fixed here. The other way: a docs/…md#section reference is a path, and its file name and heading slug are made of words nobody wrote as prose. Renaming a heading to "the sidecar check" reported the retired command name its slug contains, and a guide's file name reported whatever the guide is called — which is what the allowlist entry deleted with the rename had been holding back. Paths are blanked out of prose everywhere but Markdown; the links rule reads the raw line and still resolves them. Four tests: the three name shapes and the annotation, the nested name: that must stay data, the shell past an annotation's closing quote, and a slug that no prose rule may read while its link still resolves.
The roadmap closed a read-only root filesystem as something the project ships, on the evidence that installations survive being switched off at the socket without one. That decision stands. What has changed is that a deployed card now runs with the overlay filesystem switched on by hand and its boot partition write-protected — one operator's hardening rather than anything dexd sets up, and safe there because that card's video never changes. The consequence belongs in the guide, because it is silent: on a read-only root the exhibit config is written to the overlay, looks applied, and is gone at the next boot, with the player still running what it had. Turn the overlay off, edit, turn it back on.
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.
What this is
dexdplays one HEVC video artwork in an endless loop on a Raspberry Pi, with no visible break at the loop point, on a machine that is switched off at the mains. It feeds libmpv one stream that never ends, so playback never reaches the end of the file and never seeks — the mechanism, and the measurements behind it, are in The endless stream.It arrives here from the 4K looping experiment as a package, with documentation written for people who were not part of that work.
What is in it
The player —
packages/dexd: the crate, the Debian packaging, the systemd unit, four commands (dexd,dex-sidecar,dex-exhibit-apply,dex-wait-hdmi) and their man pages. The video, its sidecar and the exhibit config live together in/opt/dex; the package installs no config. Source MIT-0; the shipped binary links libmpv and is a GPL-3+ combined work. The emptypackages/dexdsubmodule, pointing at a repository that no longer exists, is replaced by the crate.The documentation — 19 pages in two tiers.
docs/guidesfor a venue technician: what dexd is, build a dex card, prepare your video, configure the exhibit, run, check, troubleshoot, reference.docs/designfor a developer new to the project: architecture, the endless stream, startup checks, failure handling, the service unit, the sidecar, the exhibit config, packaging, CI, building and testing, Raspberry Pi media capability, roadmap, and the measurement record the other pages cite.The vocabulary —
docs/glossary.md: 147 terms, 34 for the user tier and 113 for the developer tier, each reviewed and approved individually. The project's own shorthand is retired:soak→ long-running test;seamandthe wrap→ the loop point, with the defect described plainly; plan codes, references to private documents, device names and card identifiers do not appear.AGENTS.mdcarries the writing rules, the tone, what a comment is for, and the table of retired words.The checks —
scripts/docs-lint.mjs(Node, no dependencies, 89 tests) enforces the mechanical rules against the glossary, the table of retired words and an allowlist where every exception carries a reason. It runs in CI over the documentation, the crate, the man pages, the READMEs and the workflow file itself, so a new comment and a new CI step name are checked like a new page.CODEOWNERSroutes the glossary and the rules to the owner.What a deployed player and a final review changed
An exhibition player and a seven-agent review of the finished text found things the writing could not have found on its own.
From the deployment.
dexdanddex-loopship four of the same files, so an install onto a card running the older package stopped part-way through;ConflictsandReplacesnow declare it, and CI reads both fields back out of the built package. A monitor that offers one refresh rate, 59.95 Hz, restarts the player when the mode is spelled out as2560x1440@60— the guide had been recommending that exact value, and now says to writeautoand why. Theauto-mode warning promised the wrong failure: it named a picture at the wrong size, where a video larger than every mode the display offers never appears at all and the player restarts.From the review. Nine documents, two man pages and a glossary entry described
/opt/dexas a FAT partition any computer opens, with the three files edited by putting the card in a laptop. The package makes it an ordinary directory on the root filesystem and the card recipe never creates a partition, so a technician planning that route finds no such volume. dexOS does ship such a partition, at/dexdata, on an image that plays with pi_video_looper — so what a dexd card lacks is that arrangement, not the idea, and the glossary and roadmap now say which. The card recipe told the reader to download the package from the releases page, which carries a 2024 pre-release and no dexd package. Every shipped man page pointed atdex-sidecar(1), which the package deliberately does not install. The rules file put the exhibit config in/etc/dex, whichdexddoes not read. Both guides said anoffcounter in the heartbeat means the player never asked for it;dexdasks on every start, andoffis where mpv turned the request down — a fault the guides taught an operator to read as a setting. The troubleshooting entry for a sideways picture prescribed the flag that produces one.A CI step had carried a plan code and a retired word in its name, on every run's page, since the workflow landed. The writing check never saw it because the workflow file was not among the paths CI passed. It is now.
A last read before it was public
A separate final pass read the text as it now stands, which no earlier pass had: every review before it judged a version the corrections then replaced, and applying those corrections rewrote roughly five hundred lines that nobody had read. Five findings would have stopped a reader, and all five were written while fixing the previous round.
A repair that cannot be typed — "give the connector a forced mode with the trailing
Dalone", where the grammar takesnoneorWxH@R[D]and nothing else. A migration that fails in exactly the case its own guard exists for. The only documented route to the package needs a GitHub account and expires ninety days after the build, which the page did not say. A design page still claiming a 4K video plays scaled on a 1080p monitor, which the measurement record now says it cannot. And a man page stating the frame rate cannot be read from the video, in a project whose own tool reads it.Below those: two front doors reporting as results what the measurement record calls the player reporting on itself; a roadmap claiming players are not networked, on a card the guides give SSH; a licence paragraph that appeared to cover submodules under other projects' licences; a CI page claiming the writing check reads the glossary, which every walk skips because it is that check's own input; one observed monitor generalised into "many panels"; and ten cells in the rules table that had been shipping cut off mid-word.
The same pass found three more places calling
/opt/dexa partition — the packaging manifest, a doc-comment and the systemd unit — that the earlier sweep of nine documents had missed.The vocabulary the review left open was decided in full:
hungout, thestamp filegloss dropped,artworkreserved for the installation,sinkkept with an entry, the sidecar page renamed to what the rules already called it, andlayerstopped meaning two things. Time-based media conservation supplied three terms the project had been improvising — artist's supplied master, archival master and exhibition format, the last being exactly what a dex card plays — which settled a word that had been defined twice, differently, on two pages.Setting a player up is now framed as installing dexOS, one choice in Raspberry Pi Imager:
install-dexos.md, with a note that the trixie image carrying dexd is still being built and the manual steps reach the same card meanwhile.docs/README.mdmaps both tiers, which the design pages had no way into or out of.AGENTS.mdgains a seventeenth rule from those two corrections: the project is unfinished, so a page may name what does not work yet — marked(planned)or as a**Note:**— and an intended arrangement is never written in the present tense. Both defects above were that mistake.Verification
changes,build,lint,deny,lifecycle,docs-lint,required. The package builds in adebian:trixiecontainer on an arm64 runner with Debian's own compiler, so the dependencies it derives are the ones the devices have.dex-sidecar, sidecar-writer and FFI-constant targets.dexdlinks libmpv, so its targets build only where libmpv is present — that is CI, not a macOS workstation.cargo clippy --all-targets -- -D warnings,cargo deny check,reuse lint,shellcheck,checkbashisms,systemd-analyze verify,lintian— clean.Conflicts: dex-loopandReplaces: dex-loopread back out of the built.deb.docs-lint: 0 errors over 53 public files, 16 warnings. 280 relative links resolve, targets and#anchorsboth — a new rule, added after the review found a link into a section that had been renamed. Four man pages aremandoc -T lintclean.Not yet done
dexdwas given a 3840×2160 video on a 2560×1440 panel, the pairing that cannot work on any build using this player's zero-copy path —dex-loopfails the same way, and did, until it was given the matching video. A fair test needs a video whose size the panel offers.Notes for review
The branch before squashing is
feature/dexd-unsquashed, if the intermediate steps are of interest. Changes to the vocabulary need a glossary entry and the owner's approval — the lint will reject a term that is in neither the glossary nor the allowlist.That check reads more than it did. A workflow's own
name:, every step's- name:and the text of an::error::/::warning::/::notice::command are prose, because a step name is on every run's page and an annotation reaches whoever reads a failed build — which is where the plan code had been sitting. Turning it on found a retired word in a shipped error message the same minute. In the other direction, adocs/…md#sectionreference is treated as the path it is, so no rule reads the words in a file name or a heading slug while the link check still resolves them.