feat: propose-work — push, open the PR, and move the issue, in one command #21

Merged
fabi merged 2 commits from feat/propose-work into main 2026-09-12 14:30:09 +00:00
Owner

GITEA-SETUP.md has listed this under Still to build since 2026-09-04:

propose-work, superseding push-work — push the branch and open the PR with
Closes #N and set the label, in one step. Today push-work does the first third;
the other two thirds being manual is how they get skipped.

PROTOCOL.md §Pull requests requires all three. Until now they were three things to
remember — and PR #20 had to add a warning to port-loop.md about the two that get
forgotten. A rule enforced by memory decays; this makes the sequence structural.

It does not reimplement push-work's refusals — it calls it

main, shared branches and force-push stay refused in exactly one place. Duplicating them
would let the two copies drift, and the copy that drifts is the one that matters.

Three design choices

The issue number is derived from the branch name, which PROTOCOL.md already specifies
as auto/<agent>/<issue#>-<topic>. A PR therefore cannot cite a different issue than the
branch was cut for — a mismatch no reviewer would catch. -i overrides.

-m is mandatory. PROTOCOL.md says an issue in state/needs-human must say what to
look at and what pass and fail look like, "so a person can judge it in under a minute".
Refusing without that line costs the agent one retry; omitting it costs a human a round
trip.

