Files
ersatztv/docs/decisions/workflow-process.md
T
5fb9c8537a
Build ErsatzTV Image / CI toolchain image resolves (push) Successful in 10s
Build ErsatzTV Image / Delimiter ban (release path) (push) Successful in 17s
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 9m20s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 6m25s
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (push) Successful in 5m48s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (push) Skipped
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (push) Skipped
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 4m10s
docs(747): re-verify the Gitea 1.25.4-pinned CI claims on 1.27.1, and measure the merge-gate semantics that were source-attested (#867)
Population derived from `git ls-files`, not the issue's 9-key list (~21 claim sites).

Re-confirmed unchanged on 1.27.1: the distinct `skipped` commit-status state; `compare` serving
no `files`; no agent-side cancel route (REST route + swagger only); `branches: [main]` suppressing
the run off a non-main base.

Newly measured on four throwaway scratch bases, `main`'s rule never PATCHed: an absent required
context blocks an ORDINARY merge without needing `block_admin_merge_override` (that field governs
the FORCE path only), and `enable_bypass_allowlist` with an empty list is NOT a substitute for it.
Trap recorded: the PR API reports `mergeable: true` while such a merge is refused.

Left explicitly dated with reasons: push-supersession auto-cancel, `pull_request_target` overlap,
`--depth=1` no-merge-base, and the scope-enum/`reqRepoWriter`/403 items. Not a corpus sweep, and
`ci.actions-credential-scoping` now says so. `review-verdict.yml` untouched — #763 holds that file.

Five adversarial review rounds (21/12/9/6/2). Caveat: all same-model-family; Codex was rate-limited.

fixes #747

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Timothy <timothy@noreply.gitea.tblindustries.be>
2026-08-28 16:51:13 +00:00

9.4 KiB

Session-workflow, CI-triage & review-process decisions (#542)

How we work — orchestration, CI-run triage, review routing, worktree and parallel-session hygiene, and the tooling gotchas that have cost real sessions. Distinct from the engineering "why the code does X" records: nothing here decides product behaviour.

These records were extracted from docs/handoffs/chicorytv-issue-queue.md in #542. That doc had become the only copy of most of this knowledge — an inventory found ~30 of ~38 lore bullets had no decision record at all — so it was neither retrievable via the catalog nor safe from a careless prune. The rules are now records; the handoff doc keeps the instruction and points here for the why.

Related: release-ci-governance.md (merge/release gates and hooks), startup.parallel-orientation (session-start tracks), session.shared-checkout-refresh (session-end shared-tree refresh).


— it is noise, not a cause. Grep for the failure marker, not for the word "error".

next session debugging a phantom. Split the two counts explicitly.

runners, so high load is not always CI's own doing.

one of the 4 runner slots while doing it, which is a real cost with several parallel sessions.

conflating them leads to load-watching behaviour that this record forbids.

before theorising about a cause.

rather than diagnosis.

suite in exchange for fixing one known-slow case.

local verdict; it is not the first line of defence.

the merge record can be re-read later.

cheap fast private check is skipped in favour of the expensive slow shared one.

real" and let a starving fix ship. Send the fix approach up with the finding.

timer that fires once more than the author pictured.

failure mode a second, differently-scoped reviewer catches.

still holds.

refresh script that mitigates it. This record covers the git-state failure mode only.

#216.

route mutates only the ref, so the merge lands without disturbing the owner's tree.

tests". A git status in the new worktree before the first build catches this in one command.

hides exactly the change the golden net exists to surface.

not the agent.

the review label and milestone. A milestone is not required — backlog is unmilestoned.

the single most contended file across parallel sessions.


claimed local admin makes its Setup-gate spec unreachable.

not the overlay inputs. Route branding verification through a real channel playout instead.

retrying the same call.

names, then update this record once verified.

settle on and the browser tool hangs.

and write the structured close comments the applicable `AGENTS.md` / `CLAUDE.md` protocol requires.

feature's issue for its end-state *before* choosing between a switch and a wall.

commit `f93458c7`, where it was dropped by a later prune rather than disproved.)

rule, which is the habit being built.

inventing one) and an explicit statement of who owns the printed PID.

## Records formerly in this file

