The kickoff dispatches a cheap model to rank the backlog, and the lore then made the
orchestrator re-derive its mechanical claims (deps, milestone tiering, priority order,
in-progress state) because a small model kept getting them wrong. This pays that tax down:
the mechanical checks are now CODE — GET /dependencies exclusion, LOCAL
.milestone.state/review/priority tiering (never the no-op ?milestones= filter),
(tier,priority,issue#) ordering, in-progress/parked/PR exclusion — leaving only two
JUDGMENT flags (CLAIM?, UMBRELLA?) for a human/model to resolve.
- scripts/select-queue.sh: fail-open on no-creds/unreachable; ~1.3s; shellcheck clean;
UMBRELLA? computed in the jq pass (no per-candidate body fetch).
- handoff kickoff: run the script FIRST; trust its deps/tiering/ordering, recheck only flags.
- handoff lore: the three 'cheap selector unreliable → re-derive by hand' bullets kept as the
EVIDENCE for why the script exists; the prescription is redirected to 'run the script'.
Operator-requested this session: 'rather than have the lore make us redo the selector's work,
improve the selector.'
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The handoff PR-routine and HARD-CONSTRAINTS placed the cold adversarial review
AFTER push/PR-open. New standing rule (Timothy, 2026-07-17): local gate + cold
review run BEFORE the push; fold pre-push fixes into the branch; CI only
confirms. Runners should see only already-validated work — a Gitea run can't be
cancelled, so a run spent on a lint/review-caught issue is pure waste. Also notes
docs-only PRs are seconds now (ersatztv#416).
Operator-requested after PR #405 pushed 5 times, orphaning live runs the
operator had to cancel by hand. Batches every standing-lore correction this
session produced into one commit (per the batching rule it adds).
New HARD CONSTRAINTS:
- BATCH PUSHES. Cancellation is impossible from the agent side on Gitea 1.25.4
— REST .../runs/{id}/cancel and MCP cancel_run both 404, and the web-UI route
needs a session+CSRF that doesn't script. Only the operator can cancel, so an
orphaned run holds a runner slot until it finishes. If you must supersede a
live run, SAY SO.
- TRUST THE GITEA BUILD QUEUE. Do not gate/throttle a push on host health; the
runners were retuned for stability. Batch because you can't cancel what you
orphan, not to protect the host.
- BOM-CHECK touched .cs before pushing. The #311 gate is fix-as-you-touch, and
it bit two sessions the same day (PR #405 ×6; #70/PR #402 ×19 via Python
utf-8-sig writing BOMs back). Verify your detector — an od-based grep reported
all-clean while 19 files were dirty.
Corrects a claim I nearly published: `dotnet format --include` DOES work here.
The apparent no-op was the SHELL — CI's mapfile is bash-only, zsh has no
mapfile → empty array → zero files → exit 0. Run it under bash -c.
THERE IS NO "main checkout" — the biggest correction here.
/Users/timothy/ersatztv is a shared mutable working tree whose HEAD is whatever
the last session left there. Its name lies, and it bit TWO sessions on
2026-07-17, both doing the obvious thing: one assumed main and committed onto
the #604/CI session's branch 24s after that session's own commit; another read
git log there and concluded main was "4 behind origin" — a phantom. Framed as a
design flaw, not a discipline failure: "check git status first" appears to
confirm the false assumption and then goes stale (it WAS on main at 12:46 and
wasn't by 14:17). Worktree discipline itself is healthy — 10 feature worktrees.
Diagnosing CI reds — three ways to misread one, all hit this session:
- A KILLED job reports conclusion=failure, not cancelled. The tell is a log that
stops mid-step with NO error and NO failure marker. A runner retune killed run
1006's migration + E2E on a BOM-only diff that couldn't break them. Log
timestamps are UTC, host is UTC+2 — convert before correlating.
- cancelled ≠ failure: a cancel is NO verdict, and a run marked failure may hold
a genuine job failure from BEFORE the cancel. Monitors must count FAILED and
CANCELLED separately.
- "Unable to pull refs/heads/v4" is act refreshing its action cache and is
followed by "Cloned …" — noise, not a cause. Grep the failure marker, not
the word "error". An infra-shaped red (setup/cache step, before your code
compiles) is not a code failure; don't file a CI bug off one sample.
Also: the cheap selector's failure modes are wider than deps+priority — it also
misses in-progress claim state and umbrella-vs-child.
Docs-only.
The #73 selector packet reported #385/#386 as deps:clear when both were
blocked, and missed #383 (the only priority:medium in tier 1) by ranking on
issue number. Either error alone produces a bad pickup, and the kickoff's
winner-only recheck cannot catch a wrong winner.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The `?milestones=<name>` filter returns the whole open-issue list (not a
filtered set) when the milestone title contains special chars, mis-tiering
issues during queue selection. Recipe: fetch open issues once, filter locally
on `.milestone.title`. Also notes the dependencies-API owner/repo/index form.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Backlog grooming (2026-07-17): the kickoff cascade now excludes any issue with an
open blocked-by dependency (Gitea GET /issues/{n}/dependencies; auto-clears when the
blocker closes) so a blocked issue is never picked ahead of its blocker. Adds the
parked->closed convention and the bundle model: related work is grouped via active-arc
milestones or cross-reference comments and detected on pickup via milestone + backlinks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The go-live arc (#237) is closed; add a 'Current phase' section stating the
terminal transition to backlog mode + structural guidance (empty arc/gate/review
tiers are the expected steady state; fall through to the priority-label cascade).
Drift-proof: terminal fact + structure only, no named frontier issue.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the outstanding Plex flow in the #202 post-merge integration
checklist against a disposable test Plex Media Server: pin-flow auth,
server/library discovery (the 2026-07-13 gap that returned servers:[]),
sync-preference persistence, and sign-out cleanup all passed. Records
exact API state evidence and the test-admin credential reset.
fixes#333
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the tested GPT-5.4-Mini low-effort worker command, current spawn-agent limitation, and unsupported legacy model trap so future sessions can route bounded work without rediscovery.
Refs #355
Co-Authored-By: OpenAI Codex <codex@openai.com>
Clarify that enabled web_search makes a cheap worker tool-bearing even when the selector intends to use only Gitea, reserve minimal for tool-free supplied-evidence synthesis, and require hard evidence/output caps.
Co-Authored-By: OpenAI Codex <codex@openai.com>
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>
scripts/issue-qualification-audit.sh: lists OPEN issues missing a `priority:` label
(the #237 ranking keys off priority:/gate labels, so an unlabeled issue is invisible
to it). "Fully qualified" = has a priority: label; gate-vs-backlog derives from the
review label / milestone, and a milestone is NOT required (backlog is unmilestoned).
Advisory (exit 1 if any unqualified); fail-open without Gitea creds. Wired into the
kickoff session-end protocol + a lore bullet. Tested live (flagged 2) + no-creds no-op.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- contributing.md §7: document fix-formatting-as-you-touch (normalize a legacy file you
edit, incl. BOM strip; no big-bang), enforced by the pre-commit hook + the new format CI job.
- kickoff lore: flip "merge main into PR branch" -> "rebase on origin/main" (H11 enforces it);
keep the regenerate-generated-artifacts-on-conflict nugget.
- decisions.md: new entry (+ TOC) for both decisions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two of the four non-hook #303 process follow-ups (the two docs items; the
security scan and migration-on-prod-copy smoke are deferred to their own
sessions):
1. Codex-skip rubric (kickoff workflow lore): an independent review pass is
MANDATORY for diffs touching locks/concurrency, auth/security, API
write-path handlers, or migrations, or >~150 changed C# lines; skippable
only for a pure-SPA/docs leaf, and a skip must be stated + justified. Makes
self-exemption an auditable claim (the correlated-blindspot net).
2. Live-E2E is now a STATED REQUIREMENT for API write-path handler changes:
new "When live-E2E is required" section in docs/e2e-local.md + a decisions.md
entry, formalizing the #229 lore bullet. The seeding recipe was already in
e2e-local.md (added for #220), so the stale "recipe not yet in docs" lore
bullet is pruned to a pointer.
Docs-only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The lore had grown into a changelog (~40 verbose bullets). Removed bullets
whose substance lives in docs/decisions.md or api-conventions §7a/b/c
(#251 post-commit None, #252/#259 reconcile, #253 PR3/PR4 concurrency, addTo
layer, DTO nullable facts, disabled={saving}) and dead Blazor-parity process
notes (parity verdicts, screen!=parity, unwired-endpoint gaps, #205/#206
docs-staleness). Condensed the survivors. Added a scope banner: this section
is standing workflow/orchestration lore only; engineering decisions go in
decisions.md, never here (they duplicate and drift).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Recent session pickups re-did finished work (thought closed issues were
still open). Root cause: DONE/OPEN status lived in append-only prose (arc
inline markers + each comment's 'Recommended next'), which lags real issue
state — worst across parallel sessions narrating each other's merges.
Kickoff prompt fixes:
- SOURCE OF TRUTH = live Gitea state, never prose; verify OPEN (issue AND
milestone) before claiming.
- Gate/frontier defined structurally (lowest-numbered open arc item), not
hardcoded to an issue number (an earlier pass re-planted the bug by
hardcoding '#197 cluster'/'#91b milestone CLOSED').
- Label-based ranking: arc order -> gate(review label/milestone) -> priority.
- Pick order across arc-frontier / priority-pickup / audit pools.
- Bound the comment read (~6 newest); MCP-down REST fallback; #237 canonical.
- New lore bullet capturing the drift root cause + structural cure.
Companion edits to tracker #237 body (prune arc status -> 'Done (history)'
section) made live; noted in a session comment there.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Handoff file reduced to static kickoff prompt + append-only lessons lore;
queue/arc/session log live in pinned tracker ersatztv#237 with in-progress
claim labels and end-of-session triage. Decision recorded in decisions.md;
docs index updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cache-save hang resolved: run #371 test log shows actions/cache restore (732MB NuGet) +
save (~1s) + hit-skip working; warm Build&test ~9min. Record the Microsoft.OpenApi 2.0.0
(NU1903/GHSA-v5pm-xwqc-g5wc) assessment: DoS-only, unreachable (we generate not parse specs),
correctly non-blocking; low-priority bump to 2.7.5 available.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>