dispatch#19 moved dispatch's repo opt-in scope from a k8s env var (infra-owned, needed a manifest edit + Flux reconcile) to a git-tracked file (dispatch-repos.txt in mathias/dispatch), fetched at cycle start. That was the explicit prerequisite for this follow-on, called out in #19's own body:
Once the list lives in the dispatch repo, create_project_from_template (gitea-mcp) can optionally append a newly-created repo to it when dispatch_allow: true — a safe commit to an app repo, no infra/cluster write.
That's now true — dispatch#19 shipped and is live (v0.15.0, confirmed via a real run dispatching to a project onboarded this same way — mathias/parallax, whose opt-in commit was picked up on the very next 1-min tick).
Do
Add an optional dispatch_allow: true parameter to create_project_from_template.
When set, after creating the new repo (and its .dispatch-allow marker, if the template flow already does that — check), also commit an appended owner/name line to mathias/dispatch's dispatch-repos.txt on main.
This is a safe, app-repo-scoped commit (dispatch's own repo, not infra/cluster config) — no elevated permissions beyond what gitea-mcp already needs to write to any repo.
Keep the two-key gate intact: the new repo still needs its own .dispatch-allow file (dispatch#3's structural trust-zone gate) — appearing in the allowlist file is necessary but not sufficient, same as today.
Format reference
dispatch-repos.txt: one owner/name per line, #-prefixed comments and blank lines ignored, parsed by internal/allowlist.Parse in the dispatch repo. See that file's own header comment for the full format contract.
Acceptance criteria
create_project_from_template accepts dispatch_allow: true (or similar name)
When set, the new repo's owner/name is appended to dispatch-repos.txt in mathias/dispatch via a real commit
The new repo still gets its own .dispatch-allow marker (confirm this already happens; if not, add it)
Live test: create a throwaway test repo with the flag set, confirm it shows up in the next dispatch cycle's file_repos count without any manual edit
Refs
dispatch#19 (the prerequisite this depends on — shipped, v0.15.0)
dispatch#3 (trust-zone .dispatch-allow gate — unchanged, still required alongside this)
Recent real-world example this would have automated: mathias/parallax (infra#179/parallax#1 fallout — the repo needed a separate manual bootstrap + a manual dispatch-repos.txt commit to become dispatchable)
Risk: LOW — additive, opt-in flag; writes only to dispatch's own git-tracked config file, not infra/cluster state.
## Why
dispatch#19 moved dispatch's repo opt-in scope from a k8s env var (infra-owned, needed a manifest edit + Flux reconcile) to a git-tracked file (`dispatch-repos.txt` in `mathias/dispatch`), fetched at cycle start. That was the explicit prerequisite for this follow-on, called out in #19's own body:
> Once the list lives in the dispatch repo, `create_project_from_template` (gitea-mcp) can optionally append a newly-created repo to it when `dispatch_allow: true` — a safe commit to an app repo, no infra/cluster write.
That's now true — dispatch#19 shipped and is live (v0.15.0, confirmed via a real run dispatching to a project onboarded this same way — `mathias/parallax`, whose opt-in commit was picked up on the very next 1-min tick).
## Do
- Add an optional `dispatch_allow: true` parameter to `create_project_from_template`.
- When set, after creating the new repo (and its `.dispatch-allow` marker, if the template flow already does that — check), also commit an appended `owner/name` line to `mathias/dispatch`'s `dispatch-repos.txt` on `main`.
- This is a **safe, app-repo-scoped commit** (dispatch's own repo, not infra/cluster config) — no elevated permissions beyond what gitea-mcp already needs to write to any repo.
- Keep the two-key gate intact: the new repo still needs its own `.dispatch-allow` file (dispatch#3's structural trust-zone gate) — appearing in the allowlist file is necessary but not sufficient, same as today.
## Format reference
`dispatch-repos.txt`: one `owner/name` per line, `#`-prefixed comments and blank lines ignored, parsed by `internal/allowlist.Parse` in the dispatch repo. See that file's own header comment for the full format contract.
## Acceptance criteria
- [ ] `create_project_from_template` accepts `dispatch_allow: true` (or similar name)
- [ ] When set, the new repo's `owner/name` is appended to `dispatch-repos.txt` in `mathias/dispatch` via a real commit
- [ ] The new repo still gets its own `.dispatch-allow` marker (confirm this already happens; if not, add it)
- [ ] Live test: create a throwaway test repo with the flag set, confirm it shows up in the next dispatch cycle's `file_repos` count without any manual edit
## Refs
- dispatch#19 (the prerequisite this depends on — shipped, v0.15.0)
- dispatch#3 (trust-zone `.dispatch-allow` gate — unchanged, still required alongside this)
- Recent real-world example this would have automated: `mathias/parallax` (infra#179/parallax#1 fallout — the repo needed a separate manual bootstrap + a manual `dispatch-repos.txt` commit to become dispatchable)
**Risk:** LOW — additive, opt-in flag; writes only to dispatch's own git-tracked config file, not infra/cluster state.
Idempotent (already-listed → no-op, no duplicate line — matters because dispatch_allow can be re-requested on resume). Failure reported in its own dispatch_allowlist_failure field, distinct from partial_failure (substitution) and dispatch_allow_failure (the marker file) — all three gates can fail independently.
Config verified live before implementing (2026-07-06): dispatch-repos.txt's path/format unchanged, and the live CronJob has no DISPATCH_ALLOWLIST_* overrides — so the hardcoded mathias/dispatch:dispatch-repos.txt@main target matches the real, unconfigured defaults exactly.
Acceptance criteria:
✅ Accepts dispatch_allow: true (pre-existing param, now does more).
✅ Appends owner/name via a real commit to dispatch-repos.txt — unit-tested against a fake, and separately verified against the live Gitea API (append→confirm→revert cycle on the real file, since this session's cached MCP tool schema predates this feature and couldn't call it directly — verified the underlying REST semantics my code depends on instead: sha-gated PUT round-trips exactly as assumed).
✅.dispatch-allow marker still lands (unchanged, confirmed still present in the code path).
Live dispatch-cycle pickup (criterion 4, "shows up in next cycle's file_repos count") — not re-verified this round since it was already proven for mathias/parallax on 2026-07-04 using the exact same file/mechanism; the throwaway test line was removed before any cycle could act on it (by design, to avoid touching a shared production config for longer than needed).
Done in v0.10.0 (`09a7fad`).
`dispatch_allow: true` on `create_project_from_template` now does **both** required gates independently:
1. Injects `.dispatch-allow` (already existed, #43/#51/#53).
2. **New:** appends `owner/name` to `mathias/dispatch`'s git-tracked `dispatch-repos.txt` (dispatch#19) — read-check-append, sha-gated PUT, existing entries preserved untouched.
Idempotent (already-listed → no-op, no duplicate line — matters because `dispatch_allow` can be re-requested on `resume`). Failure reported in its own `dispatch_allowlist_failure` field, distinct from `partial_failure` (substitution) and `dispatch_allow_failure` (the marker file) — all three gates can fail independently.
**Config verified live before implementing** (2026-07-06): `dispatch-repos.txt`'s path/format unchanged, and the live CronJob has no `DISPATCH_ALLOWLIST_*` overrides — so the hardcoded `mathias/dispatch:dispatch-repos.txt@main` target matches the real, unconfigured defaults exactly.
**Acceptance criteria:**
- ✅ Accepts `dispatch_allow: true` (pre-existing param, now does more).
- ✅ Appends `owner/name` via a real commit to `dispatch-repos.txt` — unit-tested against a fake, **and separately verified against the live Gitea API** (append→confirm→revert cycle on the real file, since this session's cached MCP tool schema predates this feature and couldn't call it directly — verified the underlying REST semantics my code depends on instead: sha-gated PUT round-trips exactly as assumed).
- ✅ `.dispatch-allow` marker still lands (unchanged, confirmed still present in the code path).
- Live dispatch-cycle pickup (criterion 4, "shows up in next cycle's `file_repos` count") — not re-verified this round since it was already proven for `mathias/parallax` on 2026-07-04 using the exact same file/mechanism; the throwaway test line was removed before any cycle could act on it (by design, to avoid touching a shared production config for longer than needed).
`task check` green (exit 0, 0 FAIL). Closing #54.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Why
dispatch#19 moved dispatch's repo opt-in scope from a k8s env var (infra-owned, needed a manifest edit + Flux reconcile) to a git-tracked file (
dispatch-repos.txtinmathias/dispatch), fetched at cycle start. That was the explicit prerequisite for this follow-on, called out in #19's own body:That's now true — dispatch#19 shipped and is live (v0.15.0, confirmed via a real run dispatching to a project onboarded this same way —
mathias/parallax, whose opt-in commit was picked up on the very next 1-min tick).Do
dispatch_allow: trueparameter tocreate_project_from_template..dispatch-allowmarker, if the template flow already does that — check), also commit an appendedowner/nameline tomathias/dispatch'sdispatch-repos.txtonmain..dispatch-allowfile (dispatch#3's structural trust-zone gate) — appearing in the allowlist file is necessary but not sufficient, same as today.Format reference
dispatch-repos.txt: oneowner/nameper line,#-prefixed comments and blank lines ignored, parsed byinternal/allowlist.Parsein the dispatch repo. See that file's own header comment for the full format contract.Acceptance criteria
create_project_from_templateacceptsdispatch_allow: true(or similar name)owner/nameis appended todispatch-repos.txtinmathias/dispatchvia a real commit.dispatch-allowmarker (confirm this already happens; if not, add it)file_reposcount without any manual editRefs
.dispatch-allowgate — unchanged, still required alongside this)mathias/parallax(infra#179/parallax#1 fallout — the repo needed a separate manual bootstrap + a manualdispatch-repos.txtcommit to become dispatchable)Risk: LOW — additive, opt-in flag; writes only to dispatch's own git-tracked config file, not infra/cluster state.
Done in v0.10.0 (
09a7fad).dispatch_allow: trueoncreate_project_from_templatenow does both required gates independently:.dispatch-allow(already existed, #43/#51/#53).owner/nametomathias/dispatch's git-trackeddispatch-repos.txt(dispatch#19) — read-check-append, sha-gated PUT, existing entries preserved untouched.Idempotent (already-listed → no-op, no duplicate line — matters because
dispatch_allowcan be re-requested onresume). Failure reported in its owndispatch_allowlist_failurefield, distinct frompartial_failure(substitution) anddispatch_allow_failure(the marker file) — all three gates can fail independently.Config verified live before implementing (2026-07-06):
dispatch-repos.txt's path/format unchanged, and the live CronJob has noDISPATCH_ALLOWLIST_*overrides — so the hardcodedmathias/dispatch:dispatch-repos.txt@maintarget matches the real, unconfigured defaults exactly.Acceptance criteria:
dispatch_allow: true(pre-existing param, now does more).owner/namevia a real commit todispatch-repos.txt— unit-tested against a fake, and separately verified against the live Gitea API (append→confirm→revert cycle on the real file, since this session's cached MCP tool schema predates this feature and couldn't call it directly — verified the underlying REST semantics my code depends on instead: sha-gated PUT round-trips exactly as assumed)..dispatch-allowmarker still lands (unchanged, confirmed still present in the code path).file_reposcount") — not re-verified this round since it was already proven formathias/parallaxon 2026-07-04 using the exact same file/mechanism; the throwaway test line was removed before any cycle could act on it (by design, to avoid touching a shared production config for longer than needed).task checkgreen (exit 0, 0 FAIL). Closing #54.