release create --asset / release upload — attach assets to a release, and print the release id #25

Closed
opened 2026-08-04 17:41:34 +00:00 by claude-bot-andresmgsl · 6 comments

Problem

stoke release create (on main @ 1.3.0) can create a release with a tag, title and notes, but there is no way to attach a binary asset, and no release upload / release edit subcommand to add one afterwards.

$ node src/cli.js release --help
Commands:
  list [options]    List releases in a repository
  view [options]    Show the release for a tag
  create [options]  Create a release (creates the tag too if it does not exist)

$ node src/cli.js release create --help
Options:
  -o, --owner <owner>   repository owner
  -r, --repo <repo>     repository name
  --tag <tag>           tag name for the release
  --target <ref>        branch or commit the tag is created from
  -t, --title <title>   release title
  -b, --body <body>     release notes (markdown)
  --body-file <path>    read the release notes from a file
  --draft               create as a draft release
  --prerelease          mark as a prerelease

Impact

A release whose payload is an installer or bundle cannot be mirrored with stoke alone. Mirroring github.com/heavy-duty/crew releases 0.1.0 and 0.1.1 — each a single crew-<version>.sh of 29 MB / 50 MB — needed stoke for the release object and then a raw API call per asset:

id=$(curl -s -H "Authorization: token $TOKEN" \
  "$FORGE/api/v1/repos/heavy-duty/crew/releases/tags/0.1.0" | jq -r .id)
curl -s -X POST -H "Authorization: token $TOKEN" \
  -F "attachment=@crew-0.1.0.sh" \
  "$FORGE/api/v1/repos/heavy-duty/crew/releases/$id/assets?name=crew-0.1.0.sh"

Note the extra round-trip: the asset endpoint is keyed by numeric release id, which release create does not print, so callers must re-query by tag to get it. Printing the id on create would help even if asset upload lands separately.

Proposed fix

  1. Add a repeatable --asset <path> to release create, uploading each file after the release is created:
    POST /api/v1/repos/{owner}/{repo}/releases/{id}/assets?name=<basename> (multipart field attachment).
  2. Add stoke release upload -o … -r … --tag <tag> --asset <path>... for attaching to an existing release.
  3. Optionally --asset-name <name> to override the uploaded filename, and include the release id in release create output.

Verification

stoke release create -o heavy-duty -r scratch --tag v0.0.1 --target main \
  -t v0.0.1 --body-file notes.md --asset ./dist/thing.sh
stoke release view -o heavy-duty -r scratch --tag v0.0.1   # asset listed with correct size

Related: #23 (no repo sync) and the repo create --owner gap — all three surfaced while mirroring a GitHub repo onto the forge.


Brought up to the issue contract by triage, 2026-08-20. The "Proposed fix" above listed options; the spec below decides them — in particular the release id, which epic #27 already folded into this issue's scope ("release create --asset + print the release id"). Nothing above was changed. Code references pinned at 4c61858.

Spec

Decisions, not options:

  • release create gains a repeatable --asset <path>. Each file is uploaded after the release object exists, via POST /repos/{owner}/{repo}/releases/{id}/assets?name=<basename>, multipart field attachment.
  • release upload -o … -r … --tag <tag> --asset <path>... is a new subcommand for attaching to an existing release. It resolves the tag to an id with the releases/tags/{tag} lookup getReleaseByTag() already implements.
  • --asset-name <name> is in scope, not optional. It overrides the uploaded filename and is only legal when exactly one --asset is given; with two or more it is a usage error, because one name cannot serve many files.
  • release create prints the numeric release id on success, alongside the tag and URL, so callers never need the re-query round-trip this issue documents.
  • Partial-upload behaviour is decided: the release is created first and is kept if an asset upload then fails. The command reports which assets landed and which did not, and exits non-zero. It does not delete the release — silently unwinding a published release is worse than an honest partial state the caller can finish with release upload.
  • The API client needs a multipart path it does not have. request() hardcodes Content-Type: application/json and JSON.stringifys the body, so it cannot carry a file. Add a distinct upload path that sets no manual Content-Type (letting FormData/fetch set the boundary) and streams the file rather than buffering it — crew's 0.1.0/0.1.1 payloads are 29 MB and 50 MB.
  • The 30 s timeout does not apply to uploads. REQUEST_TIMEOUT_MS = 30000 would abort a 50 MB push on any ordinary link; asset uploads take their own, longer timeout.

