Commit Graph
13 Commits
Author SHA1 Message Date
timothyandClaude Opus 4.8 90c8348efe fix(process): #317 docs-only exemption stays passthrough, not silent auto-grant [decisions-edit]
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 9s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 13s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m22s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 5m30s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / decisions.md append-only (push) Has been skipped
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Has been skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m33s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m36s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m33s
Adversarial-review nit on the first commit: auto-granting the docs-only exemption
silently self-merges process-control PRs (.claude/.gitea/.husky — including the
gate hook itself) with no prompt and no review, bypassing human-in-the-loop for
exactly the files that control the gate. Restrict auto-grant to the genuinely-
satisfied (a+b+c) merge path; the docs/process exemption reverts to bare exit-0
passthrough (one normal prompt). Corrects this PR's own decisions.md entry
accordingly ([decisions-edit]: a not-yet-merged draft entry, not history).

8 pipe tests green (adds docs-only -> passthrough).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 20:58:26 +02:00
timothyandClaude Opus 4.8 8565f731cd fix(process): #317 merge-consent gate auto-grants on satisfied path (no double-prompt)
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 7s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 8s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 7s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 8s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 4m58s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 8m39s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
The H6/H10 merge-consent hook's satisfied path did a bare `exit 0`, which does
not auto-approve a PreToolUse tool call — it only declines to block, so control
fell through to the normal permission system and the raw MCP prompt still fired.
A ready-to-merge PR was therefore confirmed twice (conversationally + a redundant
mechanical prompt). Emit permissionDecision "allow" (new `grant` decision) on the
satisfied and docs-exempt paths so the derived state IS the consent; deny/ask
unchanged (fail-closed); non-merge methods keep the exit-0 passthrough.

Docs: CLAUDE.md, kickoff HARD CONSTRAINTS, docs/decisions.md (append-only, pure insert).
Verified: 7 pipe tests (satisfied->allow, unticked->deny, stale->deny, red-CI->deny,
no-verdict->ask, no-creds->ask, non-merge->passthrough).