Each record below moved to its own file under `records/` (ersatztv#610); the rationale is
unchanged. Resolve by **key** — that is the stable identity. A date-based pointer from
another doc or an old issue comment should land here and then follow the link.

- 2026-07-21 — "Make X consistent with Y" review findings are new code and get new-code scrutiny (#542) — [`process.consistency-fix-new-code-scrutiny`](records/process/consistency-fix-new-code-scrutiny.md)
- 2026-07-21 — A dropped subagent connection is transient; resume, don't restart (#542) — [`process.subagent-drop-resume`](records/process/subagent-drop-resume.md)
- 2026-07-21 — A killed CI job reports `conclusion: failure`; read the log tail before diagnosing the diff (#542) — [`ci.killed-job-triage`](records/ci/killed-job-triage.md)
- 2026-07-21 — A lock/channel "no cross-release" verdict must enumerate every producer via grep (#542) — [`process.lock-ownership-enumerate-producers`](records/process/lock-ownership-enumerate-producers.md)
- 2026-07-21 — A lone `decisions lifecycle` red is a known infra flake: do nothing (#542) — [`ci.decisions-lifecycle-flake`](records/ci/decisions-lifecycle-flake.md)
- 2026-07-21 — An infra-shaped red under host load is not a code failure (#542) — [`ci.infra-shaped-red-under-load`](records/ci/infra-shaped-red-under-load.md)
- 2026-07-21 — Arm the CI monitor at PR-open, via the commit-status endpoint (#542) — [`ci.monitor-armed-at-pr-open`](records/ci/monitor-armed-at-pr-open.md)
- 2026-07-21 — BOM/format pre-push detection: use the `xxd` loop, and run `dotnet format --include` under `bash -c` (#542) — [`process.bom-format-detection-recipe`](records/process/bom-format-detection-recipe.md)
- 2026-07-21 — Batch your pushes: there is no agent-side cancel route on Gitea 1.25.4; REST route re-probed on 1.27.1 (#542) — [`ci.batch-pushes-no-cancel-route`](records/ci/batch-pushes-no-cancel-route.md)
- 2026-07-21 — Before deleting a "workaround", enumerate every behavior it provided (#542) — [`process.enumerate-workaround-behaviors-before-deleting`](records/process/enumerate-workaround-behaviors-before-deleting.md)
- 2026-07-21 — Bound parallel builds by FREE RAM, never regenerate goldens (#542) — [`process.build-concurrency-limits`](records/process/build-concurrency-limits.md)
- 2026-07-21 — Build and verify locally, then trust it; CI confirms (#542) — [`ci.verify-locally-ci-confirms`](records/ci/verify-locally-ci-confirms.md)
- 2026-07-21 — Channel branding is NOT testable through the troubleshooting-playback API (#542) — [`testing.troubleshoot-path-cannot-test-branding`](records/testing/troubleshoot-path-cannot-test-branding.md)
- 2026-07-21 — Claim with `in-progress` before working; claiming is not collision safety (#542) — [`process.parallel-session-claim`](records/process/parallel-session-claim.md)
- 2026-07-21 — Do not gate or throttle pushes on host health (#542) — [`ci.no-host-health-gating`](records/ci/no-host-health-gating.md)
- 2026-07-21 — Every open issue carries a `priority:` label — run the H12 audit at session end (#542) — [`process.issue-qualification-audit`](records/process/issue-qualification-audit.md)
- 2026-07-21 — Fix onto an unmerged feature branch by branching off it and FF-pushing (#542) — [`process.branch-off-feature-branch`](records/process/branch-off-feature-branch.md)
- 2026-07-21 — Gitea's `?milestones=` issue filter silently no-ops on names containing `:` or `+` (#542) — [`ci.gitea-milestone-filter-noop`](records/ci/gitea-milestone-filter-noop.md)
- 2026-07-21 — Harden a soon-to-grow feature with a runtime posture, not a hardcoded clamp (#542) — [`process.harden-with-runtime-posture-not-clamp`](records/process/harden-with-runtime-posture-not-clamp.md)
- 2026-07-21 — Heavy-render web tests need explicit per-test vitest timeouts on the CI VM (#542) — [`ci.web-test-per-test-timeouts`](records/ci/web-test-per-test-timeouts.md)
- 2026-07-21 — Independent cross-model review is mandatory on risky diffs; a skip is a stated, auditable exemption (#542) — [`process.independent-review-rubric`](records/process/independent-review-rubric.md)
- 2026-07-21 — Launch Codex cheap workers via `codex exec`, not `spawn_agent` (#542) — [`process.codex-cheap-worker-launch`](records/process/codex-cheap-worker-launch.md)
- 2026-07-21 — Live-E2E runs BEFORE the push, and downloads are curled, not browsed (#542) — [`testing.live-e2e-prepush-timing`](records/testing/live-e2e-prepush-timing.md)
- 2026-07-21 — Local build/test gate + cold-context review run BEFORE the push, not after (#542) — [`process.local-gate-before-push`](records/process/local-gate-before-push.md)
- 2026-07-21 — Never commit or merge inside a worktree you did not create (#542) — [`process.foreign-worktree-plumbing-merge`](records/process/foreign-worktree-plumbing-merge.md)
- 2026-07-21 — One worktree, one committing agent (#542) — [`process.one-worktree-one-committing-agent`](records/process/one-worktree-one-committing-agent.md)
- 2026-07-21 — Playwright-MCP: curl download endpoints, never open a tab or `window.open` (#542) — [`testing.playwright-mcp-download-and-recovery`](records/testing/playwright-mcp-download-and-recovery.md)
- 2026-07-21 — Review disagreement on a gate PR escalates to the frontier judge — and the proposed FIX escalates with it (#542) — [`process.review-disagreement-frontier-judge`](records/process/review-disagreement-frontier-judge.md)
- 2026-07-21 — Run `scripts/e2e-local.sh` against a FRESH config dir; a reused one hangs the readiness probe (#542) — [`testing.e2e-local-fresh-config-dir`](records/testing/e2e-local-fresh-config-dir.md)
- 2026-07-21 — The PR routine is a fixed sequence: validate locally, then push, then only follow-up commits (#542) — [`process.pr-routine-sequence`](records/process/pr-routine-sequence.md)
- 2026-07-21 — The shared tree at /Users/timothy/ersatztv is read-only and tells you nothing about `main` (#542) — [`process.shared-tree-readonly`](records/process/shared-tree-readonly.md)
- 2026-07-21 — `cancelled` is not `failure`; a cancelled run is no verdict (#542) — [`ci.cancelled-is-not-a-verdict`](records/ci/cancelled-is-not-a-verdict.md)
- 2026-07-25 — E2E cleanup kills only the PIDs it started; never a `pkill -f` pattern (#586) — [`testing.e2e-cleanup-scope-by-pid`](records/testing/e2e-cleanup-scope-by-pid.md)
- 2026-07-25 — Name the model tier for every dispatched agent; a PreToolUse gate makes the silent default visible (#583) — [`process.per-agent-model-routing`](records/process/per-agent-model-routing.md)