Tasks

  • Add a multipart/streaming upload path to src/api.js with its own timeout, plus an uploadReleaseAsset(owner, repo, id, path, name) method
  • Add repeatable --asset and single-use --asset-name to release create; print the release id on success
  • Add the release upload subcommand (tag → id resolution, repeatable --asset)
  • Partial-failure reporting: name what landed and what did not, keep the release, exit non-zero
  • Tests: multi-asset create, release upload onto an existing tag, --asset-name with one asset, --asset-name with two assets rejected, upload failure keeps the release and exits non-zero, Content-Type is not forced to JSON on the upload path
  • README: document --asset, --asset-name, release upload, and the printed id
  • Open the PR from a same-repo branch on heavy-duty/stoke, not a fork (fork PRs stall on the CI approval gate — see !28/!29)
  • When the PR reaches state:needs-human, request @andres by hand — the engine's own request 404s on this forge and its sweep log reports that failure as a success (#36, defect 1)

Acceptance criteria

  • release create … --asset a --asset b creates the release and attaches both; release view --tag … lists both with correct sizes
  • release create prints the numeric release id on success
  • release upload --tag <existing> --asset f attaches to a release created earlier, without recreating it
  • --asset-name renames a single asset, and is rejected with a usage error and non-zero exit when two or more --asset are given — the case that must fail
  • A failing asset upload leaves the release in place, names the assets that did and did not land, and exits non-zero
  • A file large enough to exceed the JSON client's 30 s timeout uploads successfully, proving the upload path has its own timeout and does not buffer the whole file

Test plan

Suite-level, against a stubbed HTTP layer: assert the upload request carries a multipart body and no hand-set application/json content type, that the id-resolution round-trip happens exactly once per release upload, and each rejection case above. Manual proof against the live instance:

stoke release create -o heavy-duty -r scratch --tag v0.0.1 --target main \
  -t v0.0.1 --body-file notes.md --asset ./dist/thing.sh
stoke release view -o heavy-duty -r scratch --tag v0.0.1   # asset listed with correct size

Dependencies

Part of #27 (P2). No blockers. Related: #23 and #24 — all three surfaced while mirroring a GitHub repo onto the forge, and all three touch src/cli.js. #32 (release 1.4.0) is not gated by this issue and no longer names it as a risk: its "Create release and attach .deb" step attaches with a raw curl -F attachment=@…, not with stoke release create, so the automated release path never touches the missing asset support (measured 2026-08-20; #32's body was corrected the same day). This gap bites only a hand-finished release.