fixes #317

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 20:52:26 +02:00
timothyandClaude Opus 4.8 3d07c6818d ci(process): #311 H11 rebase-before-push hook + PR-scoped format-verify CI job
- H11: .husky/pre-push calls .claude/hooks/prepush-rebase-check.sh, which blocks a
  push whose branch is behind origin/main (rebase first; do not merge main in — a
  merge drags in files you didn't touch, e.g. legacy-BOM .cs, tripping the format
  hook on code that isn't yours). Fail-open; escape ETV_SKIP_REBASE_CHECK=1.
- New blocking `format` CI job: dotnet format --verify-no-changes scoped to the
  PR's changed .cs only (style + charset=utf-8/no-BOM), enforcing fix-as-you-touch
  without a big-bang reformat of the ~2500 legacy BOM files. .cs-free PRs skip and
  pass (always reports a status). Closes the "CI never checks charset" gap that let
  #269 land 17 BOM files (#310).

Docs (contributing.md §7 / decisions.md / lore) follow in the next commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 20:16:00 +02:00
timothyandClaude Opus 4.8 938733c3d0 fix(process): #303 H10 — anchor is_pos to the leading marker (monotonic hardening)
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 12s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 13s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 12s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m28s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 9m27s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Final re-review returned SHIP-IT with one contrived, pre-existing residual: a
line starting `Review-verdict: BLOCKED …` that ALSO contains a second literal
`review-verdict: mergeable` substring later on the same line read as positive.
Anchor the is_pos check to line-start so only the line's OWN leading verdict
word counts. Safe-by-construction: anchoring a positive matcher can only REDUCE
the allow-surface, so it cannot introduce a false-open (the dangerous
direction); the 21-case regression confirms no false-deny (all still
allow/deny/ask as before) + a new B6 case for this exact vector. No further
review round needed — the change is monotonic. shellcheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:14:37 +02:00
timothyandClaude Opus 4.8 ff3df39c43 fix(process): #303 H10 — anchor verdict marker to line-start (close self-reference false-open) [decisions-edit]
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 12s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 12s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 4m59s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m13s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Second adversarial re-review (of the fix commit itself — dogfooding H10) found
a remaining false-open: the `review-verdict:` marker was matched anywhere on a
line, so a comment merely QUOTING the positive template — an instruction
("please post: Review-verdict: MERGEABLE @ <head>"), a blocking comment
explaining how to clear itself, or the gate's OWN suggestion text echoed into a
comment — was classified as a real head verdict and self-approved the merge.

Fix: anchor the marker to line-start (`^[[:space:]]*review-verdict:`). A real
verdict line starts with the marker; quoted/instructional uses have text before
it. Also drops the dead `nosha` var (SC2034).

Finding 2 (a BLOCKED mis-anchored to an OLDER sha doesn't retract a
MERGEABLE@head) is deliberately NOT "fixed": staleness is symmetric — a
pre-fix BLOCKED@oldsha must not block forever after the fix changes the sha and
earns a fresh MERGEABLE@head. To retract, re-review head and post BLOCKED@head.
Documented in decisions.md.

Pipe-tested 21 cases (5 new: instructional-quote, self-reference reason-text,
line-start-in-multiline, leading-whitespace, blocking-quotes-template), all
deny/ask/allow correct. bash -n + shellcheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:11:05 +02:00
timothyandClaude Opus 4.8 f87a2092c4 fix(process): #303 H10 — anchor sha match to the @<sha> field; retraction-wins [decisions-edit]
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 7s
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 7s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m13s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m0s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Adversarial review found false-opens in the first cut:
- `grep -F "$short"` was an unanchored substring test: a MERGEABLE verdict for
  a DIFFERENT/older commit was accepted whenever the head 7-prefix appeared
  anywhere on the line (inside a longer sha, or an unrelated commit URL). Now
  each verdict line's `@ <sha>` token is extracted and matched to head by git
  short-sha prefix semantics (head begins with token, token >=7 chars).
- No retraction semantics: a later `BLOCKED @ head` didn't override an earlier
  `MERGEABLE @ head`. Now a negative verdict on head wins -> deny.
- A 7-digit build number falsely tripped the "references an older commit" deny;
  the `@`-anchored parse fixes it -> a marker with no @sha now asks, not denies.

Also documents the issue-comment scope (gate reads issues/{pr}/comments, not
Gitea formal-review bodies). Pipe-tested: 16 cases incl. 4 adversarial
false-open reproductions, all now deny/ask. bash -n + shellcheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:01:51 +02:00
timothyandClaude Opus 4.8 9fd8f40541 chore(process): #303 H10 — review-verdict merge-gate (latest commit must be reviewed)
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 13s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 13s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m4s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 9m43s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Folds condition (c) into pretooluse-merge-consent.sh (H6): a PR merge is
allowed only when a `Review-verdict:` comment references the PR's CURRENT
head sha — proving the latest commit was reviewed, not a stale earlier diff
(mechanizes the ersatztv#242 "re-review the fix commit" lesson).

Graceful adoption mirrors H6's Done-when tiering:
- positive verdict @ head        -> allow
- verdict @ older sha (stale)    -> deny  (#242 failure mode)
- head verdict negative          -> deny
- marker with no sha / none yet  -> ask
- comments unfetchable           -> ask

Reuses H6's PR fetch, docs-only exemption, and Gitea-auth-from-env (one hook,
no detection drift — per the #303 methodology review). Pipe-tested 12 cases.

Docs: decisions.md (new H10 entry + TOC), CLAUDE.md Task Completion Protocol.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 18:54:05 +02:00
timothyandClaude Opus 4.8 59ecafecce docs(process): #303 fold review nits — H3 case-insensitive, granularity wording, newline note [decisions-edit]
Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 10s
Build ErsatzTV Image / decisions.md append-only (pull_request) Successful in 11s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 14s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 5m9s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 6m0s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Adversarial review (PR #305, MERGEABLE) nits:
- H3: `grep -iE` so a root `Screenshot.PNG` is caught too (was lowercase-only).
- decisions-guard.sh: comment the trailing-newline assumption (dropping the final
  newline would make git render the next append as a last-line modify -> false-block;
  self-correcting via [decisions-edit], .editorconfig enforces the newline).
- docs: clarify CI is PR-wide (`range`) vs Husky per-commit (`staged`) — shared
  detection logic, deliberately different granularity; local hook is the stricter gate.
  Replaces the slightly-overstated "can't drift" wording. Touches the committed H9
  decisions.md entry, hence the [decisions-edit] token.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 18:18:30 +02:00
timothyandClaude Opus 4.8 56ef1df936 feat(process): #303 H3 root-screenshot guard + H9 decisions.md append-only guard
H3 (Husky pre-commit): reject a staged root-level *.png — belt-and-suspenders with
the .gitignore screenshot rule so `git add -f` still can't land a review artifact.

H9 (append-only decisions.md): new shared hook `.claude/hooks/decisions-guard.sh`,
wired into Husky commit-msg (staged mode) and a new blocking `decisions-guard` CI job
(range mode). Blocks any commit/PR that deletes or modifies an existing line of
docs/decisions.md — detected via `git diff --numstat` deleted-count, robust to markdown
`-` list markers — unless the message carries the `[decisions-edit]` token. Pure
insertions (a normal new entry) always pass. One implementation for local + CI so they
can't drift. Fail-open on any tooling trouble.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 18:13:12 +02:00
timothyandClaude Opus 4.8 0badff811d feat(process): #303 H6 merge-consent derived from ## Done-when checklist
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m26s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m32s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m48s
Wave 2 hook H6: derive merge-consent from state instead of memory. An
issue's ## Done-when checklist (issue body) becomes the machine-readable
source of truth for whether its PR may merge — the structural fix for the
queue-drift #303 tracks (status was living in append-only prose).

- pretooluse-merge-consent.sh (Claude PreToolUse on mcp__gitea__
  pull_request_write): a merge is ALLOWED only when the PR's CI is green
  AND every ## Done-when box on the linked issue (fixes #N) is ticked;
  DENY on an unticked box / red CI; ASK (human prompt) when state isn't
  derivable (no linked issue, no section, no creds, Gitea down). Docs/
  process-only PRs exempt.
- .husky/pre-push -> prepush-donewhen.sh: fail-open backstop for a direct
  `git push origin main`; blocks only on a positively-proven unticked box.

Gitea auth from env only (ETV_GITEA_BASICAUTH / ETV_GITEA_TOKEN,
ETV_GITEA_URL) — nothing committed; without creds the gate degrades to
today's manual confirmation, never a silent pass. Non-breaking rollout:
until issues adopt ## Done-when the merge hook simply asks.

Pipe-tested: non-merge->allow, no-creds->ask, docs-only->allow, checklist
parser (unit), linked-issue extraction, and a live end-to-end block path
(temp Done-when on #303 -> exit 1 -> restored). Docs: CLAUDE.md Task
Completion Protocol + decisions.md entry. Refs #303.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:29:34 +02:00
timothyandClaude Opus 4.8 35e41fdaad chore(process): #303 H7 worktree-owner guard + complete Wave 1 wiring
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 8m1s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 10m13s
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Wave 2 hook H7: never commit/merge inside a sibling worktree another
session created (burned us on #289 path-leak + the plumbing-merge
workaround). Ownership = a per-session .claude-worktree-owner marker:
- posttooluse-worktree-marker.sh stamps a worktree with session_id on
  `git worktree add` (parses the <path> arg past -b/-B/--reason flags).
- pretooluse-worktree-guard.sh denies `git commit`/`git merge` whose
  effective dir (resolves `git -C <p>` and leading `cd <p> &&`) is a
  worktree whose marker names a DIFFERENT session. Fail-open: no marker,
  unparsable, or own session -> allow. Main tree + pre-convention
  worktrees are never marked, so unaffected.

Also completes Wave 1's rollout, which committed pretooluse-bash-guard.sh
but left .claude/settings.json and the agent-ram/nav-guard hooks
untracked (so nothing was actually wired). Adds the settings.json that
registers all five hooks (PreToolUse Bash x2, nav, Agent; PostToolUse
Bash) + the .gitignore worktree-marker line, screenshot-scratch rules,
and the decisions.md TOC left uncommitted last session.

All hooks pipe-tested (7 guard cases + 6 marker cases). Refs #303.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:01:52 +02:00
timothyandClaude Opus 4.8 98e4767322 chore(process): #303 rigor hooks Wave 1 + decisions.md TOC + screenshot gitignore
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 4m9s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 5m22s
Build ErsatzTV Image / Build & push image (amd64) (push) Has been cancelled
Wave 1 rigor-enforcement hooks (methodology review, #303), fail-open + pipe-tested:
- pretooluse-bash-guard.sh  — deny golden-baseline regen env var in command position (H1)
- pretooluse-nav-guard.sh   — deny browser-tab nav to /iptv,.m3u8,/artwork (H2)
- pretooluse-agent-ram.sh   — RAM-gate Agent/Task spawn, deny <10%% / ask <20%% (H8)
- .claude/settings.json wires the three PreToolUse matchers

Also: docs/decisions.md gains a 44-entry Index/TOC (additive); .gitignore ignores
stray root *.png + .playwright-mcp/ (E2E scratch). Wave 2/3 + follow-ups tracked in #303.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 16:53:25 +02:00
timothyandClaude Opus 4.6 5034941a79 Add Claude Code project setup
Build / Calculate version information (push) Successful in 22s
Build / build_and_upload (push) Failing after 0s
Build / build_images (push) Failing after 0s
Close stale issues / stale (push) Successful in 14s
- CLAUDE.md with architecture overview and development guide
- .mcp.json with docker, ssh, gitea, csharp-lsp, and nuget MCP servers
- Skills for ersatztv and jellyfin
- .gitignore: exclude .mcp/ (built MCP tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 23:50:41 +01:00