6 Commits
Author SHA1 Message Date
4gray 0e4e1d2169 chore(release): begin 0.25 development and publish 0.24 article (#1674) 2026-09-26 17:13:13 +02:00
4gray dc4b33991d chore(release): prepare v0.24.0 (#1659)
* chore(release): prepare v0.24.0

* docs(release): select sculptural v0.24 cover

* docs(release): preserve announcement cover prompt
2026-09-22 08:43:42 +02:00
4gray 29ca94aa43 feat(release): announcement formats, highlight cards, and draft verification (#1480) 2026-08-29 10:16:07 +02:00
4gray 2ac0de752f fix(skills): align repository guidance with implementation (#1315)
* docs(skills): design implementation synchronization

* docs(skills): plan implementation synchronization

* fix(release): filter internal notes from public body

* docs(release): synchronize release workflow guidance

* fix(stalker): normalize catalog series flags

* fix(stalker): preserve progress with scoped episode IDs

* fix(playback): expose strict position persistence

* docs(stalker): record series position compatibility

* test(skills): validate repository skill contracts

* fix(database): keep SQL trace values private

* docs(skills): refresh Nx and SQLite ownership

* docs(skills): align provider and UI guidance

* docs(skills): tighten validated guidance

* docs(release): require exact release pushes

* style(electron): remove trailing blank line

* fix(ci): classify repository skills coverage
2026-07-31 08:00:59 +02:00
4grayandClaude Opus 5 b4ec68c1fa feat(release): manifest-driven screenshot capture with fail-closed mock-data guards (#1261)
Third slice of the release-notes pipeline (#1256 format+generator, #1257 CI
gate): release screenshots become reproducible and provably mock-only.

The v0.20 capture script was single-use (hard-coded slugs, paths, hero) and
fail-open: a lost IPTVNATOR_E2E_DATA_DIR silently fell back to the user's
real ~/.iptvnator database, `...process.env` leaked ambient TMDB keys and
proxies, nothing gated network access, and no frame content was ever
validated. Each hole leaks real playlists, credentials, or copyrighted
artwork into published screenshots without a single signal.

New pipeline:

- tools/release/screenshots.manifest.json — declarative shots (slug, title,
  named setup steps, themes). Adding a feature shot = one manifest entry.
- capture-release-screenshots.ts — orchestrator; output goes to
  apps/website/public/blog/<release>/screenshots/<slug>-<theme>.png, release
  slug derived from package.json (or --release), --only/--theme filters.
- capture-app-driver.ts / capture-navigation.ts — launch, seeding, theme,
  and the named-action vocabulary; actions are order-independent (every
  portal action starts from the dashboard).
- screenshot-guards.mjs — the fail-closed policy, pure and unit-tested:
  G1 the real database is snapshotted (sha256+mtime) before launch and must
     be byte-identical after; the isolated DB must actually exist
  G2 the app receives an allowlisted environment, never ...process.env
  G3 deny-by-default network gate; known app-level calls (GitHub update
     check) are answered by local stubs; any other blocked request fails
     the run — a silently-blocked TMDB call would leave a frame that looks
     broken rather than unsafe
  G4 every frame is scanned before capture: external img/background URLs,
     credential-shaped text, MAC addresses, non-localhost m3u8 references
  G5 TMDB enrichment asserted disabled via the renderer's IndexedDB
  Any violation deletes every frame captured in the run and exits non-zero.

The guards paid for themselves on the first live run: G3 caught the mock
server redirecting stream endpoints to a public demo HLS
(test-streams.mux.dev) — meaning earlier hand-run captures could embed
third-party video frames. The M3U shot now deliberately captures the groups
layout without starting playback.

`.changes` validation now cross-checks `screenshot:` slugs against the
manifest, so a note cannot reference an image the capture run never
produces.

Verified end-to-end: 10/10 shots (5 slugs × dark/light) captured against
dist build + xtream-mock-server, frames visually inspected (fictional
titles/artwork only), guard-violation paths exercised live. 67 unit tests
in release-tools, lint green, script files within the repo size limit.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 00:19:02 +02:00
4grayandClaude Opus 5 4e5132cbb5 ci(release): gate PRs on an authored release note (#1257)
* ci(release): gate PRs on an authored release note

Second slice of the release-notes pipeline (#1256 landed the format and
generator): make the .changes/ habit survive contact with reality.

- "Release note gate" job in ci.yml, PR-only: validates every .changes/*.md,
  then requires an added note (or the no-release-note label) when the PR
  touches runtime code under apps/ or libs/. Tests, e2e projects, the
  website, mock servers, shared testing helpers, snapshots and docs are
  auto-exempt.
- Policy lives in tools/release/check-release-note-gate.mjs as a pure
  function fed PR files+labels as JSON — unit-tested (10 cases) instead of
  encoded in workflow bash. The failure message lists the triggering files
  and names the exact fix.
- Labels are fetched live rather than from the stale event payload, so
  applying the label and re-running the check works without a new push.
- The job is dependency-free Node: no pnpm install, runs in seconds.
- release-notes and release-cut skills added under .claude/skills/ and
  mirrored to .codex/skills/; CLAUDE.md/AGENTS.md sections updated to point
  at the gate and the skills.

The no-release-note label itself was created in the repository.

Tests: 47 passing in release-tools (10 new gate cases); gate-step shell
verified with shellcheck at the CI severity; ci.yml YAML-parse checked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(agents): make the release skills discoverable by Claude Code too

`.codex/skills/**` was un-ignored so Codex picks up repository skills in any
clone, but `.claude` was ignored wholesale — and Claude Code only discovers
skills under `.claude/skills/`. The release-notes and release-cut skills
therefore existed only on whichever machine authored them.

Mirror both skills into `.claude/skills/` and opt them in by name rather than
un-ignoring the directory: contributors keep personal skills there
(i18n-fill, website, …) which must stay local and out of `git status`.

CLAUDE.md/AGENTS.md updated so the "skills live under .codex/skills/" claim
does not go stale, including the requirement to keep mirrored copies in sync.

The CI gate and the CLAUDE.md/AGENTS.md section remain the load-bearing
enforcement; skills only carry the detail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(ci): only a note this PR authored satisfies the release-note gate

Review follow-ups on #1257 (Codex P2 ×2, Greptile P1).

- Drop `renamed` from the accepted statuses. The PR files API compares
  base…head, so a note created and then renamed inside the same PR still
  reports as `added`; a `renamed` entry means the file already existed on the
  base branch. Accepting it let a runtime-code PR pass by moving another
  PR's unconsumed note, which documents nothing and gives the generator no
  adding commit to resolve a PR link from.
- Require a direct child of `.changes/`. `loadNotes()` reads only the
  immediate directory, so `.changes/sub/note.md` satisfied the old prefix
  check while never being validated or rendered into any release surface.

Tests: renamed and nested notes now assert a failing gate (12 gate cases).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 21:51:04 +02:00