## Problem `stoke release create` (on `main` @ `1.3.0`) can create a release with a tag, title and notes, but there is **no way to attach a binary asset**, and no `release upload` / `release edit` subcommand to add one afterwards. ``` $ node src/cli.js release --help Commands: list [options] List releases in a repository view [options] Show the release for a tag create [options] Create a release (creates the tag too if it does not exist) $ node src/cli.js release create --help Options: -o, --owner <owner> repository owner -r, --repo <repo> repository name --tag <tag> tag name for the release --target <ref> branch or commit the tag is created from -t, --title <title> release title -b, --body <body> release notes (markdown) --body-file <path> read the release notes from a file --draft create as a draft release --prerelease mark as a prerelease ``` ## Impact A release whose payload is an installer or bundle cannot be mirrored with stoke alone. Mirroring `github.com/heavy-duty/crew` releases `0.1.0` and `0.1.1` — each a single `crew-<version>.sh` of 29 MB / 50 MB — needed stoke for the release object and then a raw API call per asset: ```bash id=$(curl -s -H "Authorization: token $TOKEN" \ "$FORGE/api/v1/repos/heavy-duty/crew/releases/tags/0.1.0" | jq -r .id) curl -s -X POST -H "Authorization: token $TOKEN" \ -F "attachment=@crew-0.1.0.sh" \ "$FORGE/api/v1/repos/heavy-duty/crew/releases/$id/assets?name=crew-0.1.0.sh" ``` Note the extra round-trip: the asset endpoint is keyed by numeric release **id**, which `release create` does not print, so callers must re-query by tag to get it. Printing the id on create would help even if asset upload lands separately. ## Proposed fix 1. Add a repeatable `--asset <path>` to `release create`, uploading each file after the release is created: `POST /api/v1/repos/{owner}/{repo}/releases/{id}/assets?name=<basename>` (multipart field `attachment`). 2. Add `stoke release upload -o … -r … --tag <tag> --asset <path>...` for attaching to an existing release. 3. Optionally `--asset-name <name>` to override the uploaded filename, and include the release `id` in `release create` output. ## Verification ```bash stoke release create -o heavy-duty -r scratch --tag v0.0.1 --target main \ -t v0.0.1 --body-file notes.md --asset ./dist/thing.sh stoke release view -o heavy-duty -r scratch --tag v0.0.1 # asset listed with correct size ``` Related: #23 (no `repo sync`) and the `repo create --owner` gap — all three surfaced while mirroring a GitHub repo onto the forge. --- *Brought up to the issue contract by triage, 2026-08-20. The "Proposed fix" above listed options; the spec below **decides** them — in particular the release id, which epic #27 already folded into this issue's scope ("`release create --asset` + print the release id"). Nothing above was changed. Code references pinned at [`4c61858`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/4c6185898eb64cb018d6bb179ff95132300de5f4).* ## Spec Decisions, not options: - **`release create` gains a repeatable `--asset <path>`.** Each file is uploaded after the release object exists, via `POST /repos/{owner}/{repo}/releases/{id}/assets?name=<basename>`, multipart field `attachment`. - **`release upload -o … -r … --tag <tag> --asset <path>...` is a new subcommand** for attaching to an existing release. It resolves the tag to an id with the `releases/tags/{tag}` lookup [`getReleaseByTag()`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/4c6185898eb64cb018d6bb179ff95132300de5f4/src/api.js#L218) already implements. - **`--asset-name <name>` is in scope, not optional.** It overrides the uploaded filename and is only legal when exactly one `--asset` is given; with two or more it is a usage error, because one name cannot serve many files. - **`release create` prints the numeric release `id`** on success, alongside the tag and URL, so callers never need the re-query round-trip this issue documents. - **Partial-upload behaviour is decided:** the release is created first and is *kept* if an asset upload then fails. The command reports which assets landed and which did not, and exits non-zero. It does not delete the release — silently unwinding a published release is worse than an honest partial state the caller can finish with `release upload`. - **The API client needs a multipart path it does not have.** [`request()`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/4c6185898eb64cb018d6bb179ff95132300de5f4/src/api.js#L55) hardcodes `Content-Type: application/json` and `JSON.stringify`s the body, so it cannot carry a file. Add a distinct upload path that sets no manual `Content-Type` (letting `FormData`/`fetch` set the boundary) and streams the file rather than buffering it — crew's `0.1.0`/`0.1.1` payloads are 29 MB and 50 MB. - **The 30 s timeout does not apply to uploads.** [`REQUEST_TIMEOUT_MS = 30000`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/4c6185898eb64cb018d6bb179ff95132300de5f4/src/api.js#L16) would abort a 50 MB push on any ordinary link; asset uploads take their own, longer timeout. ## Tasks - [ ] Add a multipart/streaming upload path to `src/api.js` with its own timeout, plus an `uploadReleaseAsset(owner, repo, id, path, name)` method - [ ] Add repeatable `--asset` and single-use `--asset-name` to `release create`; print the release `id` on success - [ ] Add the `release upload` subcommand (tag → id resolution, repeatable `--asset`) - [ ] Partial-failure reporting: name what landed and what did not, keep the release, exit non-zero - [ ] Tests: multi-asset create, `release upload` onto an existing tag, `--asset-name` with one asset, `--asset-name` with two assets rejected, upload failure keeps the release and exits non-zero, `Content-Type` is *not* forced to JSON on the upload path - [ ] README: document `--asset`, `--asset-name`, `release upload`, and the printed id - [ ] Open the PR from a **same-repo branch** on `heavy-duty/stoke`, not a fork (fork PRs stall on the CI approval gate — see !28/!29) - [ ] When the PR reaches `state:needs-human`, request `@andres` **by hand** — the engine's own request 404s on this forge and its sweep log reports that failure as a success (#36, defect 1) ## Acceptance criteria - [ ] `release create … --asset a --asset b` creates the release and attaches both; `release view --tag …` lists both with correct sizes - [ ] `release create` prints the numeric release id on success - [ ] `release upload --tag <existing> --asset f` attaches to a release created earlier, without recreating it - [ ] `--asset-name` renames a single asset, and is rejected with a usage error and non-zero exit when two or more `--asset` are given — the case that must fail - [ ] A failing asset upload leaves the release in place, names the assets that did and did not land, and exits non-zero - [ ] A file large enough to exceed the JSON client's 30 s timeout uploads successfully, proving the upload path has its own timeout and does not buffer the whole file ## Test plan Suite-level, against a stubbed HTTP layer: assert the upload request carries a multipart body and **no** hand-set `application/json` content type, that the id-resolution round-trip happens exactly once per `release upload`, and each rejection case above. Manual proof against the live instance: ```bash stoke release create -o heavy-duty -r scratch --tag v0.0.1 --target main \ -t v0.0.1 --body-file notes.md --asset ./dist/thing.sh stoke release view -o heavy-duty -r scratch --tag v0.0.1 # asset listed with correct size ``` ## Dependencies Part of #27 (P2). No blockers. Related: #23 and #24 — all three surfaced while mirroring a GitHub repo onto the forge, and all three touch `src/cli.js`. #32 (release 1.4.0) is **not** gated by this issue and no longer names it as a risk: its "Create release and attach .deb" step attaches with a raw `curl -F attachment=@…`, not with `stoke release create`, so the automated release path never touches the missing asset support (measured 2026-08-20; #32's body was corrected the same day). This gap bites only a hand-finished release.
claude-bot-andresmgsl added the
enhancement
ready
labels 2026-08-18 00:23:52 +00:00
claude-bot-andresmgsl changed title from feature: release create cannot upload assets to release create --asset / release upload — attach assets to a release, and print the release id 2026-08-20 03:33:29 +00:00
Author
Member

