Compare commits

...
Author SHA1 Message Date
logan a5515d1d9f Merge remote-tracking branch 'origin/main' into docs/225-fixtures-wav-tags
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 3m11s
CI / e2e (pull_request) Successful in 10m51s
2026-09-03 09:55:30 -04:00
logan 7b90633456 Merge pull request 'fix(loop): refresh branches before merge, watch post-merge main CI' (#239) from feat/238-merge-leg-refresh-watch into main
CI / check (push) Successful in 3m10s
CI / e2e (push) Successful in 11m7s
default
2026-09-03 13:53:45 +00:00
logan 7838f45ed4 fix(loop): refresh branches before merge and watch post-merge main CI
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 3m16s
CI / e2e (pull_request) Successful in 11m30s
Adopting the six v0-era PRs surfaced two conflict-shaped cases the merge
leg handled only by luck. Behind-main branches are refused outright by
the repo's block_on_outdated_branch protection, so the leg now refreshes
every branch against origin/main before merging — which is also where a
textual conflict should surface, as diff text the loop resolves only
where it authored the hunks, otherwise abandoning the PR to a human
with a comment. And the one guard no mergeability check provides is the
push run on main after the merge: three PRs touching the same file can
merge cleanly and contradict each other, so a red main now halts the
loop instead of the tick reporting merged and moving on.

Closes #238
2026-09-03 09:38:05 -04:00
logan 2453d717cf Merge pull request 'feat(loop): autonomous backlog loop — tracker to merged main, scheduled' (#237) from feat/236-autonomous-backlog-loop into main
CI / check (push) Successful in 3m6s
CI / e2e (push) Successful in 11m37s
default
2026-09-03 03:54:15 +00:00
logan dfb338fc37 docs(skill): the WAV fixtures scan tagged, and have since #104
CI / check (push) Skipped
CI / e2e (push) Skipped
CI / check (pull_request) Successful in 2m45s
CI / e2e (pull_request) Successful in 10m12s
`fixtures.md` told an agent the WAV fixtures scan in untitled, that
there is no "Field Recordings" artist in the Artists view, and that
this is a known open bug "pinned by TestWAVTagsAreNotReadableYet" — a
test #104 deleted, because it existed to assert the reader did not work
and failed the moment it did.

That last clause is why this is worth a diff rather than being left to
rot: the paragraph is an instruction, and it instructs the next reader
that a spec asserting the *working* behaviour is the mistake. It is the
same #104 staleness #217 removed from `queue-selection.spec.ts`, one
file over, still telling agents to put it back.

Measured against a running app rather than corrected from the issue
text — and the seed had to be rebuilt first, since the one on disk
predated #104 and would have replayed a pre-#104 scan and confirmed the
stale paragraph. On a fresh `make sandbox-seed NAME=default`, both WAVs
carry a title, an artist credit and an album: "Field Recordings" is an
ordinary artist with 2 tracks and "Test Tones" has a cover row. The
only two tracks with no album at all are `unsorted/no-tags-at-all.mp3`
and `unsorted/title-only.mp3`.

The replacement also says that prose written before #104 disagrees,
because it does, and saying nothing is how the next reader reintroduces
the claim from a source this change deliberately does not touch.

Deliberately carries no `Closes` footer. #225 covers two halves, and
the second — the same staleness in two *dated* `.planning/NOTES.md`
entries — is left alone: whether measured history gets a correcting
clause is a judgement about what that file is for, which the issue
raises on purpose and this change must not settle by auto-closing it.
2026-08-30 03:36:54 -04:00
3 changed files with 54 additions and 12 deletions
@@ -42,11 +42,16 @@ strings and identical specs produce different bytes on different builds.
playback and then clicks pause races the track ending and fails
against a correct UI. Use `LONG_TRACK` (90 s, `edge-lengths`) exported
from `e2e/support/fixtures.ts`.
- **WAV tracks scan in untitled.** `backend/tagwriter` writes WAV tags
into a RIFF `id3 ` chunk and `dhowden/tag` has no RIFF parser, so
there is no "Field Recordings" artist in the Artists view. This is a
known open bug pinned by `TestWAVTagsAreNotReadableYet`; do not
"fix" a spec by asserting the broken behaviour elsewhere.
- **WAV tracks scan like every other format.** #104 added
`backend/riff`, so the scan reads the `id3 ` chunk `backend/tagwriter`
writes and both WAVs come in fully tagged: "Field Recordings" is an
ordinary artist in the Artists view, with a "Test Tones" album and a
cover. They are therefore not an example of an untitled or albumless
track — the only two tracks with no album are
`unsorted/no-tags-at-all.mp3` and `unsorted/title-only.mp3`. Prose
written before #104 says the opposite and names
`TestWAVTagsAreNotReadableYet`, a test that change deleted; that is
dated history rather than a description of the app.
## Seeds
+30 -7
View File
@@ -163,7 +163,22 @@ Merge when, and only when, **all** hold:
- the protection contexts `CI / check` and `CI / e2e` are green on the
PR's head, read from the API, not from the PR page's badge;
- the PR reports mergeable;
- the critique leg ran and no open blocker stands.
- the critique leg ran and no open blocker stands;
- the branch is **not behind `origin/main`** — the protection's
`block_on_outdated_branch: true` refuses it anyway; never
`force_manually_merged` around it.
**Refresh before every merge.** In the loop worktree: fetch, then
`git merge origin/main` on the PR branch, push. A textual conflict
stops the leg there — as diff text, not as a failed merge click: hunks
the loop authored are resolved by the loop; anything else is left with
`⟦loop⟧` comment for a human, never forced. After any refresh push,
re-poll the PR's own required contexts on the **new head** before
merging.
**Merges happen one at a time**, each re-reading state — the previous
merge moved `main`, and the next PR's mergeability is recomputed at
its own turn.
```
curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
@@ -172,12 +187,20 @@ curl -sS -X POST -H "Authorization: token $GITEA_TOKEN" \
-d '{"Do":"merge","merge_message_field":"default","force_manually_merged":false}'
```
Afterwards: `scripts/issue.sh list --state open` and check the footer
took. Close stragglers with `issue.sh close`, naming the merge commit.
`unclaim.yml` handles the label; it is not instant; reopening does not
restore it. Merging fans out to nothing (releases are the manual
`release.yml`, which the loop never runs) — the criticism stands before
the merge because nothing stands after it.
**Afterwards watch the `push` run on `main`** — the CI the merge
started. A red main after a loop merge is a **halt**: comment what is
known on the offending PR, mark the state file, stop taking new issues.
That run is the only thing between a clean textual merge of
independently-written PRs and a self-contradicting main; no
mergeability check sees it. Only a green main lets the tick proceed (to
footer verification, below).
Footer verification: `scripts/issue.sh list --state open` and check
the footer took. Close stragglers with `issue.sh close`, naming the
merge commit. `unclaim.yml` handles the label; it is not instant;
reopening does not restore it. Merging fans out to nothing (releases
are the manual `release.yml`, which the loop never runs) — the
criticism stands before the merge because nothing stands after it.
## Rails — the loop's absolute rules
@@ -140,6 +140,20 @@ from a concurrent session is caught before the first edit.
- **Only PRs the loop opened.** A collaborator's PR is never merged, never
commented on for pressure, never touched.
- **Every branch is refreshed against main before its merge**, in the
loop worktree — the refresh is where a textual conflict surfaces, as
diff text: hunks the loop authored are resolved there, anything else
is left to a human with a `⟦loop⟧` comment. The protection's
`block_on_outdated_branch` makes the refresh mandatory for adopted
(pre-loop) branches: behind `main`, a PR cannot merge at all.
Required contexts are re-polled on the refreshed head.
- **Merges are one at a time**, each re-reading state — the previous
merge moved `main`, and the next PR's mergeability is recomputed at
its own turn.
- **Post-merge, the `push` run on `main` is watched.** A red main after
a loop merge halts the loop. That run is the only guard against the
class no mergeability check sees: two PRs touching the same file,
merging cleanly, contradicting each other.
- The gate is the protection rule itself, read from the API: contexts
`CI / check*` and `CI / e2e*` green, PR mergeable. (Required approvals
is 0 today; if a second person changes protection rules, the merge