# Work and releases

GitHub repository admins hold project-admin authority on their own projects without duplicate
registry enrollment. Registry-listed admins with repository write access remain supported.
Use `mmi-cli oracle org access role <owner/repo> --json` to verify authority for release,
candidate, hotfix, and runtime operations. Organization membership role alone is not this check.
The Hub distribution train remains organization-master-only.

Start with `mmi-cli onboard` and the [board skill](../../board/SKILL.md).
Reading or discussing the board is read-only until the developer authorizes work.
Claim approved work with `mmi-cli oracle board claim <issue>`, then prepare an isolated task
workspace through the host. Preserve other developers' work.

MMI workflows require no personal Jerv installation. Use Git or your chosen host for worktree
creation; MMI performs its own guarded cleanup. Report a skill misfire with
`mmi-cli learning skill-lesson --skill <name> --title <summary> --body-file <path>`.
Feedback belongs on the MMI Hub board and never requires another developer's personal tooling.

Read the issue, implementation, callers, and repository policy before editing.
Run `mmi-cli tests policy --base <comparison-base>` to determine required checks against your target
branch (normally origin/development for train repositories). The repository's policy decides, not
a universal test count. Use its build and check commands.
Open a PR through `mmi-cli devops pr create` with a closing issue link, and land through
`mmi-cli devops pr land`. Verify required checks, merge, issue closure, and cleanup before calling
the implementation complete. A merged implementation is not a production release.

## Report MMI tooling bugs

This workflow covers bugs in MMI tooling, including its CLI, plugin, and shared workflows.
Product bugs stay with their product; do not automatically file them on the Hub board.
For every developer other than authenticated GitHub login `jervaise`, ask for consent before
filing a tooling bug through `mmi-cli learning report`. Do not infer identity from a display name
or Slack email. The owner exemption skips this consent workflow and its released-fix DMs.

Show a safe summary, affected MMI version, and minimal reproduction. Explain that approval files
or attaches the report to an existing Hub issue and notifies Jervaise through the MMI-Future Slack app.
Wait for explicit approval; a decline means no
report, no alternate filing command, and no repeated consent prompt for the same incident.
Remove secrets and private data from the title, reproduction, and logs before sending anything.
Write the approved report body to a UTF-8 file; never put it in command arguments.

Use `mmi-cli learning report --type bug --title "<safe summary>" --body-file <path>`.
Let the existing report command handle duplicates; reuse its returned issue and never add
`--force` to bypass matching or file another issue for the same incident. Do not claim or poll
the issue; the Hub team owns the fix.

On the first approved report without a saved Slack destination, ask once whether the developer
wants a DM when the fix is released. If they agree, ask them to confirm their Slack workspace
email and append `--slack-email <confirmed-email>`. This email is a notification destination,
not verified identity or authorization. Never guess it from GitHub profile data, use another
person's address, or include it in the public issue body. The Hub stores the confirmed email privately
until scheduled Slack lookup succeeds, then replaces it with the Slack user ID for later reports.
Declining notifications does not prevent filing the approved bug report.

Read the returned `notification.status`: `tracked` means the released-fix notification is
registered for scheduled processing, not delivered, `needs-slack-email` means no saved destination
was found, `unavailable` means the
issue was filed but notification registration failed, and `owner-excluded` means no owner DM.
If `needs-slack-email` follows a report where the notification question was not yet asked, ask
once; use the same report with the confirmed email so dedup retains its issue. Never retry an
ambiguous report submission. Do not claim a DM is registered when the status is unavailable,
and do not replay a valid report merely to repair notification failure. Unresolved Slack
lookup remains pending in the notification record; it must not use an old destination.
The MMI-Future app sends a fix DM only after the issue's closing merge is included in a stable
Hub release whose publication and deployment both succeeded. Closing an issue alone is not enough.
An uncertain Slack send is recorded for operator review and never replayed automatically.

