Commit Graph
424 Commits
Author SHA1 Message Date
4gray 3ea051532a fix(agents): exclude opaque URI prose from import scans 2026-09-21 03:45:04 +02:00
4gray ad5050ca78 fix(agents): preserve imports after bare URL punctuation 2026-09-21 03:33:08 +02:00
4gray 5a8b233840 fix(agents): convert base file URLs to native paths 2026-09-21 03:21:08 +02:00
4gray 464d829538 fix(agents): honor HTML bases and preserve adjacent imports 2026-09-21 03:09:22 +02:00
4gray 3dbd522d70 fix(agents): validate srcdoc references and empty srcset 2026-09-21 02:56:53 +02:00
4gray a7150d1e72 fix(agents): reject empty media and ignore URL at-signs 2026-09-21 02:44:36 +02:00
4gray 6558245e12 fix(agents): normalize internal HTML URL whitespace 2026-09-21 02:32:59 +02:00
4gray 3f8b5be06d fix(agents): preserve block boundaries and validate embeds 2026-09-21 02:21:11 +02:00
4gray 6bc5688001 fix(agents): require files for media references 2026-09-21 02:09:24 +02:00
4gray 576e70fc05 fix(agents): cover document families and guidance basenames 2026-09-21 01:57:30 +02:00
4gray a2d1997e29 fix(agents): validate document formats and trim HTML URLs 2026-09-21 01:45:35 +02:00
4gray 8f76a2c456 fix(agents): handle wildcard versions and alternate documents 2026-09-21 01:33:29 +02:00
4gray 22b2d7a2ac fix(agents): unify Markdown suffix and encoded import guards 2026-09-21 01:21:42 +02:00
4gray 1dd0ec2285 fix(agents): inspect document suffix before URL fragments 2026-09-21 01:09:57 +02:00
4gray 5da10082e5 fix(agents): validate iframe document references 2026-09-21 00:58:25 +02:00
4gray 82d582bb74 fix(agents): preserve filenames across prose punctuation 2026-09-21 00:46:30 +02:00
4gray 4a73cf3828 fix(agents): normalize punctuation for extensionless imports 2026-09-21 00:34:28 +02:00
4gray d10bceab7b fix(agents): delimit package prose at Unicode punctuation 2026-09-21 00:22:37 +02:00
4gray bbbaebce83 fix(agents): count all Markdown line endings in budgets 2026-09-21 00:10:57 +02:00
4gray b863d8e4d4 fix(agents): validate image-map navigation targets 2026-09-20 23:59:28 +02:00
4gray 900050be2e fix(agents): retain visible backticks and explicit path punctuation 2026-09-20 23:47:48 +02:00
4gray 505fa356ab fix(agents): validate extensionless imports and version comparators 2026-09-20 23:35:50 +02:00
4gray cb42c74f43 fix(agents): recognize possessive package mentions 2026-09-20 23:23:11 +02:00
4gray 0ac3421594 fix(agents): decode full HTML entities and media assets 2026-09-20 23:03:17 +02:00
4gray a73538994a fix(agents): validate media and hyphenated literal paths 2026-09-20 22:51:15 +02:00
4gray b54dc4c405 fix(agents): validate visible headings and spaced paths 2026-09-20 22:39:02 +02:00
4gray b0cf310074 fix(agents): retain rendered context for Markdown references 2026-09-20 22:26:34 +02:00
4gray 221fe3d68a fix(agents): accept qualified package prose 2026-09-20 22:13:39 +02:00
4gray e1bc4dee60 fix(agents): support package versions and source fragments 2026-09-20 22:01:32 +02:00
4gray 795946d2a0 fix(agents): route source work and check extensionless files 2026-09-20 21:49:06 +02:00
4gray f2f7a94857 fix(agents): decode link entities and allow package subpaths 2026-09-20 21:35:52 +02:00
4gray 73e98bd00b fix(agents): parse JSONC and constrain package exemptions 2026-09-20 21:23:30 +02:00
4gray 231ec0a513 fix(agents): recognize package scopes and route source work 2026-09-20 21:11:28 +02:00
4gray d6a35cfa31 fix(agents): validate visible HTML and image source sets 2026-09-20 20:57:34 +02:00
4gray e028712e61 fix(agents): handle image fragments and quoted imports 2026-09-20 20:44:46 +02:00
4gray 87c3e466d2 fix(agents): exclude HTML-contained guidance imports 2026-09-20 20:32:23 +02:00
4gray 84596ecb41 fix(agents): require standalone top-level Claude import 2026-09-20 20:20:09 +02:00
4gray 98e6fd3983 fix(agents): use GitHub-compatible heading slugs 2026-09-20 20:08:22 +02:00
4gray 244b1e43ce fix(agents): validate rendered HTML navigation 2026-09-20 19:56:14 +02:00
4gray bdf7017a97 fix(agents): parse prose and rendered HTML anchors 2026-09-20 19:44:07 +02:00
4gray 1206f0d255 fix(agents): handle fenced imports and encoded paths 2026-09-20 19:30:55 +02:00
4gray 989ba24ecf fix(agents): distinguish code symbols and shortcut images 2026-09-20 19:07:24 +02:00
4gray a10045f0b2 fix(agents): validate generic literal repository paths 2026-09-20 18:54:43 +02:00
4gray 8467b9406a fix(agents): parse guidance navigation with Markdown tokens 2026-09-20 18:36:14 +02:00
4gray 3a2497476b docs(agents): compact root guidance and preserve task-specific knowledge 2026-09-20 18:18:51 +02:00
4grayandClaude Fable 5.1 15d964ef88 fix(collections): non-destructive reload feedback for the scope switch (#1636)
Switching the unified Favorites/Recent page between "This playlist" and "All playlists" kept the old items on screen with no feedback until the new query resolved. The skeleton cannot be reused there: it unmounts a playing channel and drops focus from the toggle.

Add a separate reload state that keeps content mounted: after a 180 ms grace period an indeterminate progress bar overlays the header separator, the content region carries aria-busy, and the grid or the live tab's channel rail dims — the player never dims. Fast IndexedDB/SQLite answers show nothing. Only the latest request settles the state.

Also bind Clear and drag reorder to the request that loaded the rows still on screen (`loadedRequest`) instead of `effectiveScope()`, which moves ahead the moment the toggle is clicked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 18:08:41 +02:00
4grayandClaude Fable 5.1 f13d0c55da build(deps): resolve the eight open Dependabot security alerts (#1635)
Bump astro 7.2.4 → 7.2.10 (critical, website build) and retarget the pinned
pnpm overrides for the transitive alerts: js-yaml → 4.3.2 (the one runtime
path, via electron-updater), smol-toml → 1.7.1 (new key for nx's exact 1.6.1
pin), svgo → 4.1.0 (new key for astro's 4.0.2 resolution) and hono → 4.13.5.
Every target stays inside its parent's declared range except nx's exact
smol-toml pin, which is now recorded as the deliberate exception in
docs/architecture/dependency-security-overrides.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 18:02:07 +02:00
4grayandClaude Fable 5.1 033a08cad9 feat(collections): open a favorite or recent live channel inside its playlist (#1634)
* fix(xtream): keep a live-channel handoff alive until its playlist catalog is loaded

Arriving at /workspace/xtreams/:id/live from another route with
openXtreamLiveItemId in history state silently did nothing: the Xtream
shell mounts the live layout after its session bootstrap, i.e. after the
arrival's NavigationEnd, so the layout's NavigationEnd subscription never
saw it. When the layout was reused instead (playlist switch), the shared
store still held the previous playlist's catalog at NavigationEnd and the
"not in liveStreams" verdict dropped the pending id.

Read the state once at mount as well, carry openXtreamLivePlaylistId in
the navigation state, and treat a miss as final only once currentPlaylist
is the requested playlist and isContentInitialized is true.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* feat(collections): open a favorite or recent live channel inside its playlist

Live channels watched from Favorites / Recently viewed had no visible
playlist and no way to jump there, unlike movies and series with "View in
portal". Add the live counterpart:

- getLiveCollectionPlaylistNavigation() resolves the channel inside its
  playlist (Xtream via the live layout's auto-open state, M3U via
  openM3uChannelUrl on the player's all view); Stalker resolves to null
  until its ITV layout gets an open-on-arrival contract, so nothing is
  shown there instead of landing on the section root.
- app-open-in-playlist-chip, projected into the EPG timeline / list-view
  toolbar through a new [epgToolbarAction] slot beside the channel name,
  visible in the collapsed state too.
- "Open in <playlist>" entry in the channel row context menu, which also
  covers radio rows and rows that are not playing.

Both label with playlistDisplayLabel and reuse PORTALS.VIEW_IN_PORTAL_TOOLTIP.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(xtream): only match an auto-open channel against the requested playlist's catalog

Review finding (Greptile/Codex P1): the playlist check ran only on a miss,
so a colliding provider-local xtream_id in the previous playlist's catalog
was accepted, played the wrong channel and consumed the handoff. Check the
playlist before consulting the catalog at all.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 18:01:17 +02:00
4grayandClaude Fable 5.1 4cce4acaad feat(portal): season thumbnails in the season dropdown + PR #1628 follow-ups (#1633)
* feat(portal): season thumbnails in the season dropdown + PR #1628 follow-ups

Follow-ups to the season posters shipped in #1628:

- The >6-seasons dropdown (`SeasonTabsComponent`) now carries a 28×42
  season thumbnail at the start of each menu row that has a poster and in
  the closed trigger for the selected season, fed by a new `seasonPosters`
  input from the season container and the fullscreen episode panel. Rows
  without a poster get no placeholder, a failed image is dropped, and the
  pill row stays text-only as the design review decided.
- The fullscreen season strip's episode count uses its own
  `PORTALS.EPISODE_COUNT_ONE/OTHER` keys instead of borrowing the download
  manager's; all 18 locales filled through the i18n merger from their
  existing `DOWNLOADS.EPISODE_COUNT_*` translations.
- The Stalker mock's serve targets no longer pin `PORT` (an nx:run-commands
  `env` entry overrides the shell), and `main.ts` resolves `PORT`, then the
  Playwright-side `MOCK_PORT` alias, then 3210 — so `MOCK_PORT=3310` now
  relocates the whole E2E run. The Xtream mock honours `XTREAM_MOCK_PORT`
  the same way.
- `resolveAutoSelectedSeason` gets a direct spec covering every branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(xtream-mock): mint marketing asset URLs on the port the server bound

Greptile P1 on #1633: the listener honoured `XTREAM_MOCK_PORT`, but
`marketingAssetOrigin()` still read `PORT` alone, so a run relocated only
through the alias sent every poster/backdrop/logo/episode URL to 3211.

One resolver (`resolveXtreamMockPortString` in `mock-port.ts`: `PORT`,
then `XTREAM_MOCK_PORT`, then 3211) now feeds the environment parser, the
marketing asset origin and the demo-guide origin fallback. A spec pins the
precedence and that `marketingAssetUrl` follows the bound port.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 14:54:54 +02:00
4grayandClaude Fable 5.1 01c423ac43 fix(portals): stop treating a slow panel as a dead host; IPv4 fallback budget in Electron (#1621)
* fix(portals): apply the IPv6->IPv4 fallback budget in the Electron process

The 2500 ms happy-eyeballs attempt timeout from #1404 only ever ran in the
web backend. The Electron main process and its playlist-refresh and EPG
workers kept Node's 250 ms default, so a dual-stack panel hostname behind a
VPN or a slow link failed every connection attempt in a row and tripped the
host connectivity guard. The module now lives in `@iptvnator/shared/host-health`
and every Node isolate that opens connections applies it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): stop treating a slow panel as a dead one in the host guard

axios raises the same ECONNABORTED whether the SYN went unanswered or the
panel accepted the connection and then thought for longer than the request
budget. Two such timeouts opened the breaker and every request to the panel
was refused for 30 s with "portal is not responding" — the shape behind the
"connection keeps dropping" reports on 0.23 and nightly.

Both transports now report whether the TCP connection was established
(`onConnect`: Electron through a per-request observed agent instead of the
shared keep-alive globalAgent, the web backend through the transport that owns
the ClientRequest), and `classifyHostRequestFailure(error, { connected })`
downgrades a host-level code observed after the handshake to inconclusive.
Redirect attribution keeps precedence. A host that never accepts the
connection trips the guard exactly as before.

The Xtream mock gains a `silent:silent` scenario whose detail actions accept
and never answer, plus a real-socket regression spec for the guard.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): let an accepted connection clear the host-failure streak

Review finding: an unanswered SYN, then an accepted-but-slow timeout, then
another unanswered SYN still reached the two-failure threshold, because the
middle request was merely not counted. An accepted TCP connection is the
reachability the guard measures, so it now reads as `responded` and clears
the streak like an HTTP response would. Regression coverage for the mixed
sequence on one flapping loopback origin (Electron) and through the proxy
route (web backend).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): credit an accepted connection when it happens, not when the request settles

Review findings. A request that connected and then hung for 30 s cleared,
on its eventual timeout, the failures later requests had recorded while it
waited — reopening a host that had just died on evidence older than theirs.
The connect hook now reports the connection the moment it fires through a
new `HostConnectivityGuard.reportConnected`, which clears the failure streak
but closes no open or half-open breaker (the trial keeps its slot until it
settles), and the settled timeout is inconclusive.

Electron also skips the socket observer while an environment proxy
(`http_proxy` / `https_proxy` / `all_proxy`) applies to the request: through
a proxy the socket connects to the proxy, whose handshake proves nothing
about the portal, so those requests keep the pre-observer behaviour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(portals): decide the proxy exemption with axios' own resolution

Review findings. The hand-rolled environment check ignored `no_proxy`, so a
LAN portal exempted from the proxy lost its connect observer and slow
requests to it still tripped the breaker; it also read the variables with
`??`, letting an empty lowercase one mask a populated uppercase one that
axios would honour. The decision now calls `proxy-from-env`'s
`getProxyForUrl`, the same pinned package axios' http adapter uses,
declared as a direct dependency so the packaged app carries it.

The validated-axios spec clears and restores every proxy variable around each
case, so a runner that exports a proxy cannot change what the cases prove.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 14:50:50 +02:00