383171dfa4e6c7e6ec9da2a409230563712dc023
`secrets['REGISTRY_PASSWORD']` is the same reference to the expression evaluator as
`secrets.REGISTRY_PASSWORD`, and the guard shipped here could see only the dot form. Measured
2026-09-05 against the predecessor of this commit, a `pull_request`-route job whose `env:` read
`"${{ secrets['REGISTRY_PASSWORD'] }}"` produced `stored_secret_faults(...) == []` AND
`walk_versus_text_faults(...) == []` — the text cross-check cannot report the gap, because both of
its halves resolve references through the one pattern, so a spelling it does not know is a shared
blind spot they agree at zero on rather than a disagreement they name
(`proof-sharing-with-subject-proves-nothing`).
The file enumerated four other blind spots it has — composite actions, reusable workflows, nested
directories, both directions of the comment strip — and not this one, which is what made the
omission read as coverage.
`secret_refs` is now the single entry point for both halves, and it matches the dot form, both index
forms and a case-varied context, then counts the RESIDUE: any `secrets` token inside a `${{ }}` span
that yielded no literal name is reported under the sentinel `secrets.*`. Counting the residue rather
than pattern-matching `toJSON(secrets)` and a computed index is what makes it exhaustive over the
grammar — a spelling nobody has written yet still faults, in the fail-closed direction. The bare word
is read as the context only inside an expression, because in prose it is ordinary English; this file
and four workflows discuss "secrets" in comments.
`test_the_collector_sees_every_SPELLING_of_a_secret_reference` drives the six spellings through the
collector and the cross-check and asserts each is invisible to the real predecessor, so reverting the
widening reddens it. Whether act_runner resolves each spelling against this instance was not probed
from here (that needs a live run); the direction makes that acceptable — a spelling the runner does
not support costs a spurious demand on a job nobody has written, the omission cost a live
write-capable credential on the head-authored route.
Decisions-Edit: yes
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
fix(754,757): declare graphicsElementIds + padToNearestMinute, and pin every MCP tool to its OpenAPI contract (#760)
fix(754,757): declare graphicsElementIds + padToNearestMinute, and pin every MCP tool to its OpenAPI contract (#760)
ErsatzTV
ErsatzTV lets you transform your media library into a personalized, live TV experience - complete with EPG, channel scheduling, and seamless streaming to all your devices. Rediscover your content, your way.
How It Works
- Install ErsatzTV: Download and set up the server on your system.
- Add Your Media: Connect your media libraries and collections.
- Create Channels: Design and schedule your own live channels.
- Stream Anywhere: Watch on any device with IPTV and EPG support.
Key Features
- Custom channels: Create and schedule your own live TV channels.
- IPTV & EPG: Stream with IPTV and Electronic Program Guide support.
- Hardware Transcoding: High-performance streaming with hardware acceleration (NVENC, QSV, VAAPI, AMF, VideoToolbox)
- Media Server Integration: Connect Plex, Jellyfin, Emby and more.
- Music & Subtitles: Mix music videos and enjoy subtitle support.
- Open Source: Free, open, and community-driven project.
Documentation
Documentation is available at ersatztv.org.
License
This project is inspired by pseudotv-plex and the dizquetv fork and is released under the zlib license.
