97397906e3771baa68355248b3a6bf8f4d4733d0
13
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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>
|
||
|
|
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>
|
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
5034941a79 |
Add Claude Code project setup
- 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> |