## Choose the declared train

Read `mmi-cli oracle org project get <owner/repo> --json` and `mmi-cli status` for the resolved track.
The registry and MMI's resolver own the release track and deployment model; do not invent your own
inference from the repository name or branches that happen to exist. Normal train work targets
development. A trunk repository follows its single branch and declared workflow instead.

- Full track: development → rc → main. Use [rcand](../../rcand/SKILL.md) to prepare a candidate,
  then [release](../../release/SKILL.md) for promotion. The release workflow also describes its
  supported development-source route.
- Direct track: development → main. Use [release](../../release/SKILL.md); do not invent an rc lane.
- Vercel-hosted repos (registry `deployModel: vercel`) are direct track: merging development → main
  is the go-live, Vercel deploys main itself, and the release train waits for that deploy and checks
  the live URL before it tags and announces. No version-file bump on these repos. Every branch has a
  Vercel preview URL; [stage](../../stage/SKILL.md) reports it. No rc, no box stage.
- Trunk track: one main branch, without an rc/development promotion train. Follow the repository's
  declared publishing workflow rather than manufacturing train branches.
- Urgent already-merged fixes: use [hotfix](../../hotfix/SKILL.md), which carries selected development
  fixes directly to main on either track.

Production changes require explicit authorization from the authorized human.
Read the selected release skill before operating the train. Let it choose versions, run gates,
publish, deploy, and verify the resulting release. A refusal requires fixing its cause; do not
finish by hand, bypass checks, or start a duplicate release.
A release announces itself in Slack once its deploy and publish are green: the Hub to the org alerts channel, a product to its own
project channel when the project's registry META sets `releaseChannel`; never post release notes
by hand. The Hub always needs a curated `--announce-summary-file`, and so does any product whose
META sets `releaseChannel` — 3–6 plain lines in the project's `releaseLanguage` (default `en`),
written for that project's audience, never the raw PR titles. Every summary line must describe work
that actually ships in the cut: the train refuses an issue or PR number the release range carries in no
commit, so build and verify the lines from one pass over that range —
`git log --stat --format='%h %s%n%b' origin/main..origin/development`, which carries each commit's
subject, body and changed files — never from subjects, PR titles or memory; a line whose claim is not
in that output does not go in the file.

Announcement delivery uses the developer's GitHub-authenticated Hub session. The Hub checks project
authority, resolves the registered channel, and reads the MMI Future Slack key inside its vault boundary.
Developers need no AWS CLI, AWS identity, or copy of the shared bot key. Keep a failed announcement's
summary for the supported resume path. A delivery-unknown response requires checking the existing
message; never resend blindly. Confirmed delivery is recorded per repository and release tag.

For local or private live testing, read [stage](../../stage/SKILL.md).
For a new product repository, use [bootstrap](../../bootstrap/SKILL.md).

## Hosting and deploy models

The owner's hosting policy is one rule per provider (`docs/decisions/2026-09-19-one-rule-per-provider.md`):
AWS is the org control plane and the vaults only; Hetzner boxes run every long-running service
(registry `deployModel: tenant-container`); Vercel hosts only web apps that benefit from preview
links (`vercel`); Cloudflare owns all DNS, TLS and tunnels; static public downloads are S3 +
CloudFront per family — `get.mutatismutandis.co` for MM products, `get.jerv.ai` for Jerv products —
never mixed. `hub-serverless` is the Hub control plane itself, and `registry-publish`/`none` are not
hosted services. Sign-in gates and signed payloads stay on each product's own server. Do not move a
workload to a different provider, or add a second one for the same job, without an explicit owner
decision.

## Keep guidance with the change

When changing organization behavior, update the affected guide topic in the same PR.
Hub release packaging rebuilds every host's skill tree from the shared source and verifies parity.
The guide's source-review check requires reviewing a topic when its declared policy inputs change;
unchanged advice needs no cosmetic rewrite on each version bump.