It moves the label rather than adding it. Other state/* labels are removed — leaving
state/in-progress attached makes the board lie about what is waiting on a person.

Credential handling

The token is read from a file and handed to curl through a --config document on stdin:
never an argument, never exported. Arguments are world-readable in /proc, and this token
can push.

Verified that the mechanism actually delivers the header rather than silently dropping
it — with a bogus token the API answers invalid username, password or token, while the
same URL with no header returns the label list anonymously. If the header were being
dropped, the bogus-token call would have succeeded.

What is verified, and what is not

issue derived from branch #42 from auto/decoder/42-widget-census
refusal without -m
refusal when the branch carries no number
--dry-run sends nothing
repo + API host derived from the remote fabi/Sylpheed
--config delivers the auth header (above)
end-to-end against a real issue not run

Stated plainly: there is no end-to-end run. That needs a real agent token and a real
issue, and this desktop is the second machine — the agent credentials live on the agent
box. Everything above the network call is exercised; the POSTs are not. First real use
should be watched, which Phase 7 is going to do anyway.

--dry-run deliberately does not require a credential — it exists so an agent can
check the command it is about to run, and demanding a token it never sends would make the
check unavailable exactly where it is cheapest. That was a bug in my first draft, caught by
trying to dry-run it on a machine with no token.

Identical in both agents' bin/ on purpose: the agent name comes from the branch, so there
is nothing per-agent to diverge.

🤖 Generated with Claude Code

`GITEA-SETUP.md` has listed this under **Still to build** since 2026-09-04: > **`propose-work`**, superseding `push-work` — push the branch *and* open the PR with > `Closes #N` *and* set the label, in one step. Today `push-work` does the first third; > the other two thirds being manual is how they get skipped. `PROTOCOL.md` §Pull requests requires all three. Until now they were three things to remember — and PR #20 had to add a *warning* to `port-loop.md` about the two that get forgotten. **A rule enforced by memory decays; this makes the sequence structural.** ## It does not reimplement push-work's refusals — it calls it `main`, shared branches and force-push stay refused in exactly one place. Duplicating them would let the two copies drift, and the copy that drifts is the one that matters. ## Three design choices **The issue number is derived from the branch name**, which `PROTOCOL.md` already specifies as `auto/<agent>/<issue#>-<topic>`. A PR therefore cannot cite a different issue than the branch was cut for — a mismatch no reviewer would catch. `-i` overrides. **`-m` is mandatory.** `PROTOCOL.md` says an issue in `state/needs-human` must say what to look at and what pass and fail look like, *"so a person can judge it in under a minute"*. Refusing without that line costs the agent one retry; omitting it costs a human a round trip. **It moves the label rather than adding it.** Other `state/*` labels are removed — leaving `state/in-progress` attached makes the board lie about what is waiting on a person. ## Credential handling The token is read from a file and handed to curl through a `--config` document on stdin: never an argument, never exported. Arguments are world-readable in `/proc`, and this token can push. **Verified that the mechanism actually delivers the header** rather than silently dropping it — with a bogus token the API answers `invalid username, password or token`, while the same URL with no header returns the label list anonymously. If the header were being dropped, the bogus-token call would have *succeeded*. ## What is verified, and what is not | | | |---|---| | issue derived from branch | ✅ `#42` from `auto/decoder/42-widget-census` | | refusal without `-m` | ✅ | | refusal when the branch carries no number | ✅ | | `--dry-run` sends nothing | ✅ | | repo + API host derived from the remote | ✅ `fabi/Sylpheed` | | `--config` delivers the auth header | ✅ (above) | | **end-to-end against a real issue** | ❌ **not run** | **Stated plainly: there is no end-to-end run.** That needs a real agent token and a real issue, and this desktop is the second machine — the agent credentials live on the agent box. Everything above the network call is exercised; the `POST`s are not. First real use should be watched, which Phase 7 is going to do anyway. `--dry-run` deliberately does **not** require a credential — it exists so an agent can check the command it is about to run, and demanding a token it never sends would make the check unavailable exactly where it is cheapest. That was a bug in my first draft, caught by trying to dry-run it on a machine with no token. Identical in both agents' `bin/` on purpose: the agent name comes from the branch, so there is nothing per-agent to diverge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fabi added 1 commit 2026-09-10 17:18:13 +00:00
feat: propose-work — push, open the PR, and move the issue, in one command
All checks were successful
CI / Native — linux (pull_request) Successful in 34m50s
CI / WASM — Web (pull_request) Successful in 29m34s
CI / Formatting (pull_request) Successful in 55s
2f49e191c6
GITEA-SETUP.md has listed this under "Still to build" since 2026-09-04:

  `propose-work`, superseding `push-work` -- push the branch AND open the PR
  with `Closes #N` AND set the label, in one step. Today `push-work` does the
  first third; the other two thirds being manual is how they get skipped.

PROTOCOL.md 145-149 requires all three:

  * branch `auto/<agent>/<issue#>-<topic>`, one item per branch
  * open the PR with `Closes #<issue>` in the body
  * label the issue `state/needs-human` and say, in one line, what to look at

Until now those were three things to remember, and PR #20 had to add a warning
to `port-loop.md` about the two that get forgotten. A rule enforced by memory
decays; this makes the sequence structural.

WHAT IT DOES NOT DO is reimplement push-work's refusals -- it CALLS push-work,
so `main`, shared branches and force-push stay refused in exactly one place.
Duplicating them would let the copies drift, and the copy that drifts is the
one that matters.

Three design choices worth stating:

  * THE ISSUE NUMBER IS DERIVED FROM THE BRANCH NAME, which PROTOCOL already
    specifies as `auto/<agent>/<issue#>-<topic>`. So a PR cannot cite a
    different issue than the branch was cut for -- a mismatch no reviewer would
    catch. `-i` overrides.
  * `-m` IS MANDATORY. PROTOCOL says an issue in `state/needs-human` must say
    what to look at and what pass and fail look like, "so a person can judge it
    in under a minute". Refusing without that line is cheaper than letting the
    label carry an empty promise and costing a human a round trip.
  * IT MOVES THE LABEL RATHER THAN ADDING IT -- other `state/*` labels are
    removed. Leaving `state/in-progress` attached makes the board lie about
    what is waiting on a person.

The token is read from a file and handed to curl through a `--config` document
on stdin: never an argument, never exported. Arguments are world-readable in
/proc and this token can push. Verified that the mechanism actually delivers
the header rather than silently dropping it -- with a bogus token the API
answers "invalid username, password or token", while the same URL with no
header returns the list anonymously, so the header is demonstrably being read.

Also verified: issue derived from the branch (#42 from
`auto/decoder/42-widget-census`), refusal without `-m`, refusal when the branch
carries no number, `--dry-run` sends nothing, and repo/API derivation from the
remote. `--dry-run` deliberately does NOT require a credential -- it exists so
an agent can check the command it is about to run, and demanding a token it
never sends would make the check unavailable exactly where it is cheapest.

Identical in both agents' bin/ on purpose: the agent name comes from the
branch, so there is nothing per-agent to diverge.

UNVERIFIED, and stated as such: no end-to-end run. That needs a real token and
a real issue, which this desktop does not have -- it is the second machine, and
the agent credentials live on the agent box. Everything above the network call
is exercised; the POSTs are not.

Refs #11

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fabi added 1 commit 2026-09-11 04:14:05 +00:00
docs: strike propose-work from "Still to build" — it is built
All checks were successful
CI / Native — linux (pull_request) Successful in 33m50s
CI / WASM — Web (pull_request) Successful in 29m56s
CI / Formatting (pull_request) Successful in 1m6s
48e3f1a983
GITEA-SETUP.md listed `propose-work` as unbuilt, and this PR builds it without
touching that list, so merging would leave the doc describing a tool as missing
that exists. Strike it the way `gitea-verify` already is: say what was built,
then say what is still missing — here, an end-to-end run, which only the agent
box can do because only it holds a token that can make the POSTs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fabi merged commit c2213c0c47 into main 2026-09-12 14:30:09 +00:00
Sign in to join this conversation.