Triage: brought up to contract, 2026-08-20. The "Proposed fix" above listed three items, one of them explicitly optional — an issue whose spec still lists options is not ready, whatever the label said. The spec below the rule decides them; nothing above the rule was changed, and the title now names the deliverable.

What got decided:

  • --asset-name and printing the release id are in scope, not optional — epic #27 already folded the id into this issue ("release create --asset + print the release id").
  • --asset-name is legal only with a single --asset; two or more is a usage error.
  • Partial failure keeps the release and exits non-zero, reporting what landed. Unwinding a published release silently is worse than an honest partial state.

Two implementation facts found while specifying, both pinned at 4c61858, because they are the actual work and the issue did not mention either:

  • request() hardcodes Content-Type: application/json and JSON.stringifys the body. There is no multipart path today; one has to be added, and it must stream rather than buffer — the crew payloads that motivated this are 29 MB and 50 MB.
  • REQUEST_TIMEOUT_MS = 30000 would abort a 50 MB upload on any ordinary link, so the upload path takes its own timeout. There is an acceptance criterion for exactly that, so the requirement is falsifiable rather than decorative.

Unrelated but worth recording here: #32 (release 1.4.0) lists this issue as a risk to its own release step. It is not one — release.yml attaches its .deb with a raw curl -F, not with stoke release create. Noted on #32.

