feat(project-create): v0.3.0 — путь создания репо = выбор оператора (шаг 2, Hard rule 2: не регистрировать до репо) [agensyn-урок 2026-08-29]

This commit is contained in:
2026-08-30 01:57:45 +03:00
parent c09901f9a6
commit 973e59b083

View File

@@ -1,13 +1,15 @@
--- ---
name: project-create name: project-create
author: ours author: ours
version: 0.2.0 version: 0.3.0
description: > description: >
Mappa-side cycle of creating a new project: ask the operator for the hosting Mappa-side cycle of creating a new project: ask the operator for the hosting
address (platform + user/org) FIRST — never derive it from neighbouring address (platform + user/org) FIRST — never derive it from neighbouring
projects — then pre-flight checks (free in mappa AND in gitea), then create projects — then pre-flight checks (free in mappa AND in gitea), then ASK the
mappa registration and the gitea repo SIMULTANEOUSLY (repo via .admin: task operator HOW to create the repo (via .admin task+letter / server flow
+ covering letter, priority P0, no paired review for ops), then ask where on projects_create_repo / manual) — the path is the operator's call, never
assumed — then create the repo and register in mappa (in the right order,
see Hard rule 2026-08-29), then ask where on
disk the project folder goes and what it's named, create the folder, write disk the project folder goes and what it's named, create the folder, write
the `.mappa` marker (wiki:3340), and hand over to project-bootstrap (general the `.mappa` marker (wiki:3340), and hand over to project-bootstrap (general
skill). Triggers (bilingual): skill). Triggers (bilingual):
@@ -37,23 +39,33 @@ Before any `projects_register` / gitea repo creation for a **new** project.
- Re-hosting / renaming an existing project → read what's there first, no creation. - Re-hosting / renaming an existing project → read what's there first, no creation.
- A project that already exists in mappa registry or gitea → verify with the operator, don't create a duplicate. - A project that already exists in mappa registry or gitea → verify with the operator, don't create a duplicate.
## Hard rule (the original lesson) ## Hard rules
### Hard rule 1 (original lesson)
**The hosting address (platform + user/org) is the operator's decision. ASK, never derive.** Do not copy the owner from a neighbouring project (the live failure: `victor/tg-digest` was assumed by analogy with `victor/yt-digest` — wrong, the operator had to correct it). Options to offer: platform (Gitea git.kzntsv.site / GitHub), and for Gitea — the known users/orgs from the registry (e.g. `victor` personal apps, `OpeItcLoc03` infra/meta, `cancel_music` music modules). The registry `mcp__mappa__projects_list` shows the existing `qualified` names — use it to propose options, but the choice is the operator's. **The hosting address (platform + user/org) is the operator's decision. ASK, never derive.** Do not copy the owner from a neighbouring project (the live failure: `victor/tg-digest` was assumed by analogy with `victor/yt-digest` — wrong, the operator had to correct it). Options to offer: platform (Gitea git.kzntsv.site / GitHub), and for Gitea — the known users/orgs from the registry (e.g. `victor` personal apps, `OpeItcLoc03` infra/meta, `cancel_music` music modules). The registry `mcp__mappa__projects_list` shows the existing `qualified` names — use it to propose options, but the choice is the operator's.
### Hard rule 2 (2026-08-29, live failure agensyn)
**The repo-creation PATH is the operator's decision — ASK, never assume the default.** Offer: (a) via `.admin` (task + letter — the legacy canon, secrets live there), (b) server flow `mcp__mappa__projects_create_repo` (creates the repo on the provider AND registers in mappa itself, `source=auto`), (c) manual. The operator overrides the default; delegating to `.admin` before they choose is not revocable (the task lands on the board, `.admin` reacts instantly — the agensyn case).
**NEVER register the project in mappa (`projects_register`) BEFORE the repo exists.** A pre-existing registry entry makes the server flow `projects_create_repo` return 409 `already registered` (resolveStrict), and there is no delete channel for registry entries. Order that works: operator picks the path → repo is created → registration (the server flow registers itself; the `.admin` path registers AFTER the repo exists, with the known qualified).
## Steps (the pattern) ## Steps (the pattern)
1. **Ask the hosting address.** Platform (Gitea/GitHub) + user/org. One question, offer the registry-derived options, wait for the answer. Do NOT register anything yet. 1. **Ask the hosting address.** Platform (Gitea/GitHub) + user/org. One question, offer the registry-derived options, wait for the answer. Do NOT register anything yet.
2. **Pre-flight checks** — can we create in BOTH places: 2. **Ask the repo-creation path** (Hard rule 2): via `.admin` / server flow `projects_create_repo` / manual. Do NOT delegate or register yet — this is the operator's call.
- mappa: the project name is not in the registry (`projects_list` / `projects_resolve`). 3. **Pre-flight checks** — can we create:
- gitea: repo name is free, the user/org exists, we have rights (the .admin owns the tokens — it checks on its side). - mappa: the project name is not in the registry (`projects_list` / `projects_resolve`) — and do NOT create it here.
3. **Create SIMULTANEOUSLY** (not one before the other): - gitea: repo name is free, the user/org exists, we have rights (`.admin` path: `.admin` owns the tokens and checks on its side; server flow: the mappa server holds provider creds).
- `mcp__mappa__projects_register` — mappa registration (id returned). 4. **Create the repo + register, per the chosen path:**
- repo via `.admin``task_create` in `.admin` (**priority P0** — it blocks the project chain) + covering letter via `inbox_send` (a task on the board doesn't ping a live session). The repo task is an **ops task → NO paired review** (mappa-delegation: skip review for ops). Acceptance: repo created private, answer letter with clone URLs + which token is needed for push. - **server flow** (`projects_create_repo`) — single call creates the repo AND registers (`source=auto`, `qualified` from the provider response). Nothing else to register.
4. **Ask the operator: where on disk the project folder goes and what it's named** (location AND folder name — both are the operator's call) — do not guess the path or the name. - **via `.admin`** — `task_create` in `.admin` (**priority P0** — it blocks the project chain) + covering letter via `inbox_send` (a task on the board doesn't ping a live session). The repo task is an **ops task → NO paired review** (mappa-delegation: skip review for ops). Acceptance: repo created private, answer letter with clone URLs + which token is needed for push. AFTER the repo exists (qualified known) → `projects_register`.
5. **Create the folder** on the agent's local filesystem (the operator's workstation, not a remote host). - **manual** — the operator creates the repo themselves; register after they confirm.
6. **Write the `.mappa` marker** — see Step 5.5 below. Right after the folder exists, the marker values are known from this cycle; no manual generator run. 5. **Ask the operator: where on disk the project folder goes and what it's named** (location AND folder name — both are the operator's call) — do not guess the path or the name.
7. **Hand over to bootstrap**`project-bootstrap` (general skill) does git init, .gitignore, README, AGENTS.md, remote connect, push. Separate step, after the repo exists. 6. **Create the folder** on the agent's local filesystem (the operator's workstation, not a remote host).
7. **Write the `.mappa` marker** — see Step 5.5 below. Right after the folder exists, the marker values are known from this cycle; no manual generator run.
8. **Hand over to bootstrap**`project-bootstrap` (general skill) does git init, .gitignore, README, AGENTS.md, remote connect, push. Separate step, after the repo exists.
## Step 5.5 — `.mappa` маркер (контракт wiki:3340) ## Step 5.5 — `.mappa` маркер (контракт wiki:3340)
@@ -61,11 +73,11 @@ Before any `projects_register` / gitea repo creation for a **new** project.
«без маркера папка не участвует в mappa-операциях», task:1546). Значения уже «без маркера папка не участвует в mappa-операциях», task:1546). Значения уже
известны из этого цикла — ручной прогон генератора не нужен (task:1583): известны из этого цикла — ручной прогон генератора не нужен (task:1583):
- `project` — канон (имя папки, выбрано оператором в шаге 4); - `project` — канон (имя папки, выбрано оператором в шаге 5);
- `tenant``MAPPA_TENANT` (по умолчанию `vitya`); - `tenant``MAPPA_TENANT` (по умолчанию `vitya`);
- `url``MAPPA_CORE_URL` (без trailing slash); - `url``MAPPA_CORE_URL` (без trailing slash);
- `git_provider` — платформа из шага 1 (gitea/github/…); - `git_provider` — платформа из шага 1 (gitea/github/…);
- `git` — qualified (owner/repo) из ответа `.admin` (шаг 3, clone URL). - `git` — qualified (owner/repo) из ответа `.admin` / `projects_create_repo` (шаг 4, clone URL).
Запись — детерминированный рендер по контракту. Канон-скрипт — ассет Запись — детерминированный рендер по контракту. Канон-скрипт — ассет
`project-bootstrap` (в репо: `skills/project-bootstrap/assets/dot_mappa_marker.py`; `project-bootstrap` (в репо: `skills/project-bootstrap/assets/dot_mappa_marker.py`;
@@ -79,24 +91,30 @@ python <skills-repo>/skills/project-bootstrap/assets/dot_mappa_marker.py write \
Верифицировать: `python .../dot_mappa_marker.py check --dir "$FOLDER_PATH"` Верифицировать: `python .../dot_mappa_marker.py check --dir "$FOLDER_PATH"`
→ exit 0. Маркер без секретов, коммитится. Если project-bootstrap не → exit 0. Маркер без секретов, коммитится. Если project-bootstrap не
установлен — маркер всё равно появится на хэндовере (шаг 7 → bootstrap 5.8). установлен — маркер всё равно появится на хэндовере (шаг 8 → bootstrap 5.8).
## Why the repo goes through `.admin` ## Why the repo usually goes through `.admin` (and when it doesn't)
Gitea tokens are secrets — they live only with `.admin` (zone rule: secrets stay out of other agents). Precedent: yt-digest (inbox:2305) and tg-digest (inbox:2463) — `.admin` creates the repo via `POST /user/repos` → transfer to the target owner (the admin endpoint refuses without write:admin), then answers with the clone URLs and the push token (`pass show gitea/admin-token` for OpeItcLoc03, or a user token with repo:write for the target owner). Gitea tokens are secrets — they live only with `.admin` (zone rule: secrets stay out of other agents). Precedent: yt-digest (inbox:2305) and tg-digest (inbox:2463) — `.admin` creates the repo via `POST /user/repos` → transfer to the target owner (the admin endpoint refuses without write:admin), then answers with the clone URLs and the push token (`pass show gitea/admin-token` for OpeItcLoc03, or a user token with repo:write for the target owner).
**The alternative:** `projects_create_repo` (task:1542) — the mappa server holds provider creds and creates the repo itself, registering the project in the same call. The operator chooses which path; do not hard-default to `.admin` (Hard rule 2, agensyn 2026-08-29).
## Failure modes ## Failure modes
- The operator hasn't chosen the address → abort, don't create. The address is the operator's call. - The operator hasn't chosen the address → abort, don't create. The address is the operator's call.
- The operator hasn't chosen the repo-creation path → ask, don't default to `.admin` (Hard rule 2).
- mappa name taken → suggest a rename, don't overwrite. - mappa name taken → suggest a rename, don't overwrite.
- gitea name taken / user missing → report, suggest a rename. - gitea name taken / user missing → report, suggest a rename.
- `projects_create_repo` → 409 `already registered` → you registered before the repo (Hard rule 2) — there is no delete channel; report to the operator, don't work around it by hand.
- `.admin` repo task failed → report, don't register the project alone (mappa and gitea are created as a pair). - `.admin` repo task failed → report, don't register the project alone (mappa and gitea are created as a pair).
- `task_create` in `.admin` → 422 secret-detected on the token scope string (`write:repository` is flagged) → rephrase ("токен с правами на запись в репозиторий"), don't include raw token strings. - `task_create` in `.admin` → 422 secret-detected on the token scope string (`write:repository` is flagged) → rephrase ("токен с правами на запись в репозиторий"), don't include raw token strings.
- The operator answers the on-disk path only after the repo is created → that's the normal order, fine. - The operator answers the on-disk path only after the repo is created → that's the normal order, fine.
## What NOT to do ## What NOT to do
- Don't assume the hosting owner from a neighbouring project — ask (the hard rule). - Don't assume the hosting owner from a neighbouring project — ask (Hard rule 1).
- Don't assume the repo-creation path — ask (Hard rule 2); a `.admin` delegation already sent is not revocable.
- Don't register the project in mappa before the repo exists (breaks the server flow with 409; no delete channel).
- Don't create mappa registration alone without the repo (they're a pair). - Don't create mappa registration alone without the repo (they're a pair).
- Don't create the repo alone without the mappa registration. - Don't create the repo alone without the mappa registration.
- Don't create the on-disk folder in a guessed location — ask. - Don't create the on-disk folder in a guessed location — ask.