Workflow to finalize around the remote. Run the condition checklist, STOP and wait for approval before every side-effect step (push/tag/PR/MR/merge). Five tiers — 4 OUTBOUND tiers + 1 INBOUND tier: push (all steps, no tag) · release (push + tag
vX.Y.Z+ release notes) · pr (push branch + open GitHub Pull Request viagh) · mr (push branch + open GitLab Merge Request viaglab) · merge (list PR/MR incoming → pull branch back → gate+test → merge if green).
WHAT
Purpose and context
- Purpose: get changes out to the remote (push/release/PR/MR) or pull incoming PR/MR back in (merge) ONLY after the condition checklist is green, and stop to wait for approval before every side-effect.
- Trigger (when to use):
- User mentions "release" / "push" / "ship" / "lên release" / "chuẩn bị push" / "đủ điều kiện push chưa" / "mở PR" / "tạo MR".
- Before pushing code to the remote, cutting a release, or opening a PR/MR for review-then-merge.
- Want to review-then-merge the repo's open PR/MRs: "gom PR về merge" / "merge PR nếu test xanh" / "ship merge".
- Non-goals: does not fix gate failures itself (that is the job of
/medic+ the fix skill), does not close issues itself, does not install/authenticategh/glab.
Mental model
repo_role (framework · module · downstream · foreign) → bảng gate của ĐÚNG loại repo → and only then the common sequence:
mức (push · release · pr · mr · merge) → gate sức khoẻ (medic --ci + ci-local) → UAT remote (chỉ release/pr/mr có năng lực mới) → git sạch → selftest → [version + patch note] → checklist → DỪNG duyệt → side-effect → [UAT pha 2]. The tiers differ in the final step, not in the checklist.
Input and output contract
| Field | Required? | Meaning | |
|---|---|---|---|
| In | repo_role |
auto-read | python3 harness/scripts/repo_role.py . --json (client machine: ~/.claude/harness/harness/scripts/repo_role.py) — DECLARED label in .overstack.yaml; if missing, infer with evidence then ASK the user once and --set; force manually this time: ship <mức> --as <role> |
| In | tier | no (default = push) | push · release · pr · mr · merge — user describes the scope, no need to remember flags |
| In | user approval | yes, before every side-effect | explicit "yes" for push/tag/release/PR/MR/merge/canary |
| Out | checklist + proposed commands | yes | result of each gate step, phase-1 UAT or skip reason, commit msg, final command |
| Out | text | per tier | release → RELEASE-vX.Y.Z.md; pr/mr → PR/MR body; push → commit msg only; merge → test report + decision |
| Out | side-effect | only after approval | push / tag / release / PR / MR / merge executed |
Rules and capabilities
- RULE-01 (MUST): Do not push when
medic --ciis red — unless the user explicitly overrides; even with an override, the reason must be recorded in the patch note. - RULE-02 (MUST): Patch note must not exaggerate — Known-limitations is mandatory if any part is unfinished.
- RULE-03 (MUST): Levels differ at the FINAL STEP, not in the checklist: every level goes through the health gate; they differ only in the final product (tag / release / PR / MR / merge). Don't tag when the user says "pr"/"mr"; don't open a PR when the user only says "push".
- RULE-04 (MUST):
medic --cigreen does NOT prove the remote path works — that is the job of/fdk-uat.release/pr/mrwith new capability MUST go through UAT phase 1 (canary) before proposing step 7; skip only when the diff confirms it adds no user-visible capability, and the reason must be recorded. Decision that actually burned on 2026-09-17: every prior push (including6c839d8) went only throughmedic --ci/ci-local, never through real remote UAT. - RULE-05 (MUST): Level
merge— TEST is a merge condition, not a formality: must pull the branch + runmedic --ci+ test FOR REAL before merging; if red, absolutely do NOT merge, leave it for the author. One-at-a-time, do not blindly batch-merge multiple PRs at once. - RULE-06 (MUST): PR/MR body follows the same patch note rules: user voice, Known-limitations mandatory if any part is half-done. No SHA/internal function names. NO
Closes #Nautomatically. - RULE-07 (MUST): Side-effects need "yes": push/tag/release/PR/MR are hard-to-reverse (outward-facing) actions → always STOP, show the command, wait for approval; no commit/PR-body AI-attribution (per fdk).
- RULE-08 (MUST): Do not self-install/auth
gh/glab: if a tool is missing → STOP, tell the user; don't guess the host. - RULE-10 (MUST): Know which KIND of repo you're in before running any gate. The first line of the checklist is always
🏷 repo_role=<role> (khai báo | sổ máy | suy luận | ép tay). Sourcesuy luận→ ask the user to confirm, then record the label (repo_role.py . --set <role>; forforeignwrite to the MACHINE log, don't touch someone else's repo). Gates for this repo kind do NOT apply to other kinds:ci-local/capability-stamp/UAT only make sense inframework; running them inmodule/downstreamis grading the wrong thing, omitting them inframeworkis a gap. Lesson 20/09/2026:/shipsingle-flow made shipping the engine repo require remembering a completely different process. - RULE-11 (MUST): READ the gate's rc before pushing. The push/tag command stands at a SEPARATE step after having printed and read
rc=; do not chain gate with push using;or pipe| tail(swallows rc). Two fires on the same day 20/09/2026: pushc83b065whilemedicwas red, and tagv3.0.1whileinstall-testreported FAIL — both due to command chaining. Inframeworkwhen the working tree has untracked files not yet committed (e.g. overwritten by another tool), run the gate on a CLEAN CHECKOUT of the commit (git worktree add --detach <tmp> HEAD) to avoid grading the wrong workflow. - RULE-12 (MUST):
foreign= a guest in someone else's house. Level onlypr/mr, do notpushdirectly to the default branch; follow THEIR commit/CI rules (read CONTRIBUTING, do not apply R15 or overstack rules); do NOT install, do not write.overstack.yaml, do not add any of our config files to the repo. - RULE-09 (MUST): Compose, don't reinvent: use
medicfor health,git tagfor version,gh/glabfor PR/MR — this skill only coordinates the checklist. - Capabilities: read repo + run local gate; remote writes (push/tag/PR/MR/merge) ONLY via authenticated tool and after approval.
Failure boundaries
- Gate red (
medic --ci/ci-local/freshinstall) → blocked, print the gap + fix command, do not push. - UAT FAIL at any phase → failed, stop shipping, delete canary; phase 2 FAIL → remove commit from remote per
/fdk-uatstep 5. - Unsure whether the diff has "new capability" or version bump → clarify (ask user), do not decide yourself.
- Missing
gh/glabor not authenticated → blocked, tell user to install + login. - Level
merge: PR red → do not merge that PR (partial for the whole batch), report the reason.
HOW
Main workflow
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W00 | deterministic | repo | repo_role.py . --json → print 🏷 repo_role=…; source inferred then ask user + --set; choose the gate column in the "Flow by repo_role" table |
role + gate table | invalid role → blocked |
| W01 | deterministic | repo | Health gate medic --ci then python3 fdk/tools/ci-local.py (including probe freshinstall) |
rc 0 | red → blocked |
| W02 | effect | diff, level | UAT /fdk-uat phase 1 — only release/pr/mr has new capability (B02) |
PASS or skip reason | FAIL → failed |
| W03 | deterministic | worktree | Git clean: git status --short, untracked ephemeral junk |
clean | dirty → clean then repeat W03 |
| W04 | deterministic | diff | Selftest/quick check of engines touched | rc 0 | red → blocked |
| W05 | judgment | nearest tag | Version vX.Y.(Z+1) — only release (B01) |
suggested tag | unsure of level → clarify |
| W06 | judgment | git log, scratch-log |
Honest Patch note / PR-MR body, Known-limitations mandatory if anything is half-done | text | — |
| W07 | deterministic | W01–W06 results | Show checklist + proposed commands, STOP and wait for approval | "yes" | not approved → cancelled |
| W08 | effect | approved | Execute per level; if W02 has run then UAT phase 2 right after | side-effect + receipt | phase 2 FAIL → remove commit |
Details of each step (source of truth for W01–W08):
Describe the scope instead of remembering flags: "push" (push only) · "release" (push + tag + notes) · "pr" (push branch + GitHub Pull Request) · "mr" (push branch + GitLab Merge Request) · "merge" (gather PR/MR here → test → merge). Default = push.
Level push/release/pr/mr (PUSH OUT) runs steps 1-8 below. Level merge (GATHER IN) runs a separate branch at step 8 (see section "Merge level").
Steps 1, 3-4 apply to EVERY level. Step 2 (UAT) ONLY release/pr/mr. Step 5 (version tag) ONLY level release. Step 6 renames "written product": release→RELEASE-vX.Y.Z.md; pr/mr→ PR/MR body (same user-voice, same Known-limitations rule). push needs no text, only commit msg. Level merge writes no note (only report test results + merge/no-merge decision).
- HEALTH GATE —
medic --ci(orpython3 fdk/tools/medic.py --ci). FAIL → STOP, print the gap + fix command; do not push when red. Warn (known debt) → allow through but LIST in patch note.- Then
python3 fdk/tools/ci-local.py(framework repo) — run ALL stepsrun:of every GitHub workflow locally (L4). medic is L2; there are gates ONLY in CI (search-index, skill-provenance, retrieval-eval…) — already burned 080926: medic green, push, CI red 2 jobs. Red → do not push. - Includes probe
freshinstall(E2E): curl-install overstack into an ISOLATED EMPTY project (mktemp outside the repo) via working-tree → assert 3 pillars + harness bites + orchestration-ready. Red = the newcomer install path is broken → DO NOT push. Full version curl-github + live acceptance:bash harness/scripts/fresh-install-smoke.sh --remote.
- Then
- UAT gate —
/fdk-uat(ONLY levelrelease/pr/mr, NOTpushas usual):medic --ci/ci-localin step 1 only installs viafile://working-tree — proves "the code on my machine works", NOT "a stranger cancurlthe remote and run it" (the GH#77 bug class: "docs promise what the user doesn't get", actually measured 2026-09-17 when pushing6c839d8without going through UAT at all).- Diff has NEW capability (new skill / new rule that can bite / new engine-tool) → MANDATORY run at minimum PHASE 1 (canary: push a temp branch, curl-install from that branch's own raw, verify the new capability is reachable) before moving to step 7. PHASE 2 (main-URL smoke, waiting for CDN) runs IMMEDIATELY after the user approves the real merge/tag at step 8 — not at proposal time.
- Pure bugfix/internal-refactor diff, adds no capability the user can see → skip this step, record the reason explicitly in the step-7 checklist ("no new capability — skip UAT").
- Unsure whether the diff counts as a "new capability" → ask the user, don't decide to skip on your own.
- Pushing the canary is a real side-effect (pushes a temp branch to the remote) → STOP and ask the user before running, same side-effect rule as this skill. FAIL at any phase → stop shipping, report the specific error + delete the canary (not "later").
- Clean git:
git status --short; make sure ephemeral junk (scratchpad/…) has been.gitignore+git rm -r --cachedif accidentally tracked. Check that no secret/temp files slipped in. - Selftest/quick check the engines touched (e.g.
council.py selftest, tests related to the diff). - Version:
git tag --sort=-v:refname | head -1→ nearest tagvX.Y.Z→ proposevX.Y.(Z+1)(patch) or minor/major if the diff warrants — ask the user if unsure of the level. - HONEST patch note (USER-facing release-note voice — Claude Code style): write
RELEASE-vX.Y.Z.md.- Format: header
Version X.Y.Z:then a FLAT bullet list, one bullet per line, one idea each. - Voice: verb-first describing the change the USER SEES, don't dump commits/internals.
Fixed <triệu chứng> — <giờ ra sao>·Added <khả năng> — <dùng để làm gì>·Removed/Changed …. Write from the reader's perspective ("you/your"), no SHA, no function names unless the user typed them directly. - Implicitly group by verb: Added/Changed first, Fixed after (like Claude release notes). NO group headings needed if the list is short.
- Source: read
git log <tag-cũ>..HEAD+ phiên scratch-log → TRANSLATE technical commit into user-understandable sentences (1 commit may be merged/split into bullets by perceived value, not 1-1). - Known-limitations (MANDATORY if there is any half-done part — an advantage OVER Claude): proposal draft not yet impl, partial coverage, known medic warn → state clearly "in progress/known". Silence = exaggeration (council lesson 2026-07-03).
- Format: header
- Present checklist + propose commands to user (commit msg, push, tag/PR/MR, phase-1 UAT result or reason for skip). STOP and wait for approval.
- After user approval: execute according to the level. Do not run side-effect steps on your own before there is a "yes". If step 2 already ran phase-1 UAT (PASS) → after merge/tag/push, run phase-2 UAT IMMEDIATELY (main-URL smoke, wait for CDN sentinel) before telling the user "done"; if FAIL → remove the commit from remote per
/fdk-uatstep 5.- push: commit +
git push. - release: commit + push +
git tag vX.Y.Z+ create release (notes = patch note). - pr (GitHub): commit +
git push -u origin <nhánh>+gh pr create --base <base> --head <nhánh> --title "…" --body-file <PR-body>. Default base = repo's main branch (e.g.orca), ask if unsure. - mr (GitLab): commit +
git push -u origin <nhánh>+glab mr create --source-branch <nhánh> --target-branch <base> --title "…" --description "…"(or-F <file>). - Choose tool by remote:
git remote get-url origincontainsgithub.com→gh,gitlab→glab.ship pr/ship mrforces the explicit tool; if the tool is not installed/authenticated (gh auth status/glab auth status) then STOP, tell the user to install+login (do not do it yourself). - Do NOT insert
Closes #N/Fixes #Ninto the PR/MR body unless the user explicitly requests it — leave the user in control of closing issues manually.
- push: commit +
Flow by repo_role (W00 chooses ONE column; W01–W08 above describe column framework)
| Step | framework (Rheinmir/setup) |
module (orca-graph, uiux-asset…) |
downstream (project with .llmwiki/) |
foreign (someone else's repo) |
|---|---|---|---|---|
| Gate | medic --ci → ci-local (on a clean checkout if the tree has foreign files) |
commit first → ITS OWN test + eval + install-test (read each rc; install-test clones from HEAD so uncommitted means untested) | project's tests + llmwiki-validate + medic (downstream mode) |
clean git + their test/CI |
| Remote UAT | when the diff has new capability (B02) | smoke install from remote after push (install.sh of the module into a temp HOME) |
no | no |
| Version | capability-stamp.py --update WHEN what reaches the client machine changes (hook, shim, installer, global tool) — even if the surface sha is unchanged it must still be bumped, otherwise a machine already on the current version will not refresh |
bump VERSION + constant in code + CHANGELOG |
per project | per family |
| Final step | push (· tag/PR by level) | push → WAIT for green CI → tag + release → re-pin in framework (upstream_pin in .overstack.yaml: copy SKILL if changed → sync-skills.py → write commit/version to fdk/skills.provenance.json → ship framework) |
push / pr | ONLY pr / mr |
| Forbidden | installer writes to this repo (auto-reject rc 3) | ci-local, capability-stamp |
push framework files, UAT, stamp | install, write overstack config, AI-attribution against their rules |
Branches
| ID | Kind | Guard | Behavior | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | conditional_required | level = release |
W05 version + tag vX.Y.Z + release notes |
other level → skip | W06 |
| B02 | conditional_required | level ∈ {release, pr, mr} AND diff has new capability | UAT phase 1 before W07, phase 2 after W08 | internal bugfix/refactor → skip, write reason into checklist; unsure → clarify | W03 |
| B03 | user_optional | level = merge |
Separate GATHER BACK loop (section below), drop W02, W05–W06 | Red PR → do not merge that PR | summary report |
Level merge (GATHER BACK — approve-then-merge incoming PR/MR)
Do not write note/tag. Skip steps 2, 5-6; replace with test-per-request loop:
- Choose tool by remote (as above):
github.com→gh,gitlab→glab. Not authed → STOP and tell user. - List open PR/MR of the repo:
- GitHub:
gh pr list --json number,title,headRefName,mergeable,isDraft - GitLab:
glab mr list(or--output jsonif the version supports it). Show the list to user; if user specifies certain numbers then filter, otherwise ask whether to do all or select.
- GitHub:
- For each PR/MR (one-at-a-time, no blind batching):
a. Pull branch to a CLEAN LOCAL: GitHub
gh pr checkout <n>; GitLabgit fetch origin merge-requests/<n>/head:mr-<n> && git checkout mr-<n>(orglab mr checkout <n>). b. Run GATE + TEST on that branch:medic --ci+ selftest/relevant tests for the PR diff. This is the merge condition — do NOT merge before running. c. Green → propose merge command, STOP and wait for approval, then merge: GitHubgh pr merge <n> --squash(or --merge/--rebase per repo); GitLabglab mr merge <n>. Red → do NOT merge; report the red spots + leave the PR as is for the author to fix. d. After merge: return to the original branch (git checkout <nhánh-cũ>), clean up temporary branch. - Summary report: each PR/MR → merged / skipped-because-red (with reason) / skipped-because-user.
Validation and stopping
The condition to proceed is the tool's rc (medic, ci-local, fdk-uat, test), not the model's own assessment. All side-effects stop at W07 waiting for "yes"; the level merge stop waiting for approval per PR. Do not auto-retry push/tag on network error — notify the user.
Examples
- Positive (module): in repo
orca-graphtype "ship release" → W00 prints🏷 repo_role=module (khai báo)→ commit →pytest testsrc 0 →evals/run.pyrc 0 →tests/install-test.shrc 0 → bumpVERSION→ STOP for approval → push → CI green → tag + release → remind the re-pin step atRheinmir/setupperupstream_pin. - Boundary (foreign): in a repo with no stamp, no label → W00 prints
🏷 repo_role=foreign (suy luận: không có fdk/wiki, không có .harness-stamp)→ ask the user to confirm → only proposeship pr, read their CONTRIBUTING, write no overstack files. - Positive: "ship push" after editing docs → W01 green, B02 skip (no new capability, record reason), W03–W04 green → checklist +
git push→ user "yes" → push. - Boundary/failure: "ship pr" has a new skill but
ci-localred 1 job → blocked at W01, print the red job + fix command, do not push the branch, do not open a PR. - Boundary: "ship merge" has 2 PRs, PR #2 test red → merge #1 after approval, drop #2 with reason (partial).