**Triage: brought up to contract, 2026-08-20.** The "Proposed fix" above listed three items, one of them explicitly *optional* — an issue whose spec still lists options is not `ready`, whatever the label said. The spec below the rule decides them; nothing above the rule was changed, and the title now names the deliverable. What got decided: - `--asset-name` and printing the release `id` are **in scope**, not optional — epic #27 already folded the id into this issue ("`release create --asset` + print the release id"). - `--asset-name` is legal only with a single `--asset`; two or more is a usage error. - Partial failure keeps the release and exits non-zero, reporting what landed. Unwinding a published release silently is worse than an honest partial state. Two implementation facts found while specifying, both pinned at `4c61858`, because they are the actual work and the issue did not mention either: - [`request()`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/4c6185898eb64cb018d6bb179ff95132300de5f4/src/api.js#L55) hardcodes `Content-Type: application/json` and `JSON.stringify`s the body. There is no multipart path today; one has to be added, and it must stream rather than buffer — the crew payloads that motivated this are 29 MB and 50 MB. - [`REQUEST_TIMEOUT_MS = 30000`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/4c6185898eb64cb018d6bb179ff95132300de5f4/src/api.js#L16) would abort a 50 MB upload on any ordinary link, so the upload path takes its own timeout. There is an acceptance criterion for exactly that, so the requirement is falsifiable rather than decorative. Unrelated but worth recording here: #32 (release 1.4.0) lists this issue as a risk to its own release step. It is not one — `release.yml` attaches its `.deb` with a raw `curl -F`, not with `stoke release create`. Noted on #32.
Author
Member

Triage — Dependencies line corrected, 2026-08-20. No spec, task, criteria, or label change.

This issue's Dependencies said "#32 (release 1.4.0) names this issue as a risk to its own asset step." That described #32 as it read when this line was written; #32's body was corrected on 2026-08-20 and now says the opposite, with the measurement: release.yml's "Create release and attach .deb" step attaches the asset with a raw curl -F attachment=@…, not with stoke release create, so the missing CLI upload support never touches the automated release path. It bites only a hand-finished release.

The conclusion both issues reach is unchanged — this issue does not block #32 — but a builder following the old wording would have gone to #32 looking for a live risk note and found a refutation instead. Both directions of the edge now agree.

**Triage — Dependencies line corrected, 2026-08-20.** No spec, task, criteria, or label change. This issue's Dependencies said "#32 (release 1.4.0) names this issue as a risk to its own asset step." That described #32 as it read when this line was written; #32's body was corrected on 2026-08-20 and now says the opposite, with the measurement: `release.yml`'s "Create release and attach .deb" step attaches the asset with a raw `curl -F attachment=@…`, **not** with `stoke release create`, so the missing CLI upload support never touches the automated release path. It bites only a hand-finished release. The conclusion both issues reach is unchanged — this issue does not block #32 — but a builder following the old wording would have gone to #32 looking for a live risk note and found a refutation instead. Both directions of the edge now agree.
claude-bot-andresmgsl added
blocked
and removed
ready
labels 2026-08-20 13:59:03 +00:00
Author
Member

Triage — collision edge added, readyblocked, 2026-08-20. No spec, task,
acceptance criterion or estimate changed. This is an ordering edge only: nothing here
waits on #24's content, and when #24 closes this issue flips back to ready unchanged.

The finding. #23, #24 and #25 all carried ready from a single batch write on
2026-08-18T00:23:52Z, and none of the three declared a collision edge — so all three were
concurrently claimable. Each body already said "all three touch src/cli.js", but that was
recorded as Related prose and never as a dependency, so it constrained nothing.

src/cli.js is a single 1653-line file in which every command group is chained
(repo at L326, release at L985). #24 edits repo create (L424) and createRepo() in
src/api.js; #25 edits release create (L1057) and adds release upload plus the
asset-upload path in src/api.js; #23 adds a repo sync subcommand inside the same
repo group #24 modifies
and reuses gitAuthEnv(). All three also add a section to
README.md.

The fleet's builders pick ready + unassigned. Two simultaneous claims were therefore a
live possibility, and the second PR to open would have contended with the first over the
same file. TRIAGE.md's collision rule
is written for exactly this: a deliverable already carried by an open ready/claimed/blocked
issue takes an unconditional edge, and disjoint regions inside the shared files are
explicitly not an exemption.

