repo sync — fast-forward-only update of an imported repo from its upstream #23

Closed
opened 2026-07-29 14:08:59 +00:00 by claude-bot-andresmgsl · 9 comments

Context

repo import is one-shot: it creates a repository and cannot advance an existing one. Keeping an imported repo current means hand-splicing the stoke token into a git remote, where it lands in .git/config and shell history (measured while refreshing heavy-duty/ceremony from GitHub; details in the 2026-08-04 comment below). The CLI already hands credentials to git safely: gitAuthEnv(), used by repo clone — a sync path reuses it.

Epic #27 (finding 2) narrowed this spec: a fast-forward-only verb cannot serve a diverged tree (heavy-duty/ceremony carries ~30 local Forgejo-compatibility commits on purpose — evidence in ceremony#197/#198), and no flag should pretend otherwise. The diverged-tree procedure is documented merge work that already exists as ceremony's docs/UPSTREAM-SYNC.md — delivered by ceremony#200, closed 2026-08-05 — not a stoke verb.

Spec

Decisions, not options:

  • New command: stoke repo sync -o <owner> -r <repo> --from <upstream-url> [--branch <branch>] [--tags] [--dry-run]
  • Fast-forward only. If the forge branch is not an ancestor of the upstream branch, exit non-zero naming both SHAs and stating that diverged trees are out of scope (the merge procedure lives in ceremony's docs/UPSTREAM-SYNC.md). There is no --force flag.
  • --branch defaults to the forge repository's default branch.
  • --tags also pushes tags. Existing forge tags are never moved or deleted; an upstream tag that moved is reported, skipped, and makes the exit code non-zero.
  • --dry-run prints exactly what would move (branch old..new, tags to be created) and writes nothing.
  • Credentials go through gitAuthEnv(): the token never appears in argv, in remotes, or in .git/config.
  • Explicitly out of scope (the epic's three-shapes criterion):
    • Diverged trees — refused; the documented merge procedure is ceremony's docs/UPSTREAM-SYNC.md (delivered by ceremony#200).
    • Forgejo pull-mirrors (mirror: true at migration) — a forge-side per-repo setting, not a CLI verb; a repo wanting scheduled read-only mirroring should be imported as a mirror instead.
    • Release mirroring — composable by callers from release create + #25's asset upload; not folded into this verb.

Tasks

  • Implement repo sync in src/cli.js per the spec (fetch upstream, fast-forward check, push via gitAuthEnv())
  • Refusal path: non-ancestor branch → non-zero exit naming both SHAs and the out-of-scope statement
  • --tags (moved-tag report/skip/non-zero), --dry-run, default-branch resolution
  • Tests: ff success, non-ff refusal, moved-tag skip, dry-run writes nothing, token absent from argv/remotes/.git/config
  • README: document the verb and its explicit non-goals
  • 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

  • Syncing an undiverged imported repo advances the branch and (with --tags) tags, with the token visible nowhere in argv, remotes, or .git/config
  • A diverged branch is refused: non-zero exit, error names both SHAs, nothing is pushed
  • A moved upstream tag is skipped with a report and a non-zero exit; the forge tag is untouched
  • --dry-run reports the exact moves and provably writes nothing
  • README states what the verb does not do (diverged trees, pull-mirrors, releases)

Test plan

Test suite drives a local upstream fixture: fast-forward advance; second run is a no-op; non-ff refusal (the case that must fail); moved-tag skip with non-zero exit; --dry-run no-op; credential-leak check over argv and on-disk config. Manual proof: sync a scratch import twice, then add a local commit on the forge copy and confirm the refusal.

Dependencies

Part of #27. No blockers. Related: #24 and #25 (same mirroring workflow); the diverged-tree procedure lives in ceremony's docs/UPSTREAM-SYNC.md, delivered by ceremony#200 (closed). That link is deliberately not pinned to a commit: it is cited as the live procedure a reader is sent to, not as quoted evidence, and the pin it replaced (c2ef6a2, 2026-08-05) had gone two revisions stale — ceremony fdb7d75 (2026-08-24) added the 0.6.2 port record and the upstream-0.6.x vs 0.6.x tag disambiguation, and ceremony#269/7e96748 (2026-08-27) replaced the deferral paragraph with the operator's ruling that the next campaign merges rather than ports, advancing .upstream-ref. Neither loosens this issue's ff-only narrowing — a 488-commit merge campaign is even less a CLI verb than it was — and both were invisible behind the old pin.

## Context `repo import` is one-shot: it creates a repository and cannot advance an existing one. Keeping an imported repo current means hand-splicing the stoke token into a git remote, where it lands in `.git/config` and shell history (measured while refreshing `heavy-duty/ceremony` from GitHub; details in the 2026-08-04 comment below). The CLI already hands credentials to git safely: [`gitAuthEnv()`](https://forgejo.heavyduty.builders/heavy-duty/stoke/src/commit/ee0cb85/src/cli.js#L368), used by `repo clone` — a sync path reuses it. Epic #27 (finding 2) narrowed this spec: a fast-forward-only verb cannot serve a diverged tree (`heavy-duty/ceremony` carries ~30 local Forgejo-compatibility commits on purpose — evidence in ceremony#197/#198), and no flag should pretend otherwise. The diverged-tree procedure is documented merge work that already exists as ceremony's [`docs/UPSTREAM-SYNC.md`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/branch/main/docs/UPSTREAM-SYNC.md) — delivered by ceremony#200, closed 2026-08-05 — not a stoke verb. ## Spec Decisions, not options: - New command: `stoke repo sync -o <owner> -r <repo> --from <upstream-url> [--branch <branch>] [--tags] [--dry-run]` - **Fast-forward only.** If the forge branch is not an ancestor of the upstream branch, exit non-zero naming both SHAs and stating that diverged trees are out of scope (the merge procedure lives in ceremony's `docs/UPSTREAM-SYNC.md`). There is no `--force` flag. - `--branch` defaults to the forge repository's default branch. - `--tags` also pushes tags. Existing forge tags are never moved or deleted; an upstream tag that moved is reported, skipped, and makes the exit code non-zero. - `--dry-run` prints exactly what would move (branch `old..new`, tags to be created) and writes nothing. - Credentials go through `gitAuthEnv()`: the token never appears in argv, in remotes, or in `.git/config`. - **Explicitly out of scope** (the epic's three-shapes criterion): - *Diverged trees* — refused; the documented merge procedure is ceremony's `docs/UPSTREAM-SYNC.md` (delivered by ceremony#200). - *Forgejo pull-mirrors* (`mirror: true` at migration) — a forge-side per-repo setting, not a CLI verb; a repo wanting scheduled read-only mirroring should be imported as a mirror instead. - *Release mirroring* — composable by callers from `release create` + #25's asset upload; not folded into this verb. ## Tasks - [ ] Implement `repo sync` in `src/cli.js` per the spec (fetch upstream, fast-forward check, push via `gitAuthEnv()`) - [ ] Refusal path: non-ancestor branch → non-zero exit naming both SHAs and the out-of-scope statement - [ ] `--tags` (moved-tag report/skip/non-zero), `--dry-run`, default-branch resolution - [ ] Tests: ff success, non-ff refusal, moved-tag skip, dry-run writes nothing, token absent from argv/remotes/`.git/config` - [ ] README: document the verb and its explicit non-goals - [ ] 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 - [ ] Syncing an undiverged imported repo advances the branch and (with `--tags`) tags, with the token visible nowhere in argv, remotes, or `.git/config` - [ ] A diverged branch is refused: non-zero exit, error names both SHAs, nothing is pushed - [ ] A moved upstream tag is skipped with a report and a non-zero exit; the forge tag is untouched - [ ] `--dry-run` reports the exact moves and provably writes nothing - [ ] README states what the verb does not do (diverged trees, pull-mirrors, releases) ## Test plan Test suite drives a local upstream fixture: fast-forward advance; second run is a no-op; non-ff refusal (the case that must fail); moved-tag skip with non-zero exit; `--dry-run` no-op; credential-leak check over argv and on-disk config. Manual proof: sync a scratch import twice, then add a local commit on the forge copy and confirm the refusal. ## Dependencies Part of #27. No blockers. Related: #24 and #25 (same mirroring workflow); the diverged-tree procedure lives in ceremony's [`docs/UPSTREAM-SYNC.md`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/branch/main/docs/UPSTREAM-SYNC.md), delivered by ceremony#200 (closed). That link is deliberately **not** pinned to a commit: it is cited as the live procedure a reader is sent to, not as quoted evidence, and the pin it replaced (`c2ef6a2`, 2026-08-05) had gone two revisions stale — ceremony `fdb7d75` (2026-08-24) added the 0.6.2 port record and the `upstream-0.6.x` vs `0.6.x` tag disambiguation, and ceremony#269/`7e96748` (2026-08-27) replaced the deferral paragraph with the operator's ruling that the **next** campaign merges rather than ports, advancing `.upstream-ref`. Neither loosens this issue's ff-only narrowing — a 488-commit merge campaign is even less a CLI verb than it was — and both were invisible behind the old pin.
Author
Member

Still present in the apt build (1.2.0) and on main (1.3.0) as of 2026-08-04 — re-confirmed while mirroring github.com/heavy-duty/crew onto the forge. stoke repo --help lists list / create / import / rename / import-batch / transfer; nothing advances an existing repo.

The full manual fallback still costs a hand-injected credential:

git clone --mirror https://github.com/heavy-duty/crew.git
git push "$AUTH_URL" refs/heads/main:refs/heads/main --tags

Two adjacent gaps surfaced in the same task and are filed separately: #24 (repo create cannot target an org) and #25 (release create cannot upload assets). A repo sync that covered branches, tags and release objects would close all three for the mirroring use case.

Still present in the apt build (`1.2.0`) and on `main` (`1.3.0`) as of 2026-08-04 — re-confirmed while mirroring `github.com/heavy-duty/crew` onto the forge. `stoke repo --help` lists `list / create / import / rename / import-batch / transfer`; nothing advances an existing repo. The full manual fallback still costs a hand-injected credential: ```bash git clone --mirror https://github.com/heavy-duty/crew.git git push "$AUTH_URL" refs/heads/main:refs/heads/main --tags ``` Two adjacent gaps surfaced in the same task and are filed separately: #24 (`repo create` cannot target an org) and #25 (`release create` cannot upload assets). A `repo sync` that covered branches, tags **and** release objects would close all three for the mirroring use case.
claude-bot-andresmgsl changed title from feature: no way to update an already-imported repo — add repo sync to repo sync — fast-forward-only update of an imported repo from its upstream 2026-08-18 00:22:45 +00:00
Author
Member

Triage: title and body rewritten per epic #27, finding 2 — the verb is narrowed to the undiverged-mirror case. Decisions now in the body: fast-forward only with no --force; diverged trees refused by name (merge procedure belongs to ceremony#200); pull-mirrors and release mirroring explicitly out of scope. The original body's --releases flag is dropped — release mirroring composes from release create + #25 instead of growing this verb. Spec is complete; labeling ready.

Triage: title and body rewritten per epic #27, finding 2 — the verb is narrowed to the undiverged-mirror case. Decisions now in the body: fast-forward only with no `--force`; diverged trees refused by name (merge procedure belongs to ceremony#200); pull-mirrors and release mirroring explicitly out of scope. The original body's `--releases` flag is dropped — release mirroring composes from `release create` + #25 instead of growing this verb. Spec is complete; labeling `ready`.
claude-bot-andresmgsl added the
enhancement
ready
labels 2026-08-18 00:23:52 +00:00
Author
Member

Triage — cross-repo pointer repaired, 2026-08-20.

This issue told a builder four times that the diverged-tree procedure "lives in"
/ "is owned by" ceremony#200. Measured this tick: ceremony#200 closed
2026-08-05T19:12:40Z
— it closed thirteen days before the 2026-08-18 spec
revision that wrote those pointers, so following one has never landed anywhere
but a closed issue.

What it delivered is real and readable:
docs/UPSTREAM-SYNC.md
on ceremony main (c2ef6a2), verified present this tick — the procedure plus a
table of standing resolutions (VERSION, CEREMONY_SELF_REF, .github/labels.conf,
drills/*, CHANGELOG.md) that each sync would otherwise re-decide. It also
confirms, in its own words, that the two trees "diverge in opposite directions on
purpose" — so this issue's reason for refusing diverged trees is unchanged and
still true.

Body edited: Context, the fast-forward refusal bullet, the Explicitly out of
scope
bullet, and Dependencies now name that doc (crediting ceremony#200 as
what delivered it) instead of pointing at the closed issue. Nothing about the
spec, tasks, criteria or scope changed — only where the builder is sent to read,
which matters most for the refusal path, whose error text is meant to tell a
user where the merge procedure is.

Labels re-read before writing (ready + enhancement, set 2026-08-18T00:23:52Z,
no label event since): unchanged and correct. Still a real gap — main is
unmoved at 4c618589 and src/cli.js has no sync subcommand.

**Triage — cross-repo pointer repaired, 2026-08-20.** This issue told a builder four times that the diverged-tree procedure "lives in" / "is owned by" **ceremony#200**. Measured this tick: ceremony#200 **closed 2026-08-05T19:12:40Z** — it closed *thirteen days before* the 2026-08-18 spec revision that wrote those pointers, so following one has never landed anywhere but a closed issue. What it delivered is real and readable: [`docs/UPSTREAM-SYNC.md`](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/commit/c2ef6a2/docs/UPSTREAM-SYNC.md) on ceremony `main` (`c2ef6a2`), verified present this tick — the procedure plus a table of standing resolutions (`VERSION`, `CEREMONY_SELF_REF`, `.github/labels.conf`, `drills/*`, `CHANGELOG.md`) that each sync would otherwise re-decide. It also confirms, in its own words, that the two trees "diverge in opposite directions on purpose" — so this issue's *reason* for refusing diverged trees is unchanged and still true. Body edited: Context, the fast-forward refusal bullet, the **Explicitly out of scope** bullet, and Dependencies now name that doc (crediting ceremony#200 as what delivered it) instead of pointing at the closed issue. Nothing about the spec, tasks, criteria or scope changed — only where the builder is sent to read, which matters most for the refusal path, whose error text is meant to tell a user where the merge procedure is. Labels re-read before writing (`ready` + `enhancement`, set 2026-08-18T00:23:52Z, no label event since): unchanged and correct. Still a real gap — `main` is unmoved at `4c618589` and `src/cli.js` has no `sync` subcommand.
claude-bot-andresmgsl added
blocked
and removed
ready
labels 2026-08-20 13:59:18 +00:00
Author
Member

Triage — collision edge added, readyblocked, 2026-08-20. No spec, task,
acceptance criterion or estimate changed, and the narrowed scope from finding 2 is
untouched. This is an ordering edge only: nothing here waits on #25's content — the
"composable from #25's asset upload" line under out of scope is an observation about
callers, not a build order — and when #25 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 declared a collision edge, so all three were concurrently
claimable. Each body already said "all three touch src/cli.js", but only as Related
prose, which constrains 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.

This issue is the tightest case of the three: it inserts a subcommand into the same repo
group #24 is editing. Under TRIAGE.md's collision rule,
disjoint regions inside a shared file are explicitly not an exemption.

Order chosen: #24 (P1) → #25 (P2) → #23 (P3) — epic #27's own declared priority. This
issue is last because the epic ranks it P3 and largest, not because it is worth less;
it stays fully specced and buildable the moment #25 closes. Recorded as finding 3 on #27.

**Triage — collision edge added, `ready` → `blocked`, 2026-08-20.** No spec, task, acceptance criterion or estimate changed, and the narrowed scope from finding 2 is untouched. This is an **ordering edge only**: nothing here waits on #25's *content* — the "composable from #25's asset upload" line under *out of scope* is an observation about callers, not a build order — and when #25 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 declared a collision edge, so all three were concurrently claimable. Each body already said "all three touch `src/cli.js`", but only as *Related* prose, which constrains 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`. This issue is the tightest case of the three: it inserts a subcommand into the same `repo` group #24 is editing. Under [TRIAGE.md's collision rule](https://forgejo.heavyduty.builders/heavy-duty/ceremony/src/branch/main/TRIAGE.md#the-issue-contract), disjoint regions inside a shared file are explicitly not an exemption. **Order chosen: #24 (P1) → #25 (P2) → #23 (P3)** — epic #27's own declared priority. This issue is last because the epic ranks it P3 and largest, **not** because it is worth less; it stays fully specced and buildable the moment #25 closes. Recorded as finding 3 on #27.
claude-bot-andresmgsl added
ready
and removed
blocked
labels 2026-08-20 14:03:47 +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 #25 has been removed from the body and the
Dependencies section restored verbatim. The finding-2 scope narrowing was never 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. ready, unassigned, no blockers, claimable today —
including concurrently with #24 and #25. Recorded as finding 3 on epic #27.

**Triage — retracted, 2026-08-20. The collision edge above was wrong; this issue is `ready` again and claimable now.** `Blocked by #25` has been removed from the body and the Dependencies section restored verbatim. The finding-2 scope narrowing was never 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**. `ready`, unassigned, no blockers, claimable today — including concurrently with #24 and #25. Recorded as finding 3 on epic #27.
claude-bot-andresmgsl added the
scope:cli
label 2026-08-21 06:40:49 +00:00
Author
Member

Triage — re-pinned the docs/UPSTREAM-SYNC.md citations from c2ef6a2 to branch/main; no label, no scope, no criterion changed. Same edit on #27, whose Dependencies carried the third copy of the link and now explains it inline.

This issue sends a reader to that doc twice — in the Context rescope and in the refusal path's out-of-scope statement — as the live procedure, not as quoted evidence. A commit pin is right for evidence and wrong for that: the pinned revision was 2026-08-05 and the doc has moved twice since.

  • fdb7d75, 2026-08-24 — adds the 0.6.2 port record: the content baseline is upstream-0.6.3, ported rather than merged, so .upstream-ref stayed at 8c3a4d1, and tags are disambiguated upstream-0.6.x (upstream's line) vs bare 0.6.x (cut on this forge).
  • 7e96748 (ceremony#269), 2026-08-27 — replaces the deferral paragraph with the operator's ruling that the next campaign merges rather than ports, and so advances the ancestry baseline. The sentence it replaced froze upstream 0.7.4 as a ceiling; ceremony's own changelog records that it "expired before it was written".

Neither touches this issue's spec, and I checked that before editing rather than after. The ff-only narrowing (epic #27, finding 2) rests on diverged trees being documented merge work rather than a CLI verb, and the correction sizes that campaign at 92 files, +25,121/−971 over 488 commits — further from a verb, not closer. The refusal path, the --tags and --dry-run shapes, and the three out-of-scope items all stand as written.

Worth naming because it is a rot direction, not just a stale link: on 2026-08-21 triage verified this exact pin byte-identical to ceremony main and recorded a re-fire condition — re-check when ceremony#229/#231 land. Both are irrelevant to what actually moved the file; the change came from #229's sibling fdb7d75 and from #269, neither of them named. A conditional that names the issues expected to invalidate a fact only fires on the futures you thought of. The branch link removes the condition entirely — the citation cannot freeze again.

(One caveat kept honest: the branch route is right here because both links are prose citations with no line anchors. Anything quoting line numbers still gets a commit pin — see #36, which pins 0.6.1 and 0.6.3 deliberately.)

**Triage — re-pinned the `docs/UPSTREAM-SYNC.md` citations from `c2ef6a2` to `branch/main`; no label, no scope, no criterion changed.** Same edit on #27, whose Dependencies carried the third copy of the link and now explains it inline. This issue sends a reader to that doc twice — in the Context rescope and in the refusal path's out-of-scope statement — as **the live procedure**, not as quoted evidence. A commit pin is right for evidence and wrong for that: the pinned revision was 2026-08-05 and the doc has moved twice since. - `fdb7d75`, 2026-08-24 — adds the **0.6.2 port record**: the content baseline is `upstream-0.6.3`, ported rather than merged, so `.upstream-ref` stayed at `8c3a4d1`, and tags are disambiguated `upstream-0.6.x` (upstream's line) vs bare `0.6.x` (cut on this forge). - `7e96748` (ceremony#269), 2026-08-27 — replaces the deferral paragraph with the **operator's ruling that the next campaign merges rather than ports**, and so advances the ancestry baseline. The sentence it replaced froze upstream `0.7.4` as a ceiling; ceremony's own changelog records that it "expired before it was written". **Neither touches this issue's spec, and I checked that before editing rather than after.** The ff-only narrowing (epic #27, finding 2) rests on diverged trees being documented *merge* work rather than a CLI verb, and the correction sizes that campaign at 92 files, +25,121/−971 over 488 commits — further from a verb, not closer. The refusal path, the `--tags` and `--dry-run` shapes, and the three out-of-scope items all stand as written. Worth naming because it is a rot direction, not just a stale link: on 2026-08-21 triage verified this exact pin byte-identical to ceremony `main` and recorded a re-fire condition — *re-check when ceremony#229/#231 land*. Both are irrelevant to what actually moved the file; the change came from #229's sibling `fdb7d75` and from #269, neither of them named. **A conditional that names the issues expected to invalidate a fact only fires on the futures you thought of.** The branch link removes the condition entirely — the citation cannot freeze again. (One caveat kept honest: the branch route is right *here* because both links are prose citations with no line anchors. Anything quoting line numbers still gets a commit pin — see #36, which pins `0.6.1` and `0.6.3` deliberately.)
codex-bot-andresmgsl added
claimed
and removed
ready
labels 2026-08-31 16:46:29 +00:00
codex-bot-andresmgsl self-assigned this 2026-08-31 16:46:29 +00:00

Starting #23. I’ll add the fast-forward-only verb on a same-repo worktree.\n\nDesign / plan of record:\n- use an ephemeral bare Git directory; never persist either remote or the token;\n- resolve an omitted branch from the forge repository’s symbolic ;\n- fetch forge/upstream branch tips into separate temporary refs and refuse non-ancestor updates with both SHAs and the documented diverged-tree boundary;\n- compare upstream and forge tags, omit moved tags, include new tags only, and return non-zero when any moved tag is found;\n- make print the exact branch/tag ref movements without pushing;\n- drive the behavior with local bare-repository tests first, including a Git argv recorder and on-disk config/token leak checks;\n- document the verb, its three explicit non-goals, and add the required changelog fragment.\n\nI’ll checkpoint each completed worklog item in the draft PR body and push at least every 15 minutes while active.

Starting #23. I’ll add the fast-forward-only verb on a same-repo worktree.\n\nDesign / plan of record:\n- use an ephemeral bare Git directory; never persist either remote or the token;\n- resolve an omitted branch from the forge repository’s symbolic ;\n- fetch forge/upstream branch tips into separate temporary refs and refuse non-ancestor updates with both SHAs and the documented diverged-tree boundary;\n- compare upstream and forge tags, omit moved tags, include new tags only, and return non-zero when any moved tag is found;\n- make print the exact branch/tag ref movements without pushing;\n- drive the behavior with local bare-repository tests first, including a Git argv recorder and on-disk config/token leak checks;\n- document the verb, its three explicit non-goals, and add the required changelog fragment.\n\nI’ll checkpoint each completed worklog item in the draft PR body and push at least every 15 minutes while active.

Starting #23. I’ll add the fast-forward-only repo sync verb on a same-repo worktree.

Design / plan of record:

  • use an ephemeral bare Git directory; never persist either remote or the token;
  • resolve an omitted branch from the forge repository’s symbolic HEAD;
  • fetch forge/upstream branch tips into separate temporary refs and refuse non-ancestor updates with both SHAs and the documented diverged-tree boundary;
  • compare upstream and forge tags, omit moved tags, include new tags only, and return non-zero when any moved tag is found;
  • make --dry-run print the exact branch/tag ref movements without pushing;
  • drive the behavior with local bare-repository tests first, including a Git argv recorder and on-disk config/token leak checks;
  • document the verb, its three explicit non-goals, and add the required changelog fragment.

Correction: the immediately preceding comment’s formatting was damaged in transport; this is the plan of record.

I’ll checkpoint each completed worklog item in the draft PR body and push at least every 15 minutes while active.

Starting #23. I’ll add the fast-forward-only `repo sync` verb on a same-repo worktree. Design / plan of record: - use an ephemeral bare Git directory; never persist either remote or the token; - resolve an omitted branch from the forge repository’s symbolic `HEAD`; - fetch forge/upstream branch tips into separate temporary refs and refuse non-ancestor updates with both SHAs and the documented diverged-tree boundary; - compare upstream and forge tags, omit moved tags, include new tags only, and return non-zero when any moved tag is found; - make `--dry-run` print the exact branch/tag ref movements without pushing; - drive the behavior with local bare-repository tests first, including a Git argv recorder and on-disk config/token leak checks; - document the verb, its three explicit non-goals, and add the required changelog fragment. Correction: the immediately preceding comment’s formatting was damaged in transport; this is the plan of record. I’ll checkpoint each completed worklog item in the draft PR body and push at least every 15 minutes while active.
Author
Member

Triage, 2026-08-31T18:45Z — transition record for the Closes #23 merge, and the claim released.

!41 merged 18:42:40Z by @andres (merge commit c34a8b04d23480ce5767e2e72ef398e913d651ee, head 316bec5855b3d4f8f003df6f580b3d5cb4a34e38), and the Closes #23 in its body auto-closed this issue the same second. The engine builds derived transitions only from Refs references and enumerates issues?state=open, so no claimedpost-merge move ran and no transition comment was written — on a close that skips the derived move, triage writes the record in the same tick. That is this comment. claimed is now removed; the assignee stays as build attribution, matching the #26/#24/#25/#1 precedent.

The Closes was correct, not a slip: all five acceptance criteria here are pre-merge and reviewable on the PR, so the Refs rule never bound and nothing is stranded in post-merge.

What the merge proves — re-measured by triage at c34a8b04, not read off the PR

Criterion Measured
repo sync implemented src/cli.js:456 .command('sync'); new module src/repo-sync.js (177 lines)
Fast-forward / divergence refusal, tags, dry-run present in src/cli.js + src/repo-sync.js; --dry-run/--tags both wired
Tests test/sync.test.js11 integration tests
README documents the verb and its non-goals README.md:296 describes it; README.md:317 states it "deliberately does not merge diverged trees, configure Forgejo pull-mirrors, or copy releases" — the explicit non-goals this issue demanded
Same-repo branch, not a fork build/23-repo-sync on heavy-duty/stoke; no CI approval gate hit
Token absent from argv / remotes / .git/config src/repo-sync.js never builds a credentialed URL (no interpolated token found); it receives env and passes it to spawnSync('git', …), so authentication is environment-only. All three panelists verified this path independently at the merged head

Panel at the merged head, none stale: @glm-bot-andresmgsl 18:29:36Z, @claude-bot-andresmgsl 18:32:06Z, @kimi-bot-andresmgsl 18:32:14Z — all APPROVED at 316bec58, which is exactly the commit that merged. ci / test and labels / labels green at that head.

One task was NOT exercised — recorded, not quietly ticked

Task "When the PR reaches state:needs-human, request @andres by hand — the engine's own request 404s on this forge" never happened. !41 reached state:needs-human at 18:34:13Z and @andres merged it 8 minutes later at 18:42:40Z with no hand request recorded; the only review_request events on !41 are the three bots, requested by the builder at 18:22:26–27Z. Overtaken by events rather than skipped — but it is not evidence the hand-request step works, and #36 defect 1 fired live in that exact window (sweep runs 605/607 both logged HTTP 404 from 'POST repos/heavy-duty/stoke/pulls/41/requested_reviewers'). Same shape as #32's Task 2, left unticked for the same reason.

No body boxes were ticked here. This issue is closed and the merge is fully evidenced above; ticking twelve boxes on a closed issue adds nothing the record does not already carry, and the one that would have been wrong is named explicitly.

— triage (@claude-bot-andresmgsl)

**Triage, 2026-08-31T18:45Z — transition record for the `Closes #23` merge, and the claim released.** !41 merged **18:42:40Z by @andres** (merge commit `c34a8b04d23480ce5767e2e72ef398e913d651ee`, head `316bec5855b3d4f8f003df6f580b3d5cb4a34e38`), and the `Closes #23` in its body auto-closed this issue the same second. The engine builds derived transitions only from `Refs` references and enumerates `issues?state=open`, so **no `claimed` → `post-merge` move ran and no transition comment was written** — on a close that skips the derived move, triage writes the record in the same tick. That is this comment. **`claimed` is now removed**; the assignee stays as build attribution, matching the #26/#24/#25/#1 precedent. The `Closes` was **correct**, not a slip: all five acceptance criteria here are pre-merge and reviewable on the PR, so the `Refs` rule never bound and nothing is stranded in `post-merge`. ## What the merge proves — re-measured by triage at `c34a8b04`, not read off the PR | Criterion | Measured | |---|---| | `repo sync` implemented | `src/cli.js:456` `.command('sync')`; new module `src/repo-sync.js` (177 lines) | | Fast-forward / divergence refusal, tags, dry-run | present in `src/cli.js` + `src/repo-sync.js`; `--dry-run`/`--tags` both wired | | Tests | `test/sync.test.js` — **11** integration tests | | README documents the verb **and its non-goals** | `README.md:296` describes it; **`README.md:317`** states it *"deliberately does not merge diverged trees, configure Forgejo pull-mirrors, or copy releases"* — the explicit non-goals this issue demanded | | Same-repo branch, not a fork | `build/23-repo-sync` on `heavy-duty/stoke`; no CI approval gate hit | | Token absent from argv / remotes / `.git/config` | `src/repo-sync.js` never builds a credentialed URL (no interpolated token found); it receives `env` and passes it to `spawnSync('git', …)`, so authentication is environment-only. All three panelists verified this path independently at the merged head | Panel at the merged head, none stale: @glm-bot-andresmgsl 18:29:36Z, @claude-bot-andresmgsl 18:32:06Z, @kimi-bot-andresmgsl 18:32:14Z — all **APPROVED** at `316bec58`, which is exactly the commit that merged. `ci / test` and `labels / labels` green at that head. ## One task was NOT exercised — recorded, not quietly ticked Task *"When the PR reaches `state:needs-human`, request `@andres` **by hand** — the engine's own request 404s on this forge"* **never happened.** !41 reached `state:needs-human` at 18:34:13Z and @andres merged it 8 minutes later at 18:42:40Z with no hand request recorded; the only `review_request` events on !41 are the three bots, requested by the builder at 18:22:26–27Z. Overtaken by events rather than skipped — but it is not evidence the hand-request step works, and #36 defect 1 fired live in that exact window (sweep runs 605/607 both logged `HTTP 404 from 'POST repos/heavy-duty/stoke/pulls/41/requested_reviewers'`). Same shape as #32's Task 2, left unticked for the same reason. **No body boxes were ticked here.** This issue is closed and the merge is fully evidenced above; ticking twelve boxes on a closed issue adds nothing the record does not already carry, and the one that would have been wrong is named explicitly. — triage (@claude-bot-andresmgsl)
claude-bot-andresmgsl removed the
claimed
label 2026-08-31 18:45:25 +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#23
No description provided.