Order chosen: #24 (P1) → #25 (P2) → #23 (P3) — epic #27's own declared priority, not
mint order. Mint order (#23#24#25) would have gated both smaller, higher-priority
issues behind the largest one, inverting the epic. #24 stays ready as the cluster's single
source; each close now releases exactly one successor.

Verified with ceremony's own parser (lib/issue_references.sh + blocked_reference_records)
against the live bodies: this issue parses to {#24}, #23 to {#25}, #24 to {}.
Recorded as finding 3 on epic #27.

**Triage — collision edge added, `ready` → `blocked`, 2026-08-20.** No spec, task, acceptance criterion or estimate changed. This is an **ordering edge only**: nothing here waits on #24's *content*, and when #24 closes this issue flips back to `ready` unchanged. **The finding.** #23, #24 and #25 all carried `ready` from a single batch write on 2026-08-18T00:23:52Z, and none of the three declared a collision edge — so all three were concurrently claimable. Each body already said "all three touch `src/cli.js`", but that was recorded as *Related* prose and never as a dependency, so it constrained nothing. `src/cli.js` is a **single 1653-line file** in which every command group is chained (`repo` at L326, `release` at L985). #24 edits `repo create` (L424) and `createRepo()` in `src/api.js`; #25 edits `release create` (L1057) and adds `release upload` plus the asset-upload path in `src/api.js`; #23 adds a `repo sync` subcommand *inside the same `repo` group #24 modifies* and reuses `gitAuthEnv()`. All three also add a section to `README.md`. The fleet's builders pick `ready` + unassigned. Two simultaneous claims were therefore a live possibility, and the second PR to open would have contended with the first over the same file. [TRIAGE.md's collision rule](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/branch/main/TRIAGE.md#the-issue-contract) is written for exactly this: a deliverable already carried by an open `ready`/`claimed`/`blocked` issue takes an unconditional edge, and disjoint regions inside the shared files are explicitly **not** an exemption. **Order chosen: #24 (P1) → #25 (P2) → #23 (P3)** — epic #27's own declared priority, not mint order. Mint order (#23 → #24 → #25) would have gated both smaller, higher-priority issues behind the largest one, inverting the epic. #24 stays `ready` as the cluster's single source; each close now releases exactly one successor. Verified with ceremony's own parser (`lib/issue_references.sh` + `blocked_reference_records`) against the live bodies: this issue parses to `{#24}`, #23 to `{#25}`, #24 to `{}`. Recorded as finding 3 on epic #27.
claude-bot-andresmgsl added
ready
and removed
blocked
labels 2026-08-20 14:03:46 +00:00
Author
Member

Triage — retracted, 2026-08-20. The collision edge above was wrong; this issue is
ready again and claimable now.
Blocked by #24 has been removed from the body and the
Dependencies section restored verbatim. No spec, task or criterion was ever touched.

Why the edge was wrong. The collision rule
(#288) governs one deliverable carried by two issues, not any two issues that touch one
large file, and its "disjoint regions do not waive it" clause means regions of that shared
deliverable. I read "all three touch src/cli.js" as the collision and it is not one.

The sweep that enforces the rule settles it: deliverable_key (issueflow-reconcile 0.6.1,
L433) keys on the title's em-dash prefix, normalized and case-folded. The three keys are
repo sync (#23), repo create --owner (#24) and release create --asset / release upload
(#25) — three distinct deliverables, so the collision flag would not fire on this trio, and
it would be right not to.

Ordinary same-file contention between distinct deliverables is a rebase — the builder's
normal cost, already carrying its own PR label (blocker:conflict). It is not a board gate,
and making it one would have left two smaller, higher-priority issues unclaimable behind the
largest for no doctrinal reason.

Net effect on this issue: none. It is exactly as it was before my previous comment —
ready, unassigned, no blockers, claimable today. Recorded as finding 3 on epic #27 so the
question is not re-opened from scratch.

**Triage — retracted, 2026-08-20. The collision edge above was wrong; this issue is `ready` again and claimable now.** `Blocked by #24` has been removed from the body and the Dependencies section restored verbatim. No spec, task or criterion was ever touched. **Why the edge was wrong.** [The collision rule](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/branch/main/TRIAGE.md#the-issue-contract) (#288) governs **one deliverable carried by two issues**, not any two issues that touch one large file, and its "disjoint regions do not waive it" clause means regions of that shared deliverable. I read "all three touch `src/cli.js`" as the collision and it is not one. The sweep that enforces the rule settles it: `deliverable_key` (issueflow-reconcile 0.6.1, L433) keys on the **title's em-dash prefix**, normalized and case-folded. The three keys are `repo sync` (#23), `repo create --owner` (#24) and `release create --asset / release upload` (#25) — three distinct deliverables, so the collision flag would not fire on this trio, and it would be right not to. Ordinary same-file contention between distinct deliverables is a rebase — the builder's normal cost, already carrying its own PR label (`blocker:conflict`). It is not a board gate, and making it one would have left two smaller, higher-priority issues unclaimable behind the largest for no doctrinal reason. Net effect on this issue: **none**. It is exactly as it was before my previous comment — `ready`, unassigned, no blockers, claimable today. Recorded as finding 3 on epic #27 so the question is not re-opened from scratch.
claude-bot-andresmgsl added the
scope:cli
label 2026-08-21 06:40:50 +00:00
codex-bot-andresmgsl added
claimed
and removed
ready
labels 2026-08-30 09:40:35 +00:00
codex-bot-andresmgsl self-assigned this 2026-08-30 09:40:35 +00:00

Starting work on #25 as @codex-bot-andresmgsl.

Design / plan of record:

  • Extend ForgejoClient with an upload-only request path that accepts FormData, leaves Content-Type to fetch, streams files from disk, and uses a longer upload timeout without changing JSON requests.
  • Add uploadReleaseAsset(owner, repo, releaseId, path, name); keep release lookup/creation in the existing API methods.
  • Add repeatable --asset plus single-asset --asset-name to release create, print the release id, and account for each upload so partial success is reported honestly while the created release remains.
  • Add release upload using one tag-to-id lookup and the same upload orchestration.
  • Drive each behavior test-first, update README and the issue-scoped changelog fragment, then run the full test/governance suite and live-safe CLI help checks.
  • Open a same-repo draft PR after the first commit and maintain its ## Worklog through signal-then-ready handoff.
Starting work on #25 as @codex-bot-andresmgsl. Design / plan of record: - Extend `ForgejoClient` with an upload-only request path that accepts `FormData`, leaves `Content-Type` to fetch, streams files from disk, and uses a longer upload timeout without changing JSON requests. - Add `uploadReleaseAsset(owner, repo, releaseId, path, name)`; keep release lookup/creation in the existing API methods. - Add repeatable `--asset` plus single-asset `--asset-name` to `release create`, print the release id, and account for each upload so partial success is reported honestly while the created release remains. - Add `release upload` using one tag-to-id lookup and the same upload orchestration. - Drive each behavior test-first, update README and the issue-scoped changelog fragment, then run the full test/governance suite and live-safe CLI help checks. - Open a same-repo draft PR after the first commit and maintain its `## Worklog` through signal-then-ready handoff.
Author
Member

Triage, 2026-08-31T16:39Z — stale claimed released on a closed issue. No other change.

This issue closed at 2026-08-30T13:12:37Z on !37's Closes #25, which closes the issue but never releases the claim: the derived claim→post-merge transition is built only from Refs #N references, and issueflow-reconcile enumerates issues?state=open (L1380), so nothing the machine runs ever looks at a closed issue's labels again. The label has been asserting an active claim ever since.

claimed removed. The assignee stays as build attribution. Nothing is owed on this issue and its close is not disturbed.

**Triage, 2026-08-31T16:39Z — stale `claimed` released on a closed issue. No other change.** This issue closed at **2026-08-30T13:12:37Z** on !37's `Closes #25`, which closes the issue but never releases the claim: the derived claim→`post-merge` transition is built only from `Refs #N` references, and `issueflow-reconcile` enumerates `issues?state=open` (L1380), so nothing the machine runs ever looks at a closed issue's labels again. The label has been asserting an active claim ever since. `claimed` removed. The assignee stays as build attribution. Nothing is owed on this issue and its close is not disturbed.
claude-bot-andresmgsl removed the
claimed
label 2026-08-31 16:39:18 +00:00
Sign in to join this conversation.
No milestone
No project
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: heavy-duty/stoke#25
No description provided.