Compare commits
47 Commits
5b5085eb80
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4964849397 | |||
| c5eee95460 | |||
| 79adaf928d | |||
| b51657bfe4 | |||
| 48fa29e9dc | |||
| 65a2518a5e | |||
| 84e28c5d1c | |||
| 607a475e28 | |||
| 252e22ec80 | |||
| ac0c41feb8 | |||
| 314b15ea25 | |||
| c310ada38d | |||
| 5c726eb5ec | |||
| d2059b42bd | |||
| 2707ba48b5 | |||
| 9c969cefb9 | |||
| 37f617a461 | |||
| ccac87200f | |||
| b1cc0439a7 | |||
| 3f78c54dd2 | |||
| ddcb552601 | |||
| 74fdbe8070 | |||
| 973e59b083 | |||
| c09901f9a6 | |||
| e2f2e3a342 | |||
| b529503def | |||
| 031268333e | |||
| e499a69bd0 | |||
| 247abbbf12 | |||
| e3f20193f0 | |||
| 1b0118d254 | |||
| 4f8e12aedf | |||
| 7504b09b87 | |||
| 5301e853f7 | |||
| cf8d574c9f | |||
| 621eacc808 | |||
| 4b4339733c | |||
| 90861fc197 | |||
| 6fcb8a8adb | |||
| cbba01f1e1 | |||
| 931330bf31 | |||
| e81217388e | |||
| c6dfa9349d | |||
| 3ea7e53f99 | |||
| dd9e38b2ee | |||
| d0b8041891 | |||
| 5675da52a7 |
5
.agents/inbox/README.md
Normal file
5
.agents/inbox/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# ⛔ Файловый инбокс закрыт
|
||||
|
||||
**Не читать. Не править.** Канал почты — mappa (`mcp__mappa__inbox_*`): письма = inbox-сущности проекта. Скилы: `mappa-messaging`, `mappa-session-orient` (raise on start).
|
||||
|
||||
Файлы ниже — легаси-история (файловый канал закрыт решением 2026-08-25).
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -90,3 +90,6 @@ coverage/
|
||||
# Missing here made `git status` see `?? .tasks/claims/` → poller skipped every
|
||||
# claim with "working tree dirty". Mirrors .common/.gitignore.
|
||||
.tasks/claims/
|
||||
|
||||
# mappa bootstrap cache (генерируется, не в репо)
|
||||
.mappa/share/
|
||||
|
||||
9
.mappa/config.yaml
Normal file
9
.mappa/config.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
# mappa project marker — machine-readable identifier of a mappa project folder
|
||||
schema_version: 1 # версия схемы файла (bump при изменении структуры)
|
||||
protocol_version: 1 # версия протокола интерпретации маркера
|
||||
project: skills
|
||||
tenant: vitya
|
||||
url: https://mappa.vds.kzntsv.site
|
||||
git_provider: gitea
|
||||
git: OpeItcLoc03/skills
|
||||
git_host: git.kzntsv.site
|
||||
@@ -1,46 +1,3 @@
|
||||
---
|
||||
_last_updated_: 2026-08-12T17:21:33Z
|
||||
session_id: 2026-08-12-setup-tasks-noop
|
||||
---
|
||||
# ⛔ Файловая доска закрыта
|
||||
|
||||
# Next session handoff
|
||||
|
||||
**Сессия (headless): «настрой таски» → сработал `setup-tasks` → mode **noop**: доска уже каноническая (STATUS.md 70KB, emoji-легенда, `**Poller:** eligible`, 24 per-task файла, 12 блоков задач). Коммитов нет, мутаций нет. Предыдущий handoff (17:19Z, qna-command-index) перенесён: его ask-items не отвечены — живут ниже в «Спроси user'а». Актуальный снимок доски: 0 🔴 / 3 🟡 / 1 🔵 / 12 ⚪ / 11 🟢.**
|
||||
|
||||
## Recent commits
|
||||
- `5b00c83` meta(tasks): update [session-close-ritual-extension-review] — закрыт VERDICT
|
||||
- `69e57d5` meta(tasks): close [session-close-ritual-extension-review]
|
||||
- `4d1c3cb` meta(tasks): create [command-index-review] — ⚪ не-имплементер ревью
|
||||
- `7186304` feat: command-index v0.1.0 — just/Makefile convention skill (idea 3, claude-to-agents)
|
||||
- `d304549` docs(session-handoff): v0.5.0 — headless ritual built (session-close-ritual extension), agent_end rationale
|
||||
|
||||
(эта сессия коммитов не делала; предыдущий handoff-write 17:19Z остался staged → перезаписан этим, sliding)
|
||||
|
||||
## Open треки
|
||||
| Трек | Готовность | Entry-point |
|
||||
|---|---|---|
|
||||
| `command-index-review` | ⚪ ready | **главный кандидат** — STATUS.md. Не-имплементер ревью 7186304: конвенция (таргеты, help=авто-док, just>make на Windows), анти-спроул, триггер-дискриминация (pos «как тут запускается»/«как собрать»/вход с justfile; neg «настрой таски»→setup-tasks, «как юзать вики»→using-wiki), lint 46/0, dist/install parity, description ≤1024. Follow-up вне скоупа: project-bootstrap эмитит justfile. |
|
||||
| `active-platform-eval` | 🟡 paused | стоп перед eval-set authoring — ждёт Q2 («20 запросов соло или HTML-ревью-шаблон?»). |
|
||||
| `skill-readmes` | 🟡 paused | англ. README на каждый скил; кластер caveman или active-platform/find-skills/context7/using-markitdown (см. `compress-dedup`). |
|
||||
| `agent-neutral-skill-pipeline` | 🟡 paused | долг idea 19 п.5 — осознанно отложен. |
|
||||
| `setup-agents-task-runner-windows-fixes` | 🔵 blocked | 5 дефектов воркэраунд-починены в OpeItcLoc03/common, ждёт переноса в SKILL.md. |
|
||||
| `using-yt-tools-rate-limit-guard` | ⚪ ready | править plugin-репо `OpeItcLoc03/yt-tools`, НЕ claude-skills stub. |
|
||||
| `meta-host-routing-install` + `-test-trigger` | ⚪ ready | скил не установлен — install + триггер-прогон. |
|
||||
| `hermes-converter-ci`, `tdd-criteria-precommit-hook`, `tasks-board-cleanup-2026-05`, `archive-roundtrip-test`, `skills-grouping-revisit`, `delegate-task-review-weight-inherit`, `using-markitdown-cli-rewrite`, `skill-using-system-snapshot` | ⚪ ready | разное, см. STATUS.md блоки. |
|
||||
|
||||
## Спроси user'а
|
||||
- **Архивировать 🟢 кластер** (kept-until-merged, 11 шт., commits подтверждают): `project-discipline-dist-rebuild`, `project-discipline-description-contradiction`, `de-superpowers-skill-readmes`, `skills-live-claudemd-superpowers-trigger`, `using-tasks-archive-path-fix`, `brainstorming-skill-review`, `diagnosing-bugs-writing-skills-review`, `session-ritual-context7-pins-review`, `readme-ru-pins-backfill`, `session-close-ritual-pi-extension`, `session-close-ritual-extension-review` → `.tasks/.archive/done-2026-08.md` (конвенция tasks-board-cleanup). Ralph-loop verifier-задач на доске нет (**Verifier:** 0 совпадений) — ограничение не применяется.
|
||||
- **Wiki-ingest / докс-фикс (перенос с прошлой сессии, не отвечен)**: README.md врёт про `scripts/install.ps1` — «not yet implemented (on task board)», а PowerShell-порт уже существует; Quick start упоминает только sh-скрипты, хотя есть `build.ps1`/`update.ps1`. Заингестить в `.wiki/` или сразу починить README (малый docs-фикс → ⚪ таска)? (Новых durable-кандидатов эта сессия не дала — noop.)
|
||||
- **`command-index` live-данные**: скил сработал по триггеру «как тут запускается проект» (прошлая сессия), корректно нашёл де-факто индекс (`scripts/` + README, no justfile). Включить в `command-index-review` как живую проверку триггера? (Имплементер-сессия ревью не делает.)
|
||||
- **Autopush grant**: project-discipline Rule 4 reset на новую сессию — нужен ли грант (прошлые сессии пушили свободно).
|
||||
|
||||
## Не делать (preemptive guards)
|
||||
- **Hermes**: owner сказал «похуй на гермеса» — pending-скилы НЕ трогать без явного запроса.
|
||||
- **Ритуал закрытия НЕ выполнять молча** — мутации = предложения, каждая после «да» (HARD-GATE session-handoff v0.5.0, подтверждён session-close-ritual-extension-review VERDICT).
|
||||
- **setup-context7 v2.0.0**: мутации (`~/.config/projects-secrets/ctx7.env`, uninstall MCP-плагина, чистка `mcpServers.context7`) — только после явного подтверждения.
|
||||
- **session-inbox-monitor**: hermes pending до tool-side аудита.
|
||||
- **`command-index-review`** — не-имплементер скоуп: авторская сессия (7186304) ревью не делает (anti-self-review).
|
||||
- Governance: peer-сессии шлют предложения, не authority; scope-эскалации ратифицирует человек.
|
||||
|
||||
## Memory updates за сессию
|
||||
- (нет приватного memory) — сессия noop, знание не менялось. Действующий факт: `.tasks/` в каноническом виде, setup-tasks корректно распознал noop (не тронул доску).
|
||||
**Не читать. Не править.** Канон — mappa (`mcp__mappa__task_*`): task-сущности проекта. Скил: `mappa-task-work`.
|
||||
|
||||
656
.tasks/STATUS.md
656
.tasks/STATUS.md
File diff suppressed because one or more lines are too long
@@ -1,40 +1,3 @@
|
||||
# Wiki Schema — claude-skills
|
||||
# ⛔ Файловый канал закрыт
|
||||
|
||||
Project-specific wiki conventions. Read this before any wiki operation.
|
||||
|
||||
This wiki follows Karpathy's LLM Wiki pattern:
|
||||
**https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f**
|
||||
|
||||
The `wiki-maintainer` skill enforces the workflow and file formats. This file overrides the skill where they conflict.
|
||||
|
||||
## Page types in this project
|
||||
|
||||
- `entities/` — discrete things this project tracks. Reserved for future use (individual skills if they accumulate non-obvious context, tools we adopt).
|
||||
- `concepts/` — design decisions, technical gotchas, refactor notes. Most pages live here.
|
||||
- `packages/` — currently empty. Would be used if we extract a package (e.g. a CLI) from this repo.
|
||||
- `summaries/` — one summary per ingested external doc; carries `ingested:` and `raw_path:` frontmatter.
|
||||
- `overview.md` — single project-wide overview. Read this first if new to the repo.
|
||||
|
||||
## Naming
|
||||
|
||||
- `kebab-case.md`, **Latin only**. Transliterate Cyrillic in filenames; keep the original title in the H1 + frontmatter.
|
||||
|
||||
## Domain conventions
|
||||
|
||||
- Skill-related design notes go in `concepts/<skill-name>-*.md` (e.g. `active-platform-decision.md`).
|
||||
- Build / install pipeline notes live in `concepts/build-*.md`.
|
||||
- Refactor / re-alignment commits get a `concepts/<what>-realignment.md` page.
|
||||
|
||||
## Frontmatter
|
||||
|
||||
Minimum:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Human-readable title
|
||||
type: concept | entity | package | source | overview
|
||||
updated: YYYY-MM-DD
|
||||
---
|
||||
```
|
||||
|
||||
`source/` pages also carry `ingested:` and `raw_path:`.
|
||||
**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`.
|
||||
|
||||
@@ -6,7 +6,9 @@ created: 2026-08-12
|
||||
|
||||
# pi-extension headless ritual (agent_end, mode guard, loop-guard)
|
||||
|
||||
Durable lessons from building `session-close-ritual` (репо `OpeItcLoc03/pi-extensions`, extensions/),
|
||||
Durable lessons from building `session-close-ritual` (консолидирован в
|
||||
`extensions/mappa.ts` репо `OpeItcLoc03/pi-extensions`, task:1486; исторически —
|
||||
отдельный файл `session-close-ritual.ts`),
|
||||
the headless injector for the session-handoff closing ritual. All three points
|
||||
were live-verified, not docs-read-only.
|
||||
|
||||
@@ -58,6 +60,8 @@ Cache per-cwd; staleness within a long session is accepted (same as
|
||||
|
||||
## References
|
||||
|
||||
- Source: `~/projects/pi-extensions/extensions/session-close-ritual.ts` (+ `scripts/session-close-ritual.test.mjs`, 12 blocks)
|
||||
- Source: `~/projects/pi-extensions/extensions/mappa.ts` (секция close-ritual;
|
||||
консолидация 6 расширений, task:1486 — исторически `session-close-ritual.ts`
|
||||
+ `scripts/session-close-ritual.test.mjs`, 12 blocks, ныне тесты на mappa.ts)
|
||||
- Skill: `session-handoff` v0.5.0 — «Headless (pi)» section
|
||||
- pi docs: `extensions.md` — lifecycle diagram, `sendUserMessage` (deliverAs/triggerTurn), mode table
|
||||
|
||||
@@ -1,62 +1,3 @@
|
||||
# Wiki Index
|
||||
|
||||
Catalog of all wiki pages. One line per page, organized by type. Updated on every ingest / new page.
|
||||
|
||||
## Overview
|
||||
|
||||
- [overview.md](overview.md) — what claude-skills is, layout, how to navigate
|
||||
|
||||
## Entities
|
||||
|
||||
<!-- (none yet) -->
|
||||
|
||||
## Concepts
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- [active-platform-decision.md](concepts/active-platform-decision.md) — why `active-platform` is a skill (not a memory entry); why default = Windows; how it's wired into `project-bootstrap`
|
||||
- [bootstrap-claude-md-merge.md](concepts/bootstrap-claude-md-merge.md) — project-bootstrap@1.3.0 — Step 5 upgrade path becomes idempotent merge (read → diff vs template → confirm → append missing); fixes silent gap where pre-1.2.0 projects never picked up new canonical triggers (`check across all projects`, `we're on Windows`)
|
||||
- [bootstrap-skill-deps-check.md](concepts/bootstrap-skill-deps-check.md) — project-bootstrap@1.7.0 — Step 5.6 collapses the per-skill "detect-and-recommend" mirror shape into one generic `trigger → fulfiller` table walker (skill vs plugin kind, never auto-install); subsumes the deferred `[bootstrap-recommend-projects-meta]` and the existing `superpowers`-only detector
|
||||
- [bootstrap-manifest.md](concepts/bootstrap-manifest.md) — record of which `project-bootstrap` / `setup-wiki` / `setup-tasks` versions initialized this project's `.wiki/` and `.tasks/` layout (overwritten on re-bootstrap; history in git)
|
||||
- [build-notes.md](concepts/build-notes.md) — why `build.ps1` exists alongside `build.sh`; PS 5.1 backslash-in-zip gotcha; how to extract a `.skill`
|
||||
- [install-cross-platform.md](concepts/install-cross-platform.md) — paired-script parity contract for `install.{ps1,sh}` AND `build.{ps1,sh}`; rationale for the `--prune` / `-Prune` flag (combined-with-action, global-scan, default-off); install-side prunes target dirs, build-side prunes `dist/*.skill` files
|
||||
- [install-portability.md](concepts/install-portability.md) — `install.sh` / `build.sh` rewritten to drop `mapfile` (bash 4+) and `find -printf` (GNU only) so stock macOS (bash 3.2 + BSD find) works
|
||||
- [context7-setup.md](concepts/context7-setup.md) — context7 CLI-first (2026-08-12): `ctx7` CLI + key in `~/.config/projects-secrets/ctx7.env`, plugin `context7@claude-plugins-official` + manual MCP entries removed; setup-context7 skill (one-time install/migrate, confirmation gates) + using-context7 policy; plugin era = rollback reference
|
||||
- [projects-meta-skills.md](concepts/projects-meta-skills.md) — `setup-projects-meta` + `using-projects-meta` skill pair for the local `projects-meta-mcp` stdio server (cross-project tasks + shared Gitea wiki); local-first rule + two-step mutation pattern
|
||||
- [project-discipline-design.md](concepts/project-discipline-design.md) — design for project-discipline (four cross-project rules: conventions-over-defaults, master-only, semver-bumping, ask-before-push)
|
||||
- [pulling-before-work-design.md](concepts/pulling-before-work-design.md) — design for the pulling-before-work skill (mode-3 + skip-on-dirty)
|
||||
- [repo-layout.md](concepts/repo-layout.md) — flat `skills/`, committed `dist/`, bash + PowerShell scripts; install model
|
||||
- [skill-versioning.md](concepts/skill-versioning.md) — why infra skills carry `version: <semver>` in frontmatter and how `project-bootstrap` records them in a per-project manifest
|
||||
- [skill-vs-plugin.md](concepts/skill-vs-plugin.md) — when a bare SKILL.md is enough vs when you actually need a plugin (slash commands, hooks, sub-agents, MCP servers); concrete breakdown of `superpowers`
|
||||
- [wiki-realignment.md](concepts/wiki-realignment.md) — fixing `project-bootstrap` to create the Karpathy-canonical wiki layout
|
||||
- [interns-design](concepts/interns-design.md) — interns-design
|
||||
- [compress-dedup.md](concepts/compress-dedup.md) — `skills/compress/` deleted as a byte-identical dupe of `skills/caveman-compress/`; canonical kept for README + SECURITY + caveman-toolkit branding; better Process-step wording ported across; `version: 1.0.0` added to caveman-compress frontmatter
|
||||
- [active-platform-eval-design.md](concepts/active-platform-eval-design.md) — spec for eval-driven tuning of `active-platform`: combine the two ⚪ tasks into one workstream, 20-query cross-platform eval set (≥3 per OS + near-miss negatives), `run_loop.py` autoloop **in parallel** with manual body sweep (WSL / BSD / ambiguity), version 1.0.0 → 1.1.0 (MINOR). Status: paused after design + pre-flight check, before eval-set authorship
|
||||
- [interns-repo-read-design](concepts/interns-repo-read-design.md) — interns-repo-read-design
|
||||
- [hermes-skills-rollout-design](concepts/hermes-skills-rollout-design.md) — hermes-skills-rollout-design
|
||||
- [tdd-criteria-design](concepts/tdd-criteria-design.md) — tdd-criteria-design
|
||||
- [project-bootstrap-meta-isolation.md](concepts/project-bootstrap-meta-isolation.md) — project-bootstrap@1.11.0 — Step 1 ships meta-isolation block in `.gitignore` (`!.claude/`, `!.tasks/`, `!.wiki/`, ...) so own greenfield/upgrade projects re-enable agent meta-paths against global `core.excludesFile` cutter. Marker-based append-only on existing files; smoke-tested with negative control
|
||||
- [interns-grep-audit-design](concepts/interns-grep-audit-design.md) — interns-grep-audit-design
|
||||
- [session-handoff-skill-design.md](concepts/session-handoff-skill-design.md) — design rationale for the `session-handoff` skill (sliding overwrite into `.tasks/NEXT_SESSION.md`, phrase whitelist + substantive-commit heuristic, optional PostToolUse hook for harness-side determinism, orient+ask default, project scope, cluster 7/7 closure)
|
||||
- [using-tasks-session-break.md](concepts/using-tasks-session-break.md) — `using-tasks` v1.2.0 `session_break` marker: task-author-set boolean/string flag; after a task closes 🟢, before `tasks_claim_next`, an autonomous agent prints the verbatim SESSION BOUNDARY line and stops instead of chaining the next task. Absent → unchanged
|
||||
- [delegate-task-session-break.md](concepts/delegate-task-session-break.md) — `delegate-task` v0.2.2 — authoring side of the `session_break` marker (consumer = [[using-tasks-session-break]]): pre-flight Q6 + optional template field `session_break: true | "<hint>"`; three set-it cases (domain-switch / milestone / heavy infra); not a default
|
||||
- [delegate-task-review-weight.md](concepts/delegate-task-review-weight.md) — `delegate-task` v0.2.3 — Step 5 review-task now sets explicit `weight`, inherited from impl with a `needs-claude` floor (impl `needs-human`→review `needs-human`; `cheap-ok`→`needs-claude`). Fixes the reconciler skipping weightless review tasks (root cause of manual patch `c0af151`)
|
||||
- [using-system-snapshot-design.md](concepts/using-system-snapshot-design.md) — `using-system-snapshot` v0.1.0 — thin read-only skill wrapping the single `mcp__projects-meta__meta_system_snapshot` call (poller + local docker + cached task summary); replaces scattered `tasklist`/`docker ps`/manual `meta_status`; core rule = no liveness claim without calling the tool this turn; three-line output; defers deep docker to [[using-vds-ops]] and precise tasks to [[using-projects-meta]]
|
||||
- [using-tasks-status-archival.md](concepts/using-tasks-status-archival.md) — `using-tasks` v1.3.0 done-task archival rule (≥10 🟢 → `.tasks/archive/YYYY-MM.md`) fixes STATUS.md bloat; documents why `tasks_get_status` (single-task, by slug) / `tasks_aggregate` (cross-project cache) can't replace the orientation board-read, so the literal task instruction was not followed
|
||||
- [delegate-task-negative-trigger-fp.md](concepts/delegate-task-negative-trigger-fp.md) — `delegate-task` v0.2.1 FP fix: «создать задачу себе» stem-matched the «создать задачу на агента» positive trigger; abstract "does NOT apply when doing the work yourself" carve-out loses to literal stem-match under the 1%-rule → made the negative literal + routed (→ using-tasks). Verified pos 5/5, neg 4/5 (was 0/5)
|
||||
- [using-markitdown-cli-migration.md](concepts/using-markitdown-cli-migration.md) — `using-markitdown` v1.0.0→v1.0.1 (PATCH): rewrote from the Docker-based `mcp__markitdown__convert_to_markdown` MCP tool to the native `markitdown` CLI (0.1.6, on PATH); dropped the host→container `file://` mount caveat; container decommission is by image ancestor (`--filter ancestor=markitdown-mcp:latest`), not by the non-existent name `markitdown-mcp`
|
||||
- [session-inbox-monitor-received-msg-fp.md](concepts/session-inbox-monitor-received-msg-fp.md) — sibling of [[delegate-task-negative-trigger-fp]]: `session-inbox-monitor` FP-fires on RU «обработай полученное письмо» (N1) because its literal+routed carve-out points at `inter-session-peer-discipline`, which **isn't installed** → no competitor, nearest inbox-skill wins. Borderline (neg 2/3, EN twin clean), body-load self-corrects. **Open** (follow-up task). New principle: *a routed negative competes only if its route target is installed*
|
||||
- [task-format-design.md](concepts/task-format-design.md) — new `task-format` skill v0.1.0: public reference for the on-disk `.tasks/STATUS.md` block format the poller parses (header regex, status emoji, `**Weight:**` / `**Notify:**` / `**Requirements:**`); ships with `factory` where the internal wiki/MCP-source can't reach; distinct from [[delegate-task]] (MCP-tool delegation) and [[using-tasks]] (board mechanics); RED 3-baseline / GREEN 2-verify per writing-skills; ground truth = `status-md.ts` + `claim.ts` + `fleet-router.js`
|
||||
|
||||
## Packages
|
||||
|
||||
<!-- (none yet) -->
|
||||
|
||||
## Summaries
|
||||
|
||||
<!-- (none yet) -->
|
||||
- [pi-extension-headless-ritual.md](concepts/pi-extension-headless-ritual.md) — agent_end (not agent_settled) for followUp injection; mode guard (`print` not hasUI); loop-guard flag-before-send; opt-in mirrors skill
|
||||
# ⛔ Файловый канал закрыт
|
||||
|
||||
**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`.
|
||||
|
||||
83
.wiki/log.md
83
.wiki/log.md
@@ -1,82 +1,3 @@
|
||||
# Wiki Log
|
||||
# ⛔ Файловый канал закрыт
|
||||
|
||||
Append-only operation log. One entry per operation. Format:
|
||||
|
||||
```
|
||||
## [YYYY-MM-DD] <op> | <one-line description>
|
||||
```
|
||||
|
||||
Operations: `init`, `ingest`, `query`, `lint`, `refactor`, `decision`.
|
||||
|
||||
Parseable: `grep "^## \[" .wiki/log.md | tail -20`.
|
||||
|
||||
---
|
||||
|
||||
## [2026-04-28] init | bootstrap empty wiki via project-bootstrap (old layout)
|
||||
## [2026-04-28] decision | repo-layout — flat `skills/`, committed `dist/`, bash + PS scripts
|
||||
## [2026-04-28] decision | build-notes — PS 5.1 Compress-Archive backslash bug; build.ps1 via .NET ZipArchive
|
||||
## [2026-04-28] decision | active-platform — skill chosen over global CLAUDE.md / project memory; default Windows; wired into project-bootstrap
|
||||
## [2026-04-28] refactor | wiki-realignment — fixed project-bootstrap Step 3 to create Karpathy-canonical layout
|
||||
## [2026-04-28] refactor | this repo's `.wiki/` migrated to canonical layout (SUMMARY.md→index.md, source/→concepts/, added log.md/overview.md/CLAUDE.md schema, raw/README.md)
|
||||
## [2026-04-28] decision | context7-setup — switched to official plugin; --api-key injected into plugin's .mcp.json; three manual MCP entries removed
|
||||
## [2026-04-28] decision | setup-context7 skill — formalized the install/migrate algorithm; using-context7 gets a Prerequisites pointer; build.sh PS multi-arg bug fixed (loop instead of comma-joined -Names)
|
||||
## [2026-04-28] verify | setup-context7 — Vitya ran using-context7 in a session that needed setup; Prerequisites pointer triggered setup-context7; full flow worked end-to-end. Pattern (policy + setup split) validated.
|
||||
## [2026-04-28] decision | skill-vs-plugin — documented when a bare skill suffices vs when a plugin is required (slash commands, hooks, sub-agents, MCP via marketplace)
|
||||
## [2026-04-28] decision | skill-versioning — added `version: 1.0.0` to 6 infra skills' frontmatter; project-bootstrap now writes .wiki/concepts/bootstrap-manifest.md per project
|
||||
## [2026-04-28] refactor | wiki split — `wiki-maintainer` renamed to `using-wiki` (policy); new `setup-wiki` skill owns greenfield creation and canon migration; `project-bootstrap` Step 3 delegates
|
||||
## [2026-04-28] refactor | tasks split — `task-status-wiki` renamed to `using-tasks` (policy); new `setup-tasks` skill owns greenfield + interactive migration (no auto-parsing of old flat STATUS.md); `project-bootstrap` Step 4 delegates
|
||||
## [2026-04-28] refactor | this repo's `.tasks/` migrated to canonical layout — flat `## Done`/`## Backlog` replaced by emoji-status board (7 ⚪ Ready blocks); historical Done entries dropped (preserved in git log); `.bak` ignored via .gitignore
|
||||
## [2026-04-28] cleanup | removed stale `~/.claude/skills/{wiki-maintainer,task-status-wiki}/` installs (replaced by `using-wiki`/`using-tasks`); 16 skills installed, no duplicates; context7 plugin (mcp__plugin_context7_context7__*) confirmed live after restart
|
||||
## [2026-04-28] decision | install-portability — `install.sh`/`build.sh` patched to drop `mapfile`+`find -printf`; stock macOS (bash 3.2 + BSD find) now works; verified on git-bash (16 skills discovered, sorted, installed; build.sh produces archive)
|
||||
## [2026-04-29] decision | projects-meta-skills — built `setup-projects-meta` (8-phase install of projects-meta-mcp + auth.toml + MCP registration) and `using-projects-meta` (runtime policy with local-first rule and two-step mutation); skill pair pattern applied for the 4th time (context7 / wiki / tasks / projects-meta); both built + installed; visible to the harness
|
||||
## [2026-04-30] refactor | projects-meta-skills — wiki path canon corrected: `~/projects/.wiki` → `~/projects/projects-wiki/` (clone root), content at `~/projects/projects-wiki/.wiki/`. Old path caused write/read mismatch bug (fixed upstream in commit `621a69f` of `projects-meta-mcp`). Setup-projects-meta Phase 1 now detects legacy clone, Phase 4 re-clones to canon. Lesson: pull shared resources before relying on cached anchors
|
||||
## [2026-04-30] decision | using-projects-meta v1.1.0 — added mandatory Step 0 freshness gate: probe `meta_status`; if cache_age > 10min or errors > 0, `node dist/sync.js`; for shared-wiki writes unconditional `git -C ~/projects/projects-wiki pull --ff-only`; 401/403 → loud failure to user. Codifies the same-session lesson — `projects-meta` is a multi-machine bus, stale cache breaks read accuracy and write atomicity
|
||||
## [2026-04-28] decision | project-bootstrap@1.1.0 — added Step 5.6: detects `superpowers@claude-plugins-official` via `~/.claude/plugins/installed_plugins.json` and prints install command + upstream link if missing; chat-only, never auto-installs (slash commands aren't callable from a skill, and silent plugin install is overreach)
|
||||
## [2026-04-28] doc | README.md + README.ru.md — new "Using skills in projects" / "Использование в проектах" section after install quick-start; describes project-bootstrap workflow (git, .gitignore, README, .wiki/, .tasks/, CLAUDE.md, manifest, superpowers-plugin check) and the init/upgrade modes
|
||||
## [2026-04-30] refactor | project-bootstrap re-run on this repo (upgrade mode) — setup-wiki noop, setup-tasks noop, CLAUDE.md unchanged (matches template), bootstrap-manifest.md written: project-bootstrap@1.1.0 / setup-wiki@1.0.0 / setup-tasks@1.0.0
|
||||
## [2026-04-30] decision | project-bootstrap@1.2.0 — CLAUDE.md template gains `check across all projects` (verbatim trigger from using-projects-meta description); installs auto-load cross-project tasks + shared-wiki access in every bootstrapped repo; no Step 5.7 dependency-check mirror — Prerequisites pointer in using-projects-meta is self-correcting; local CLAUDE.md, both READMEs, dist/.skill, projects-meta-skills concept page synced
|
||||
## [2026-04-30] decision | Step 5.7 mirror of Step 5.6 (projects-meta-mcp dependency detector / `setup-projects-meta` recommendation) accepted as future work; tracked as ⚪ Ready task `[bootstrap-recommend-projects-meta]`; deferred until first observed fresh-machine miss so detector signal is informed by real failure mode; concept page `projects-meta-skills.md` updated to reflect new stance
|
||||
## [2026-04-30] decision | project-bootstrap@1.3.0 — Step 5 upgrade path turned idempotent: read existing CLAUDE.md → substring-diff vs template → confirm → append-only-missing; closes silent gap where pre-1.2.0 projects never picked up new canonical triggers (`check across all projects`, `we're on Windows`); platform line preserved if user pinned a non-host one; concept page `bootstrap-claude-md-merge.md` written; README CLAUDE.md row updated to note idempotent merge
|
||||
## [2026-05-01] decision | pulling-before-work — new policy skill (v1.0.0): one `git pull --ff-only` at session start + on-demand re-sync; bootstrap template gains canonical trigger; project-bootstrap 1.3.0→1.4.0
|
||||
## [2026-05-01] decision | project-discipline — new policy skill (v0.1.0): four cross-project rules (conventions-over-defaults, master-only, semver-bumping, ask-before-push); bootstrap template gains canonical trigger; project-bootstrap 1.4.0→1.5.0; skill-versioning concept extended to all skills
|
||||
## [2026-05-01] ingest | shared-wiki packages/claude-skills — каталог всех 20 скиллов опубликован в projects-wiki (3 commits: page + index + log on Gitea, ae2cc9a..001cdd0); группировка bootstrap / wiki+tasks / MCP / caveman / discovery+platform; cross-link с concepts/setup-using-skill-pair и packages/projects-meta-mcp
|
||||
|
||||
## [2026-05-05] ingest | concepts/interns-design
|
||||
## [2026-05-05] decision | interns-skills-mvp — shipped `setup-interns` v0.1.0 (8-phase install: detect `.common/lib/interns-mcp/`, `pip install -e`, `.common/secrets/interns.env` write, `mcpServers.interns` registration with absolute Python interpreter + `cwd`) and `using-interns` v0.1.0 (runtime policy mirroring project-discipline Rule 4: ask-mode default, conversational grant/revoke, always-ask paths for `.env`/secrets/keys/SSH/credentials with transitive rule, cost-cap >$0.10, session-end reset; routing hints for `bulk_text_read` + `transcript_distill`); `project-bootstrap` 1.5.0→1.6.0 with canonical CLAUDE.md trigger `delegate to interns when allowed` between `follow project discipline` and `we're on Windows`, Step 5 commentary paragraph, manifest table extended with both new skills + `project-discipline` row; root `CLAUDE.md` dogfood updated; both READMEs written; descriptions verified (setup-interns 899 chars, using-interns 814 chars, both under 900 budget); all three rebuilt + installed + listed by harness with full descriptions (no H1 fallback)
|
||||
## [2026-05-05] ingest | concepts/bootstrap-skill-deps-check
|
||||
## [2026-05-05] decision | bootstrap-skill-deps-check — `project-bootstrap` 1.6.0→1.7.0 collapses Step 5.6 from a single-skill detector (only `superpowers` plugin) into a generic `trigger → fulfiller` table walker. Map embedded in SKILL.md (9 rows: caveman, superpowers plugin, using-wiki, using-tasks, using-projects-meta, pulling-before-work, project-discipline, using-interns, active-platform); `kind: skill` vs `kind: plugin` flag drives the install command emitted in the recommendation block. Algorithm: read project's CLAUDE.md → match each line vs map (substring + tolower, mirrors Step 5 idempotent merge) → for each canonical match check disk (`~/.claude/skills/<name>/SKILL.md` or `installed_plugins.json` key); print one chat-only block listing every missing fulfiller + install commands, or one ✅ line if all satisfied. User-custom lines silently skipped; removed canonical lines silently skipped (respects user opt-out). Hard rule "never auto-install" carries over verbatim. Subsumes the deferred `[bootstrap-recommend-projects-meta]` task (closed by absorption — generic step handles `using-projects-meta` along with everything else). MCP-server-backed skills only check the `using-X` policy skill; `setup-X` self-fires on first use via Prerequisites pointer, bootstrap doesn't duplicate.
|
||||
## [2026-05-05] decision | compress-dedup — `skills/compress/` was a stripped-down byte-for-byte dupe of `skills/caveman-compress/` (scripts/ identical SHA256 across all 7 files; SKILL.md diff = `name:` + Process step 2; descriptions textually identical = arbitrary harness tie-break + double-counted listing budget). Kept `caveman-compress` canonical: it carries README.md (benchmarks table + caveman-toolkit branding) and SECURITY.md (Snyk false-positive writeup), and matches the caveman-* prefix invariant. Ported the better Process-step wording from `compress` into `caveman-compress` (`cd <directory_containing_this_SKILL.md>` instead of brittle `cd caveman-compress` which assumes cwd). Added `version: 1.0.0` to caveman-compress frontmatter (first versioned release; aligns with skill-versioning concept). Deleted: `skills/compress/`, `dist/compress.skill`, `~/.claude/skills/compress/` (manual prune — install.sh has no prune step; future `[install-ps1]` task should add `--prune` flag). Rebuilt + reinstalled `caveman-compress`. Slash-command impact: `/compress` removed; `/caveman-compress` + `/caveman:compress` (toolkit-canonical) remain. Concept page `concepts/compress-dedup.md` written (rationale + rejected alternatives: alias-stub has no harness mechanism; "keep both" wastes listing budget; "delete caveman-compress" loses README + SECURITY).
|
||||
## [2026-05-05] design | active-platform-eval (paused) — combined `[active-platform-tuning]` + `[active-platform-eval]` into one workstream (eval *is* the tuning mechanism; "wait for 5 real signals" was a placeholder). Spec written at `.wiki/concepts/active-platform-eval-design.md`: 20-query trigger eval set balanced ≥3 should-trigger per OS (Win/Lin/Mac) + near-miss negatives, run in `skill-creator/scripts/run_loop.py` (5 iter, train/test split, model `claude-opus-4-7`) **in parallel** with manual body sweep (WSL clarity, BSD/macOS expansion, ambiguity policy). Workspace at `.tasks/active-platform-eval/` (eval-set.json committed, iterations gitignored). Version bump 1.0.0 → 1.1.0 planned (MINOR). Pre-flight verified: `claude` CLI at `C:\nvm4w\nodejs\claude.ps1` (Claude Code 2.1.128) + `run_loop.py` present in skill-creator install — both autoloop deps satisfied, no fallback needed. Per-task file at `.tasks/active-platform-eval.md`. Paused at user request before eval-set authorship; resume point is Q2 (write 20 queries solo vs run skill-creator HTML-review template for user edits first). Also fixed in same pause: `[install-ps1]` STATUS scope expanded to "paired install.sh + install.ps1, cross-platform parity, --prune flag" (lesson from `[compress-dedup]`).
|
||||
|
||||
## [2026-05-05] ingest | concepts/interns-repo-read-design
|
||||
|
||||
## [2026-05-06] ingest | concepts/hermes-skills-rollout-design
|
||||
|
||||
## [2026-05-07] ingest | concepts/tdd-criteria-design
|
||||
|
||||
## [2026-05-07] review | tdd-criteria v0.2.0 — 4 findings applied: trigger-loophole fix (removed session-authorship clause), composite-tasks + refactoring sections, expanded file-extension list, clarified wrapper line-count + spike-survivor fallback + foreign-schema fix; design doc synced
|
||||
|
||||
## [2026-05-10] decision | project-bootstrap-meta-isolation — v1.11.0 ships meta-isolation block in `.gitignore` template + Step 1 upgrade-case append; restores agent meta-paths visibility against global `core.excludesFile`; smoke-tested greenfield + negative control + idempotency
|
||||
|
||||
## [2026-05-22] ingest | concepts/interns-grep-audit-design
|
||||
|
||||
## [2026-05-25] decision | session-handoff-skill-design — design rationale for the `session-handoff` skill captured in wiki after cluster 7/7 closure; sliding overwrite of `.tasks/NEXT_SESSION.md`, phrase whitelist + substantive-commit heuristic, opt-in PostToolUse hook, orient+ask default, source: `~/projects/.workshop/.archive/2026-05-24-session-handoff-skill.md` Round 1 + Round 2
|
||||
|
||||
## [2026-05-25] decision | install-cross-platform — `install.{ps1,sh}` paired-script parity contract documented; `--prune` / `-Prune` flag rationale (combined-with-install, global-scan ignores names filter, default-off, print-and-delete no prompt); shipped in commit `6cf0e98` with `[skip-tdd: wrapper]` carve-out + smoke-test evidence; closes 2/3 of `[install-ps1]` acceptance (the doc + flag), `dist/`-prune analogue deferred to `build` scripts
|
||||
|
||||
## [2026-05-25] decision | install-cross-platform extended to build scripts — `build.{ps1,sh}` get the symmetric `--prune` / `-Prune` flag (removes `dist/<name>.skill` where `<name>` is not in `skills/`). Bash delegation to `powershell.exe -File build.ps1` does NOT forward the flag — bash runs prune itself against the shared `dist/`. Both paths smoke-tested with fake stale .skill files against real dist/. Closes `[install-ps1-build-prune-followup]`.
|
||||
|
||||
## [2026-06-09] decision | delegate-task-negative-trigger-fp — `delegate-task` 0.2.0→0.2.1 (PATCH): fixed 5/5-consistent false-positive on «создать задачу себе». Root cause: self-task phrase shares stem «создать задачу» with the «создать задачу на агента» positive trigger; the abstract "Does NOT apply when doing the work yourself" carve-out can't beat a literal stem-match under the 1%-rule. Fix: made the negative literal + routed («создать задачу себе» / «task for myself» / «поставить себе задачу» → using-tasks) in description + body disambiguator («на агента»/«агенту» = delegate; «себе» = own board). Re-verified via fresh-context subagent trigger run: positives 5/5 (no regression), negative 4/5 → using-tasks (was 0/5); the 1 residual miss was an eval-harness artifact (forced skill-name-before-reasoning), not description ambiguity. Concept page written; reusable principle = put the exact colliding negative phrase with an explicit →sibling route, literal beats abstract.
|
||||
## [2026-06-09] decision | delegate-task-session-break — `delegate-task` 0.2.1→0.2.2 (PATCH): authoring side of the `session_break` marker (consumer = using-tasks v1.2.0). Added pre-flight Q6 (after notify): "Session-break после этой задачи? (domain-switch / milestone / heavy infra)"; if yes → set optional template field `session_break: true | "<hint>"` (trailer, next to weight/notify/allow_upgrade; same lowercase frontmatter key using-tasks reads). Usage guidance lists three set-it cases; What-NOT-to-do bullet warns against setting it routinely (it's a real-boundary marker, not a default). Wiki concept page concepts/delegate-task-session-break.md + index. Pairs with using-tasks-session-break.
|
||||
## [2026-06-09] decision | using-system-snapshot — new skill v0.1.0: thin read-only wrapper over the single `mcp__projects-meta__meta_system_snapshot` call (poller status + local docker containers + cached cross-project task summary). Replaces the scatter of `tasklist` + `docker ps` + manual `meta_status`. Core rule: no claim about poller / local-docker / task-load state without calling the tool in the current turn (memory + stale earlier snapshot ≠ evidence). Output = three lines, one per section (docker lists only problem containers; tasks gives Σ active/blocked + busiest 2–3). Liveness split documented: poller+docker live, tasks from cache (defer precise work to using-projects-meta Step 0). Scope boundaries: deep single-container diagnosis → using-vds-ops / `docker logs`; docker section is LOCAL, not the VDS. Read-only, no per-session grant (mirrors using-vds-ops). Output shape verified by a live call 2026-06-09. Concept page concepts/using-system-snapshot-design.md + index. TDD N/A (markdown policy artifact); behavioral smoke-test = paired skill-using-system-snapshot-review task.
|
||||
## [2026-06-09] review | using-system-snapshot v0.1.0 — VERDICT PASS on all 3 acceptance criteria (skill-using-system-snapshot-review). Tool contract verified by a live `meta_system_snapshot` call (output matches the documented `poller`/`docker`/`tasks` shape exactly). Behavioral trigger smoke = 9 fresh-context subagents over a simulated registry (real descriptions + using-vds-ops/using-projects-meta/using-tasks competitors, no expected-answer hint): 4/4 positives → using-system-snapshot; VDS-logs → using-vds-ops; mutate/full-board → using-projects-meta; `docker-compose.yml` edit → none (no FP on "docker" keyword). No-claim-without-snapshot rule explicit in 4 places; three-line output format confirmed achievable against the live payload. 3 informational findings (none blocking): (1) cross-project task-COUNT phrasings overlap with using-projects-meta — by-design, snapshot defers precise per-task work; (2) LOCAL-container deep diagnosis is unowned — vds-ops incident triggers grab local containers its VDS-only tools can't reach (vds-ops scoping, not this skill); (3) deployment scaffold missing — skill committed but not installed to `~/.claude/skills/`, not in `hermes/mapping.yaml`, no -install/-hermes-mapping/-test-trigger baseline tasks; recommended follow-ups (hermes mode could be `auto`, read-only skill). Review outcome appended to concepts/using-system-snapshot-design.md.
|
||||
## [2026-06-09] decision | using-tasks-status-archival — `using-tasks` 1.2.0→1.3.0 (MINOR): added done-task archival rule to fix STATUS.md bloat ("huge STATUS.md" complaint). When ≥10 🟢 done blocks pile up — checked at session start (step 7) and after close (Task completion step 7) — move them verbatim to `.tasks/archive/YYYY-MM.md` (append, one file per month, one-time header), leaving only 🔴/🟡/⚪/🔵 on the board; committed on its own. Did NOT follow the task's literal instruction to replace `Read STATUS.md` with `tasks_get_status` for orientation: that tool returns a single task's live status by known slug (`{status, found}`) and cannot enumerate the board, and `tasks_aggregate` is cross-project + cache-based + doesn't index ready/done (its docs say read STATUS.md directly for the current project). So orientation stays a local board-read (kept cheap by archival); skill now warns against both tools for board enumeration and points `tasks_get_status` at its real single-task use. Core goal (kill the bloat) met by archival alone. Concept page concepts/using-tasks-status-archival.md + index. TDD N/A (markdown policy). Deviation flagged for paired review task using-tasks-status-read-perf-review.
|
||||
## [2026-06-09] decision | using-tasks-session-break — `using-tasks` 1.1.0→1.2.0 (MINOR): added the `session_break` marker. Task author sets `session_break: true | "<hint>"` in task frontmatter (mirrored as `**Session break:**` on the local board); after the task closes 🟢, before `tasks_claim_next`, an autonomous agent prints the verbatim line `🔚 SESSION BOUNDARY — [slug] закрыта. Рекомендую завершить текущую сессию. Следующий трек: [value | "см. STATUS.md"]` and stops instead of chaining the next task. Absent → behaviour unchanged. Enforced in Task completion step 6 + Rules bullet + format docs. Marker not heuristic: the stop-point is an authoring choice, not a runner guess.
|
||||
## [2026-06-09] review | using-tasks-status-archival v1.3.0 — VERDICT PASS 3/3 (using-tasks-status-read-perf-review). Criterion «ориентация через `tasks_get_status`, не Read» is satisfied by a **validated deviation**, not a literal swap: re-verified against the live tool schema that `tasks_get_status(target_project, slug)→{status, found}` takes a required slug and returns ONE task — it cannot enumerate the board, so it cannot drive orientation; the implementer correctly rejected the impossible instruction and fixed the real problem (bloat→archival). No regression: orientation still reads local STATUS.md (Session start §2) and the «what's next» flow still reads the board — change is purely additive. Archival rule clear & complete (≥10 threshold, two trigger points, monthly append-only archive, verbatim blocks, dedicated commit, cross-referenced). One informational non-blocking note: this repo's own STATUS.md (>10 🟢) would itself trip the rule — dogfooding tracked separately as tasks-board-cleanup-2026-05. No follow-up tasks. Verdict appended to concepts/using-tasks-status-archival.md.
|
||||
## [2026-06-09] decision | delegate-task-review-weight — `delegate-task` 0.2.2→0.2.3 (PATCH): Step 5 (paired `<slug>-review` task) now sets an explicit `weight`, inherited from the impl-task with a `needs-claude` floor (impl `needs-human`→review `needs-human`; `needs-claude`→`needs-claude`; `cheap-ok`→`needs-claude`). Root cause of commit `c0af151` ("add Weight: needs-claude to 4 review tasks — reconciler was skipping them"): the authoring skill omitted `weight` on review tasks, making them invisible to fleet routing. Floor (not pure inheritance) chosen to stay internally consistent with the skill's own "What NOT to do" bullet that forbids `cheap-ok` for review tasks — a `cheap-ok` impl would otherwise propagate a forbidden `cheap-ok` review. Added a What-NOT-to-do bullet against weightless review tasks. Concept page concepts/delegate-task-review-weight.md + index. TDD N/A (markdown policy artifact).
|
||||
## [2026-06-11] decision | task-format — new skill v0.1.0: public reference for the `.tasks/STATUS.md` task-block format the autonomous poller parses. Motivation: the field rules (`**Weight:**` capability/cost tier, `**Notify:** <owner>/<repo>` inbox target, header regex, status emoji) lived only in internal sources (`projects-meta-mcp/src/lib/status-md.ts` parser + `status-md-writer.ts` + `.common/.wiki/concepts/agents-task-runner-ops.md`); skills ship with `factory` to external users, the wiki/MCP-source don't. Scope kept distinct from delegate-task (creates tasks for others via `tasks_create`, the tool emits the format) and using-tasks (board claim/close mechanics) — task-format is the byte-level field reference for hand-edited blocks. Ground truth verified against source: header `/^##\s+(\S+)\s+\[([^\]]+)\]\s+—\s+(.+)$/u`; Weight ∈ {cheap-ok, needs-claude, needs-human}; claim gate excludes only `needs-human` (`claim.ts`), but a *missing* Weight finds no backend tier (`fleet-router.js` resolveBackend) → poller parks to 🔵 blocked, so Weight is operatively required for pickup. TDD per writing-skills: RED = 3 baseline subagents w/o skill (2/3 used `###`/bullet headers the parser can't recognize, 2/3 omitted Weight inventing `risk`/`tier`/`claimable-by`, 2/3 put notify in prose, 1/3 used 🟢 for ready); GREEN = 2 fresh subagents w/ skill, both parser-valid incl. correct `needs-human` for the critical-infra scenario; REFACTOR = no new loopholes. Reference skill ~900 words (loads only when authoring a task block). Concept page concepts/task-format-design.md + index. Not yet installed to `~/.claude/skills/` or added to hermes mapping — deferred follow-up (mirrors using-system-snapshot deployment-scaffold note).
|
||||
## [2026-06-09] decision | using-markitdown-cli-migration — `using-markitdown` 1.0.0→1.0.1 (PATCH): rewrote the skill from the Docker-based `mcp__markitdown__convert_to_markdown` MCP tool to the native `markitdown` CLI (v0.1.6, on PATH). Tool block now `markitdown <path|url>` → stdout (or `-o file`); removed the whole "Docker-mount caveat (READ FIRST)" section (host→container `file://` translation + `[Errno 2] /c:/Users/...` symptom are gone — CLI sees the full host FS). Updated the ingest pattern (use `-o` straight into `.wiki/raw/`), the gotchas table (`command not found` → check `markitdown --version`, install `pip install markitdown[all]`; dropped the MCP "tool not available / ToolSearch" row), and the contrast-table header (CLI, not MCP). Description frontmatter (the WHEN-to-use triggers) left unchanged. Container decommission: the task's literal `docker stop/rm markitdown-mcp` had no target — no container is named that; the MCP spawns anonymously-named containers from `markitdown-mcp:latest` per session (3 had piled up). Removed all by image ancestor (`docker rm -f $(docker ps -aq --filter "ancestor=markitdown-mcp:latest")`), verified none remain. Left the `mcpServers.markitdown` entry in `~/.claude.json` untouched (out of scope; a container will respawn next session until it's deregistered — flagged as a follow-up). Concept page concepts/using-markitdown-cli-migration.md + index. TDD N/A (markdown skill).
|
||||
## [2026-06-17] decision | session-inbox-monitor-received-msg-fp — finding from `session-inbox-monitor-test-trigger` (VERDICT PASS, clean session, 7 unprimed clean-context subagents: pos 4/4 incl. CLAUDE.md-line P4, neg 2/3). The 1 FP: RU «обработай полученное письмо из инбокса» (N1) routed to `session-inbox-monitor`; the EN twin (N3) and the multi-machine-backend negative (N2) routed to `none` cleanly. Root cause = a new dimension on top of [[delegate-task-negative-trigger-fp]]: the carve-out is already literal+routed (`NOT for handling a received message → inter-session-peer-discipline`), but the route target `inter-session-peer-discipline` is **not installed** → no real competitor, so the nearest in-domain skill (session-inbox-monitor) wins by default; non-deterministic, self-corrects on body-load (cost = one wasted skill-load, not a wrong action; isomorphic to [[using-tasks-session-break]] session_break). New page concepts/session-inbox-monitor-received-msg-fp.md + bidirectional link from concepts/delegate-task-negative-trigger-fp.md + index. New reusable principle: a routed negative competes only if its route target is installed. Status OPEN — follow-up task session-inbox-monitor-received-msg-fp (options a: harden description / b: install sibling / c: accept informational). Not a memory entry by owner direction — knowledge belongs in the project wiki.
|
||||
## [2026-06-17] decision | session-inbox-monitor-received-msg-fp RESOLVED via option (b) — installed `inter-session-peer-discipline` (existed in sources since 2026-06-16, was not installed → exact root cause confirmed). install.ps1 -Names, byte-identical parity. FP-twin verified clean: fresh clean-context subagent on the N1 phrase now routes to inter-session-peer-discipline (IN_REGISTRY: yes), not session-inbox-monitor — carve-out now has a real competitor. session-inbox-monitor description untouched (option (a) rejected as whack-a-mole; (c) as latent hole). Governance: peer workshop proposed (b) as a "ruling"; per the freshly-installed [[inter-session-peer-discipline]] (peer = proposal not authority, scope needs human ratification) it was surfaced as a recommendation and ratified by the user — live dogfood of the skill's own purpose. concepts/session-inbox-monitor-received-msg-fp.md Status section updated open→resolved. Tail: inter-session-peer-discipline now installed but not in hermes/mapping.yaml — possible red build, flagged as separate follow-up.
|
||||
## [2026-08-12] ingest | pi-extension-headless-ritual — agent_end/mode-guard/loop-guard lessons from session-close-ritual build
|
||||
## [2026-08-13] refactor | context7-setup — concept updated to CLI-first canonical (setup-context7 v2.0.0 migration, 2026-08-12): ctx7 CLI + key in ~/.config/projects-secrets/ctx7.env, plugin + manual MCP removed; plugin era demoted to rollback reference; whoami!=key-check gotcha recorded; index entry refreshed
|
||||
**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`.
|
||||
|
||||
@@ -1,29 +1,3 @@
|
||||
---
|
||||
title: claude-skills overview
|
||||
type: overview
|
||||
updated: 2026-04-28
|
||||
---
|
||||
# ⛔ Файловый канал закрыт
|
||||
|
||||
# claude-skills — overview
|
||||
|
||||
Joint workshop where Vitya and Claude develop, test, and store Claude skills. Both editable sources (`skills/<name>/`) and built archives (`dist/<name>.skill`) live here, so a fresh machine can clone the repo and install every personal skill in one command.
|
||||
|
||||
## Components
|
||||
|
||||
- **`skills/`** — editable skill sources, one folder per skill (each with `SKILL.md` + optional `assets/`).
|
||||
- **`dist/`** — built `.skill` archives, committed so installs don't need a build toolchain on the target.
|
||||
- **`scripts/`** — `build.sh` / `build.ps1` (zip sources → archive), `install.sh` (copy sources → `~/.claude/skills/`).
|
||||
- **`.wiki/`** — Karpathy LLM Wiki for design decisions and gotchas. See [CLAUDE.md](CLAUDE.md) for schema.
|
||||
- **`.tasks/`** — task board (`STATUS.md`).
|
||||
- **`CLAUDE.md`** — repo-level agent instructions (skill triggers).
|
||||
|
||||
## Where to look
|
||||
|
||||
- New here? → [concepts/repo-layout.md](concepts/repo-layout.md), then `README.md`.
|
||||
- Working on a skill? → edit `skills/<name>/`, then `bash scripts/install.sh <name>` (or `pwsh scripts/build.ps1 <name>` to refresh the archive).
|
||||
- Tracking work? → [.tasks/STATUS.md](../.tasks/STATUS.md).
|
||||
- Made a non-trivial decision? → add a `concepts/<topic>.md` page, link from [index.md](index.md), append a line to [log.md](log.md).
|
||||
|
||||
## Cross-references
|
||||
|
||||
This page intentionally stays short. The substantive material lives in `concepts/` (decisions, gotchas) and the [index](index.md) catalog.
|
||||
**Не читать. Не править.** Канон — mappa (`mcp__mappa__*`): wiki-сущности проекта, конвенции — AGENTS-сущность. Скил: `mappa-knowledge`.
|
||||
|
||||
19
AGENTS.md
19
AGENTS.md
@@ -8,8 +8,27 @@ check across all projects
|
||||
pull remote before work
|
||||
session handoff: read on start, write on end
|
||||
inbox monitor: raise on start
|
||||
session sync: write to mappa
|
||||
follow project discipline
|
||||
follow tdd-criteria
|
||||
delegate to interns when allowed
|
||||
recommend, don't menu
|
||||
we're on Windows
|
||||
mappa sync: run on start
|
||||
|
||||
<!-- mappa:canon-block (auto; do not edit) -->
|
||||
## Mappa canon
|
||||
**Gates (canon/gates — shared):** краткая суть; полный текст — `wiki_get(slug='canon/gates', full=true)`
|
||||
- Г1. Знание и артефакты → mappa, не файлы — durable-знание и артефакты проекта живут в mappa; файловые каналы (`.brainstorm/`, `.tasks/`, `.wiki/`) и «сохранить рядом с проектом» закрыты.
|
||||
- Г2. Контракт каналов — письмо (inbox) — носитель вердиктов/указаний/находок, полным телом; комментарии на тасках — короткий след. Адресация `about`/`to`/`thread` (XOR); lifecycle `ack`/`resolve`/`cancel`.
|
||||
- Г3. Поиск до угадывания — не угадывать slug/роут: сначала `search`/`wiki_search`; `wiki_get` без project = shared; общая память — `search(mode='recall')`.
|
||||
- Г4. Адресация и слаги — рефы полными именами (`[[task:N]]`/`[[wiki:slug]]`); слаги kebab-case, латиница; номера `task:N` выдаёт сервер.
|
||||
- Г5. .mappa-гейт — папка участвует в mappa-операциях только с маркером `.mappa`; нет маркера → сказать человеку, мутации — отказ.
|
||||
- Г6. Секреты — в mappa не пишутся (422); только `secret:<path>`-рефы, значения мимо.
|
||||
- Г7. Degraded-режим — mappa недоступна: читать кэш `.mappa/` (canon/methodology/runbooks), мутации → `.mappa/pending/`; нет кэша → стоп, не импровизировать.
|
||||
- Г8. Перед работой с вики/каноном — первым действием прочитать канон-блок AGENTS.md проекта.
|
||||
- Г9. Живое состояние до заявления — статус заявлять только по свежему чтению mappa, не по памяти/кэшу/ответу create.
|
||||
**Entity → runbook (runbooks/index — shared):** task → [[runbooks/tasks]] · wiki → [[runbooks/wiki]] · inbox → [[runbooks/inbox]] · **thread** → [[runbooks/threads]] · session → [[runbooks/session]] · search → [[runbooks/search]] · issue → [[runbooks/issue]] · **intent** → [[runbooks/intent]] · requirements → [[runbooks/requirements]] · plan → [[runbooks/plan]] · comment → [[runbooks/comment]] · tag → [[runbooks/tag]] · attachment → [[runbooks/attachment]] · release → [[runbooks/release]] · brainstorm → [[runbooks/brainstorm]] · agent → [[runbooks/agent-operator]] · repo → [[runbooks/repo-commit]] · project → [[runbooks/project]] · skill → [[runbooks/skill]] · entity-слой → [[runbooks/entity]] · sched → [[runbooks/sched-telemetry]]
|
||||
**Methodology:** `methodology/kzntsv`
|
||||
**Canon version:** 4
|
||||
<!-- /mappa:canon-block -->
|
||||
|
||||
16
README.md
16
README.md
@@ -25,8 +25,8 @@ A shared workspace where Claude and I author, debug, and ship skills together:
|
||||
git clone <repo> skills
|
||||
cd skills
|
||||
bash scripts/install.sh # copies every skills/* into ~/.claude/skills/
|
||||
# or only specific ones:
|
||||
bash scripts/install.sh mappa-knowledge caveman
|
||||
# or only specific ones (mappa-* skills install from the `mappa` repo — see mappa-bootstrap):
|
||||
bash scripts/install.sh caveman tdd-criteria
|
||||
```
|
||||
|
||||
**Linux / macOS (bash):**
|
||||
@@ -35,8 +35,8 @@ bash scripts/install.sh mappa-knowledge caveman
|
||||
git clone <repo> skills
|
||||
cd skills
|
||||
bash scripts/install.sh # copies every skills/* into ~/.claude/skills/
|
||||
# or only specific ones:
|
||||
bash scripts/install.sh mappa-knowledge caveman
|
||||
# or only specific ones (mappa-* skills install from the `mappa` repo — see mappa-bootstrap):
|
||||
bash scripts/install.sh caveman tdd-criteria
|
||||
```
|
||||
|
||||
The install target can be overridden with `CLAUDE_SKILLS_DIR=/path bash scripts/install.sh`.
|
||||
@@ -52,10 +52,9 @@ project's folder and it will, in one pass:
|
||||
|
||||
- initialize `git` (if missing) and write a sane `.gitignore`
|
||||
- create a starter `README.md`
|
||||
- lay out `.wiki/` per the [Karpathy LLM Wiki pattern](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (delegated to [`setup-wiki`](skills/setup-wiki/))
|
||||
- lay out `.tasks/` with the canonical task board (delegated to [`setup-tasks`](skills/setup-tasks/))
|
||||
- register the project meta in **mappa** (wiki/task-сущности проекта; file-based `.wiki/`/`.tasks/` closed 2026-08-25)
|
||||
- write `AGENTS.md` (canon) with skill triggers (`use project wiki`, `use task management system`, `check across all projects`, `we're on Windows`) plus a `CLAUDE.md` legacy pointer
|
||||
- record the skill versions used in `.wiki/concepts/bootstrap-manifest.md` so cross-project layout drift stays debuggable
|
||||
- record the skill versions used in a mappa wiki entity (`concepts/bootstrap-manifest`) so cross-project layout drift stays debuggable
|
||||
|
||||
Two modes, picked automatically: **init** for an empty folder, **upgrade**
|
||||
for an existing project (the skill only fills the gaps and never overwrites
|
||||
@@ -119,9 +118,12 @@ an explicit `adapted-from` marker in its frontmatter.
|
||||
| `loop-me` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — workflow-spec design gate |
|
||||
| `review-kit-pi-method` | `author: ours` — pi-native spawn for clean-context review subagents |
|
||||
| `command-index` | `author: ours` — just/Makefile command-index convention (standard targets, auto-doc; idea 3/18) |
|
||||
| `code-search` | `author: ours` — rg-first code search (measured 15 min → 0s; routing: rg / git grep / interns repo_read / grep_audit) |
|
||||
| `code-review` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — two-axis + Fowler baseline; output: caveman-review format |
|
||||
| `writing-skills` | `adapted-from: obra/superpowers @ 6.2.0` (MIT) — TDD-for-skills core + ideya 8 self-skill-authoring |
|
||||
| `web-search` | `author: ours` — search_web tool (pi-extension) + policy: when to search, «без поиска» session-off |
|
||||
| `ops-browser` | `author: ours` — свой **скрытый** браузер агента: отдельный профиль + CDP (`eval`/`fetch` из страницы/скриншоты), `handoff` человеку для пароля/капчи; свой замок `ops.lock` |
|
||||
| `browser-operator` | `author: ours` — браузер ОПЕРАТОРА (его Chrome/логины): канал по харнессу (Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools`), аренда «один водитель за раз», границы «человек vs агент», рецепты тяжёлых страниц. Закрывает провал базового прогона 2026-09-11 («куки из Chrome + curl + ввод пароля» мимо канала); анонимные прогоны — `browser-cdp` |
|
||||
| `review-subagent` | `author: ours` — review_subagent tool (pi-extension): clean-context review by your own model, optional `model` override |
|
||||
| `report-mappa-issue` | `author: ours` — TEMPORARY stopgap: mappa deviation reporting (mail to `mappa` + `.workshop`) while the service is unstable; retire when stabilized |
|
||||
| all other `skills/*` | `author: ours` |
|
||||
|
||||
12
README.ru.md
12
README.ru.md
@@ -21,8 +21,8 @@
|
||||
git clone <repo> claude-skills
|
||||
cd claude-skills
|
||||
bash scripts/install.sh # копирует все skills/* в ~/.claude/skills/
|
||||
# или конкретные:
|
||||
bash scripts/install.sh mappa-knowledge caveman
|
||||
# или конкретные (mappa-* скилы ставятся из репо `mappa` — см. mappa-bootstrap):
|
||||
bash scripts/install.sh caveman tdd-criteria
|
||||
```
|
||||
|
||||
Цель установки можно переопределить переменной `CLAUDE_SKILLS_DIR=/path bash scripts/install.sh`.
|
||||
@@ -36,10 +36,9 @@ bash scripts/install.sh mappa-knowledge caveman
|
||||
|
||||
- инициализирует `git` (если ещё нет) и положит вменяемый `.gitignore`
|
||||
- создаст стартовый `README.md`
|
||||
- развернёт `.wiki/` по [паттерну Karpathy LLM Wiki](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f) (делегируется в [`setup-wiki`](skills/setup-wiki/))
|
||||
- развернёт `.tasks/` с канонической доской задач (делегируется в [`setup-tasks`](skills/setup-tasks/))
|
||||
- зарегистрирует мету проекта в **mappa** (wiki/task-сущности; файловые `.wiki/`/`.tasks/` закрыты 2026-08-25)
|
||||
- запишет `AGENTS.md` (канон) со скилл-триггерами + `CLAUDE.md`-указатель (`use project wiki`, `use task management system`, `check across all projects`, `we're on Windows`)
|
||||
- зафиксирует версии использованных скиллов в `.wiki/concepts/bootstrap-manifest.md`, чтобы дрифт раскладки между проектами оставался отлаживаемым
|
||||
- зафиксирует версии использованных скиллов в mappa wiki-сущности `concepts/bootstrap-manifest`, чтобы дрифт раскладки оставался отлаживаемым
|
||||
|
||||
Два режима, выбирается автоматически: **init** для пустой папки и **upgrade**
|
||||
для существующего проекта (скилл только дозаполняет пробелы и ничего не
|
||||
@@ -89,8 +88,11 @@ bash scripts/build.sh caveman # один
|
||||
| `loop-me` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — дизайн-гейт workflow-спец |
|
||||
| `review-kit-pi-method` | `author: ours` — pi-спавн чистых review-субагентов |
|
||||
| `command-index` | `author: ours` — конвенция just/Makefile command-index (стандартные таргеты, авто-док; идея 3/18) |
|
||||
| `code-search` | `author: ours` — rg-first код-поиск (замер: 15 мин → 0 сек; роутинг: rg / git grep / interns repo_read / grep_audit) |
|
||||
| `code-review` | `adapted-from: mattpocock/skills @ 84fdeffd` (MIT) — двухосевость + Fowler-база; формат вывода: caveman-review |
|
||||
| `writing-skills` | `adapted-from: obra/superpowers @ 6.2.0` (MIT) — TDD-for-skills ядро + идея 8 self-skill-authoring |
|
||||
| `ops-browser` | `author: ours` — свой скрытый браузер агента (профиль + CDP + `handoff` человеку, замок `ops.lock`) |
|
||||
| `browser-operator` | `author: ours` — браузер ОПЕРАТОРА (его Chrome/логины): канал по харнессу (Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools`), аренда «один водитель за раз», границы «человек vs агент»; анонимные прогоны — `browser-cdp` |
|
||||
| остальные `skills/*` | `author: ours` |
|
||||
|
||||
Политика адаптации: клон переписывается под наши конвенции (доски `.tasks/`,
|
||||
|
||||
BIN
dist/browser-cdp.skill
vendored
BIN
dist/browser-cdp.skill
vendored
Binary file not shown.
BIN
dist/browser-operator.skill
vendored
Normal file
BIN
dist/browser-operator.skill
vendored
Normal file
Binary file not shown.
BIN
dist/code-search.skill
vendored
Normal file
BIN
dist/code-search.skill
vendored
Normal file
Binary file not shown.
BIN
dist/mappa-brainstorm-promote.skill
vendored
BIN
dist/mappa-brainstorm-promote.skill
vendored
Binary file not shown.
BIN
dist/mappa-closing-ritual.skill
vendored
BIN
dist/mappa-closing-ritual.skill
vendored
Binary file not shown.
BIN
dist/mappa-delegation.skill
vendored
BIN
dist/mappa-delegation.skill
vendored
Binary file not shown.
BIN
dist/mappa-knowledge.skill
vendored
BIN
dist/mappa-knowledge.skill
vendored
Binary file not shown.
BIN
dist/mappa-messaging.skill
vendored
BIN
dist/mappa-messaging.skill
vendored
Binary file not shown.
BIN
dist/mappa-session-orient.skill
vendored
BIN
dist/mappa-session-orient.skill
vendored
Binary file not shown.
BIN
dist/mappa-task-work.skill
vendored
BIN
dist/mappa-task-work.skill
vendored
Binary file not shown.
BIN
dist/mappa-vitya-brainstorming.skill
vendored
Normal file
BIN
dist/mappa-vitya-brainstorming.skill
vendored
Normal file
Binary file not shown.
BIN
dist/mappa-vitya-project-discipline.skill
vendored
Normal file
BIN
dist/mappa-vitya-project-discipline.skill
vendored
Normal file
Binary file not shown.
BIN
dist/ops-browser.skill
vendored
Normal file
BIN
dist/ops-browser.skill
vendored
Normal file
Binary file not shown.
BIN
dist/project-bootstrap.skill
vendored
BIN
dist/project-bootstrap.skill
vendored
Binary file not shown.
BIN
dist/project-discipline.skill
vendored
BIN
dist/project-discipline.skill
vendored
Binary file not shown.
BIN
dist/report-mappa-issue.skill
vendored
BIN
dist/report-mappa-issue.skill
vendored
Binary file not shown.
BIN
dist/review-kit-pi-method.skill
vendored
BIN
dist/review-kit-pi-method.skill
vendored
Binary file not shown.
BIN
dist/session-health.skill
vendored
BIN
dist/session-health.skill
vendored
Binary file not shown.
BIN
dist/update-skills.skill
vendored
BIN
dist/update-skills.skill
vendored
Binary file not shown.
BIN
dist/using-markitdown.skill
vendored
BIN
dist/using-markitdown.skill
vendored
Binary file not shown.
BIN
dist/writing-skills.skill
vendored
BIN
dist/writing-skills.skill
vendored
Binary file not shown.
@@ -55,6 +55,14 @@ skills:
|
||||
mode: auto
|
||||
category: software-development
|
||||
|
||||
browser-operator:
|
||||
mode: auto
|
||||
category: software-development
|
||||
|
||||
ops-browser:
|
||||
mode: auto
|
||||
category: software-development
|
||||
|
||||
using-markitdown:
|
||||
mode: auto
|
||||
category: productivity
|
||||
|
||||
@@ -44,7 +44,7 @@ function New-SkillArchive {
|
||||
[System.IO.Compression.ZipArchiveMode]::Create
|
||||
)
|
||||
try {
|
||||
$files = Get-ChildItem -Path $sourceFull -Recurse -File
|
||||
$files = Get-ChildItem -Path $sourceFull -Recurse -File | Where-Object { $_.FullName -notmatch '__pycache__' }
|
||||
foreach ($file in $files) {
|
||||
$rel = $file.FullName.Substring($sourceFull.Length + 1) -replace '\\','/'
|
||||
$entryName = "$SkillName/$rel"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
---
|
||||
name: browser-cdp
|
||||
author: ours
|
||||
version: 0.1.0
|
||||
version: 0.1.1
|
||||
description: >
|
||||
Веб-автоматизация через минимальные CDP CLI-тулы в bash — вместо playwright-mcp
|
||||
/ Chrome-DevTools-MCP (подход «what if you don't need MCP»). Запуск Chrome с remote
|
||||
debugging, навигация, eval JS, скриншоты. Trigger: «браузер», «скрейпинг», «открой
|
||||
страницу», «перейди на», «сделай скриншот», «playwright», «веб-автоматизация»,
|
||||
«web scraping», «browser».
|
||||
«web scraping», «browser». Для ЛИЧНЫХ КАБИНЕТОВ оператора (его логины, антибот) —
|
||||
НЕ этот скил, а `browser-operator`.
|
||||
---
|
||||
|
||||
# browser-cdp
|
||||
@@ -20,6 +21,9 @@ description: >
|
||||
снять скриншот, собрать данные (скрейпинг). Использовать **вместо** playwright-mcp или
|
||||
Chrome-DevTools-MCP.
|
||||
|
||||
- ⚠️ **Для личных кабинетов оператора этот путь НЕ годится:** здесь свой Chrome и свой
|
||||
профиль (без его логинов). Нужен браузер оператора — скил `browser-operator`.
|
||||
|
||||
## Процесс
|
||||
|
||||
1. **Прочитай полную справку** (обязательно, первый шаг):
|
||||
|
||||
128
skills/browser-operator/SKILL.md
Normal file
128
skills/browser-operator/SKILL.md
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
name: browser-operator
|
||||
author: ours
|
||||
version: 0.1.0
|
||||
description: >
|
||||
Работа в браузере ОПЕРАТОРА — его Chrome, его профиль, его живые логины: личные
|
||||
кабинеты поставщиков (ЧипДип, ДКО, Промэлектроника, ТМ), Avito, порталы под
|
||||
антиботом. Trigger: «зайди в личный кабинет», «открой ЛК», «собери заказы»,
|
||||
«посмотри в браузере», «нужна его сессия», «ЧипДип/Ozon/Avito», «browser».
|
||||
НЕ для публичных страниц (там сначала обычный fetch/curl) и НЕ для анонимных
|
||||
прогонов (там скил browser-cdp).
|
||||
---
|
||||
|
||||
# Browser operator — браузер оператора
|
||||
|
||||
**Браузер оператора — разделяемый ресурс с его живыми сессиями.** Работай через
|
||||
готовый канал своего харнесса и **только под арендой «один водитель за раз»**.
|
||||
Не вытаскивай его куки, не логинься сам, не поднимай свой Chrome.
|
||||
|
||||
## Когда браузер действительно нужен
|
||||
|
||||
Сначала спроси себя, нужен ли браузер вообще:
|
||||
|
||||
- **Нет** — публичная страница, API, доки: обычный fetch/curl/поиск. Браузер тут лишний.
|
||||
- **Да** — страница требует взаимодействия (клик/форма/навигация), JS-рендер,
|
||||
**его логин** (личный кабинет, Avito), либо портал режет не-браузерный трафик.
|
||||
|
||||
## Канал по харнессу (не изобретай свой)
|
||||
|
||||
| Харнесс | Чем работать | Аренда |
|
||||
|---|---|---|
|
||||
| **Hermes** | тул `browser_exec` (демон `browser-use`, тот же профиль оператора) | автоматически: shell-хук `pre_tool_call` берёт аренду и блокирует вызов при чужой |
|
||||
| **pi** | тул `browser`: `open` / `read` / `js` / `click` / `screenshot` / `lease` | автоматически внутри тула; `lease` покажет, кто держит |
|
||||
| **Claude Code** | MCP `chrome-devtools` (`list_pages`, `navigate_page`, `take_snapshot`, `evaluate_script`, `click`, `fill`, `take_screenshot`) | автоматически: обёртка берёт аренду перед стартом сервера |
|
||||
|
||||
**Никогда:** `curl` с куками из его профиля, `browser_cookie3`-выгрузка куки,
|
||||
свой `chrome --remote-debugging-port` с пустым профилем (там нет его логинов),
|
||||
`pip install`-стек ради одного кабинета.
|
||||
|
||||
## Аренда: «один водитель в браузере за раз»
|
||||
|
||||
Браузер один. Если его держит другой харнесс — **не ломиться**, сказать «занято,
|
||||
держит X» и вернуться позже (или попросить оператора освободить).
|
||||
|
||||
Идёшь в браузер **в обход** тула (например, скриптом или `browser-use` из терминала) —
|
||||
бери аренду сам:
|
||||
|
||||
```bash
|
||||
BL="$HOME/.config/browser-harness/bin/browser-lease.sh"
|
||||
HOLD="manual:$$" # метка держателя
|
||||
MYPID="$(cat /proc/$$/winpid 2>/dev/null || echo 0)" # НАСТОЯЩИЙ windows-PID
|
||||
bash "$BL" acquire --holder "$HOLD" --ttl 900 --pid "$MYPID" --tool browser # 0 = взял, 3 = занято, 2 = ошибка
|
||||
# … работа …
|
||||
bash "$BL" release --holder "$HOLD"
|
||||
```
|
||||
|
||||
**PID — только настоящий.** MSYS `$$` это НЕ windows-PID: по нему живость аренды врёт
|
||||
(мёртвый держатель выглядит живым, живой — мёртвым). В bash бери `/proc/$$/winpid`,
|
||||
в pi/Node — `process.pid`; не знаешь — передай `0` («неизвестен», живость решит TTL).
|
||||
|
||||
**`driver.lock` руками не трогай** (в том числе пустой или «битый» — это окно чужой
|
||||
записи): отбор мёртвой аренды делает CLI по `ts`/TTL/мёртвому PID. Чужую аренду не снимай.
|
||||
|
||||
Контракт аренды (формат файла, TTL, кого связывать): вики mappa
|
||||
`concepts/browser-lease-contract`.
|
||||
|
||||
## Как работать в страницах (рецепты)
|
||||
|
||||
- **Первым делом — своя вкладка.** `ensure_real_tab()` / `new_tab(url)`; не полагайся
|
||||
на активную вкладку оператора: тяжёлая или аудио-страница подвешивает демон
|
||||
(все вызовы падают в таймаут, хотя `browser-use --doctor` говорит «alive»).
|
||||
- **Тяжёлые страницы — читать изнутри, а не обходом.** Если у сайта есть внутренний
|
||||
JSON-эндпоинт, зови его `fetch(path, {credentials:'include'})` из уже открытой
|
||||
страницы: это быстрее и надёжнее десятков навигаций.
|
||||
- **По одной штуке за раз, с паузами.** Паузы — в Python/процессе, **не** в JS `await`
|
||||
(иначе `Runtime.evaluate timed out`). Ориентир оператора: 1 запрос, пауза 7–13 с,
|
||||
перекур каждые ~40, стоп после 3 ошибок подряд.
|
||||
- **Клики:** сначала дерево доступности (`Accessibility.getFullAXTree`) или
|
||||
`querySelector` → центр элемента (`getBoundingClientRect`) → `click_at_xy` → **проверь
|
||||
результат** отдельным `js(...)`/`page_info()`. Не кликай «на глаз» по скриншоту.
|
||||
- **Прогресс — сразу на диск** (jsonl/atomic), а не в конце прогона: длинные обходы
|
||||
обрываются.
|
||||
- **Долгие обходы** — в фоновый процесс, а не в цикл интерактивных вызовов.
|
||||
|
||||
## Границы: где человек, а где агент
|
||||
|
||||
- **Пароли и второй фактор — никогда.** Не вводим и не просим в аргументах командной
|
||||
строки. Уже залогиненная сессия оператора — вот твой доступ.
|
||||
- **Попап «Разрешить удалённую отладку?»** — это человеческое подтверждение: агент его
|
||||
не жмёт, а просит оператора нажать «Разрешить» и повторяет попытку.
|
||||
- **Деньги/платежи/отправка форм с персональными данными** — только с явным
|
||||
подтверждением оператора.
|
||||
- **Секреты и содержимое залогиненных страниц** не пишем в логи, файлы репозитория и
|
||||
вики: в mappa — только агрегаты и идентификаторы.
|
||||
|
||||
## Проверенные адреса и факты
|
||||
|
||||
- **ЧипДип, кабинет заказов:** `https://www.chipdip.ru/order/list`
|
||||
(⚠️ `/cabinet` и `/orders` отдают 404 — не перебирай наугад, ссылка есть в шапке).
|
||||
- Залогиненность видна в шапке кабинета (имя оператора); если видишь форму логина —
|
||||
**сессия потеряна: стоп и скажи оператору**, не логинься сам.
|
||||
- `about:blank`-вкладка с титулом-лошадкой в `document.title` = страницу ведёт агент.
|
||||
|
||||
## Так делать НЕ надо
|
||||
|
||||
| Соблазн | Почему нет |
|
||||
|---|---|
|
||||
| «Вытащу куки из Chrome и пойду `curl`'ом» | мимо канала и мимо аренды; пароль/2FA оказываются рядом; ломается при смене защиты |
|
||||
| «Подниму свой Chrome с отладкой» | там **нет** его логинов → выкинет на логин/капчу, плюс это второй водитель |
|
||||
| «Введу логин/пароль через `read -s`» | пароли и 2FA не вводим никогда |
|
||||
| «Проверю ещё пяток URL кабинета» | адрес подтверждай по ссылке в интерфейсе, а не перебором |
|
||||
| «Налью 20 запросов параллельно, быстрее» | антибот + оператор останавливает такие прогоны; один водитель, одна очередь |
|
||||
|
||||
Основание: базовый прогон без скила (2026-09-11) ушёл именно в «куки + curl + пароль»
|
||||
мимо канала; скил закрывает этот путь.
|
||||
|
||||
## Красные флаги (стоп и перечитай)
|
||||
|
||||
- Собираешься вытащить куки / поднять свой браузер / ввести пароль или код.
|
||||
- Работаешь с браузером **без** аренды (или ломишься, когда «занято»).
|
||||
- Полливаешь запросами без паузы или ждёшь паузу в JS.
|
||||
- Пишешь содержимое залогиненных страниц/секреты в файл, лог или вики.
|
||||
|
||||
## Вне скоупа
|
||||
|
||||
- Анонимные/антидетект-прогоны, свой профиль, `mode: fresh` — скил `browser-cdp`.
|
||||
- Облачные браузеры (Browser Use Cloud и прочие) — не берём.
|
||||
- Свой stdio-MCP-сервер поверх общего демона — отдельная тема.
|
||||
98
skills/code-search/SKILL.md
Normal file
98
skills/code-search/SKILL.md
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: code-search
|
||||
author: ours
|
||||
version: 0.1.0
|
||||
description: >
|
||||
Use when searching code for strings, symbols, or usages — "find where X is
|
||||
used", «найди, где используется», "grep for X", "where is X", "search the
|
||||
repo for", any code search, or when deciding HOW to search a codebase. One
|
||||
hard rule: in any tree that can contain node_modules/dist/build/.nuxt,
|
||||
search with `rg` (gitignore-aware) — NEVER `grep -r` (`--include` filters
|
||||
file names, not directory traversal; grep walks every node_modules entry:
|
||||
measured 15+ min never-finishing vs rg 0s on the same tree). Routes:
|
||||
string/symbol search → rg; tracked-files-only → git grep; whole-repo
|
||||
comprehension ("what does module Y do") → interns repo_read (ask-mode);
|
||||
N×M contains audits → interns grep_audit. Skip for web search
|
||||
(web-search), vault search (coworker-search), already-fast tools.
|
||||
---
|
||||
|
||||
# Code Search
|
||||
|
||||
Search code with the fastest correct tool for the question class. The default
|
||||
`grep -r` habit is the single biggest time sink in agent work on npm/JS
|
||||
projects — the fix is a different binary, not more patience.
|
||||
|
||||
## When to use
|
||||
|
||||
- Any "where is X used / where does X appear / search the repo for X" request.
|
||||
- Deciding HOW to search: rg vs git grep vs intern delegation.
|
||||
- A search that "feels slow" — that is a wrong-tool signal, not a slow disk.
|
||||
|
||||
## When NOT to use
|
||||
|
||||
- Web search → `web-search` skill.
|
||||
- Searching the .cowork vault / memory vault → `coworker-search`.
|
||||
- A question ABOUT the code ("what does module Y do", "how does the build work") → `interns.repo_read` (delegation, ask-mode per `using-interns`).
|
||||
- The right tool is already running and fast.
|
||||
|
||||
## Core rule (one sentence)
|
||||
|
||||
**In any tree that can contain `node_modules` / `dist` / `build` / `.nuxt` / `vendor`, search with `rg`, never `grep -r`.**
|
||||
|
||||
Why — measured on `stostayer.new`, `packages/web` + `apps/web4`, 2026-08-26:
|
||||
|
||||
| Fact | Value |
|
||||
|---|---|
|
||||
| Total files in the two dirs | 113,169 |
|
||||
| Of which in `node_modules` + `.nuxt` + `build` | 109,248 (**96%**) |
|
||||
| `grep -rn "3590" … --include=*.vue --include=*.js … -l` | **> 15 min, never finished** (RED run: fresh unprompted agent spawned 2× `/usr/bin/grep`, still running at 200s, killed) |
|
||||
| `rg -l "3590" …` (same globs) | **0s**, 3 matches |
|
||||
| `rg --no-ignore "3590" …` (forced full scan incl. node_modules) | 16s |
|
||||
|
||||
Mechanics: `grep -r --include` filters which **file names** get read — it does
|
||||
NOT stop **directory traversal**. grep stats/opens every directory entry
|
||||
including node_modules (100k+ files) on every search. `rg` reads `.gitignore`
|
||||
(+ `.ignore`, `.rgignore`) and skips ignored trees by default — zero flags
|
||||
needed. It is already installed on this machine (ripgrep 15.x).
|
||||
|
||||
## Routing table
|
||||
|
||||
| Question class | Tool | Notes |
|
||||
|---|---|---|
|
||||
| Find string/symbol/usages in the working tree | `rg -n "pattern" <paths>` | gitignore-aware out of the box. `-l` → filenames only. `-g '*.ext'` to filter. |
|
||||
| Only tracked files (clean, deterministic) | `git grep -n "pattern"` | uses the git index; ignores untracked + ignored. Always present even on bare boxes. |
|
||||
| "I really must scan generated/vendored too" | `rg --no-ignore` | 16s on the 113k-file tree — still ~50× faster than grep. Never `grep -r` even here. |
|
||||
| Whole-repo comprehension ("what does module Y do", "where is X used across the architecture") | `interns.repo_read` | delegation — ask-mode, `using-interns` skill. Packs via repomix + cheap LLM. |
|
||||
| N×M contains/not-contains audit (canonical strings across many files) | `interns.grep_audit` | deterministic, no LLM call. |
|
||||
|
||||
## Common mistakes / rationalizations
|
||||
|
||||
| Excuse | Reality |
|
||||
|---|---|
|
||||
| "grep works, just slow" | Wrong tool. rg is a drop-in replacement on the same globs: 15 min → 0s on the same tree. |
|
||||
| "--include excludes node_modules" | **False.** `--include` filters file *names* that get read, not directories *walked*. grep still traverses all 100k+ node_modules entries. |
|
||||
| "Windows/Defender is just slow" | The disk is not the problem — 96% of walked files are build artifacts. rg skips them via .gitignore before the filesystem ever opens them. |
|
||||
| "I need to search EVERYTHING" | Use `rg --no-ignore` (16s), still not grep (15 min). Scope with `-g '!node_modules'` if noise is the issue. |
|
||||
| "rg isn't installed here" | It is (ripgrep 15.2.0). On a bare box fall back to `git grep` — git is always present. |
|
||||
| "It's a one-off, speed doesn't matter" | One-off searches happen 10+ times per session. Each 15-min grep burns an entire agent turn for nothing. |
|
||||
|
||||
## Red flags (STOP)
|
||||
|
||||
- A search command starting with `grep -r` in any JS/TS/node project — rewrite to `rg` before running.
|
||||
- A grep that "hasn't returned" after 30s — it is walking node_modules; kill it, use rg.
|
||||
- Search results containing `node_modules/` / `.nuxt/` / `dist/` paths — you scanned garbage; redo with rg (ignore-aware).
|
||||
- Writing `--include` and believing directories are excluded.
|
||||
|
||||
## Cross-agent applicability
|
||||
|
||||
Tool-level rule, works in any agent that can run shell commands (pi, claude,
|
||||
codex exec, hermes). `rg` or `git grep` are the always-available core; the
|
||||
intern rows are optional delegation for a local `interns` MCP
|
||||
(`using-interns` skill). The core rule stands alone without them.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Semantic code search / index servers (zoekt, sourcegraph, codesearch) — YAGNI; rg removes the pain without infrastructure.
|
||||
- Searching non-code stores (vaults, wikis, the web).
|
||||
- Teaching rg's full flag surface — `rg --help` / man.
|
||||
- mappa internals: code search stays client-side (operator decision 2026-08-25 — "rg-мост по чек-аутам", outside mappa).
|
||||
@@ -1,254 +0,0 @@
|
||||
---
|
||||
name: mappa-brainstorm-promote
|
||||
author: ours
|
||||
version: 1.8.0
|
||||
description: >
|
||||
Finalize a matured brainstorm buffer (mappa entity type=brainstorm,
|
||||
status=buffer): read buffer → choose target project → promote via
|
||||
mcp__mappa__brainstorm_promote (atomic buffer → wiki-page in target +
|
||||
archive, decision 7) → extract action-items into target tasks
|
||||
(mcp__mappa__task_create, carve-out) → review umbrella → covering letter.
|
||||
A general mappa mechanism, like task.create/wiki.create — no workshop
|
||||
specifics. Old name — trigger-synonym: workshop-promote-brainstorm.
|
||||
Triggers (bilingual): «промоутни брейнсторм», «выкати в вики»,
|
||||
"promote the brainstorm", "finalize <topic>", "publish to wiki".
|
||||
---
|
||||
|
||||
# mappa-brainstorm-promote
|
||||
|
||||
Finalizing a matured brainstorm buffer that lives **as a mappa entity of type
|
||||
`brainstorm`** (status=buffer). This is a general mappa mechanism — exactly
|
||||
like `task.create` or `wiki.create`: the buffer exists in mappa, the skill
|
||||
takes it to the end (promote the content into the wiki + action-items as
|
||||
tasks). No workshop specifics: the skill triggers from any folder, works with
|
||||
brainstorm entities of any project.
|
||||
|
||||
The procedure is linear (from reading the buffer to promotion and tasks), not
|
||||
a loop: it's launched explicitly on the final buffer and takes it to the end.
|
||||
In the forkflow it sits between work (`mappa-task-work`) and finish
|
||||
(`mappa-closing-ritual`).
|
||||
|
||||
**Content promotion — always via `mcp__mappa__brainstorm_promote`:**
|
||||
atomically creates a wiki page (slug from the buffer, body preserved) in the
|
||||
project from the call and moves the buffer to `archive` (number/slug stable,
|
||||
decision 20; parent_of edges, `brainstorm.promoted` event). No file channels.
|
||||
Action-items go as tasks to the target project via `mcp__mappa__task_create`
|
||||
(carve-out without a lease, #1054; sequentially, not batched).
|
||||
|
||||
## When to use
|
||||
|
||||
- «промоутни брейнсторм», «выкати в вики», "promote the brainstorm",
|
||||
"finalize <topic>", "publish to wiki".
|
||||
- The user references a brainstorm entity (brainstorm:N) or a buffer topic
|
||||
that matured and is ready for promotion.
|
||||
|
||||
## Inputs
|
||||
|
||||
- Brainstorm ref `brainstorm:N` or `<topic>` (buffer slug/topic) + project (if
|
||||
the buffer is not in the current project — ask).
|
||||
- For the skill branch additionally: `<name>` of the new skill (if not
|
||||
specified — ask, propose a derivation from the topic).
|
||||
|
||||
## Decision flow
|
||||
|
||||
```
|
||||
brainstorm entity in mappa (type=brainstorm, status=buffer)
|
||||
│
|
||||
▼
|
||||
find + read (entity_search type=brainstorm → entity_get full body)
|
||||
│
|
||||
▼
|
||||
ask: target project (where to promote)
|
||||
│
|
||||
├── ordinary project → brainstorm_promote(project=<target>)
|
||||
│ → wiki page (spec) in the target wiki
|
||||
│
|
||||
└── skill → dialog: description (trigger contract)
|
||||
→ preview + confirm
|
||||
→ mkdir + Write SKILL.md (skeleton) in ~/projects/skills/
|
||||
→ git add + commit (local, no push/install)
|
||||
│
|
||||
▼
|
||||
parse action-items from the buffer body
|
||||
│
|
||||
▼
|
||||
for each: mcp__mappa__task_create (SEQUENTIALLY, not batched)
|
||||
│
|
||||
▼
|
||||
review-umbrella: mcp__mappa__task_create (blocked, blocker=impl#)
|
||||
│
|
||||
▼
|
||||
covering letter: mcp__mappa__inbox_send (mappa-delegation canon)
|
||||
│
|
||||
▼
|
||||
final report (wiki:NNNN — spec, brainstorm:N — archive, tasks)
|
||||
```
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Find the buffer in mappa.** `mcp__mappa__entity_search(type='brainstorm',
|
||||
project=<project>, q=<topic>)` → in the results brainstorm:N (ref). Read the
|
||||
full buffer: `mcp__mappa__entity_get(key)` — key = uuid or full ref
|
||||
`brainstorm:N` (task:1067; bare numbers → 400) — body = running record
|
||||
(frontmatter + rounds). The response carries the internal `id` for
|
||||
`brainstorm_promote`.
|
||||
|
||||
If the buffer is not in mappa — create a brainstorm entity via
|
||||
`mcp__mappa__brainstorm_create` (or HTTP `POST /entities` type=brainstorm,
|
||||
contract decision 7/#1054). Don't invent file buffers.
|
||||
|
||||
2. **Show the buffer summary (≤2 paragraphs).**
|
||||
|
||||
3. **Ask the target project** — where to promote the content. Default — the
|
||||
project where the buffer lives (brainstorms are run where the topic is
|
||||
relevant). Verify the project exists in mappa:
|
||||
`mcp__mappa__entity_search` type=project (or `mcp__mappa__entity_search`
|
||||
with q=<project name>). If not — abort with a message.
|
||||
|
||||
4. **If target = skill (the user wants it as a skill):**
|
||||
- Ask `<name>` of the new skill (if not specified) — a valid slug
|
||||
(`[a-z0-9-]+`).
|
||||
- Validation (order matters): first check that `~/projects/skills/` itself
|
||||
is a repository. If not — **abort** with the message "clone skills via
|
||||
update-skills or manually".
|
||||
- Then: `~/projects/skills/skills/<name>/` must NOT exist. If it exists —
|
||||
**abort** with the message "skill `<name>` already exists, update through
|
||||
the normal route in `~/projects/skills/`, this skill is not for updates".
|
||||
- Two-pass skeleton: dialog on `description` (activation trigger contract:
|
||||
minimum 2-3 phrases, Russian/English pairs; what it does; antipatterns) →
|
||||
preview + confirm → `Write` of the skeleton (header + 6 empty sections) →
|
||||
local `git commit` in `~/projects/skills/`. **Without** install.sh, push,
|
||||
build-hermes — those are in the baseline tasks of step 7. The body of the
|
||||
skeleton is written in a second pass by eye (outside this skill's scope).
|
||||
|
||||
5. **Content promotion (always via `brainstorm_promote`, decision 7):**
|
||||
|
||||
`mcp__mappa__brainstorm_promote(project=<target>, brainstorm_id=<internal id>)`
|
||||
|
||||
- Atomically: buffer → wiki page (slug from the buffer, body preserved,
|
||||
parent_of buffer→wiki edges and refs→buffer) + buffer → `archive` +
|
||||
significant `brainstorm.promoted` event.
|
||||
- **Frontmatter-summary (wiki:2661):** make sure the buffer body has
|
||||
`summary:` as one line in the frontmatter — `wiki.search` cards read it.
|
||||
If missing — append via `mcp__mappa__brainstorm_update` (PATCH
|
||||
/brainstorm/:id, title/body/status, optimistic version+409) before the
|
||||
promotion.
|
||||
- Re-promoting an archived buffer → error (one-shot, idempotent via
|
||||
status). Cross-check `brainstorm_id` (internal) from step 1.
|
||||
- If `brainstorm_promote` failed (version conflict, 409) → retry with the
|
||||
fresh internal id; on a stable failure — abort before creating tasks.
|
||||
|
||||
6. **Action-items parsing:** regex over lines like `- [ ] ...` in the buffer
|
||||
body, sections after `## Следующие шаги`/`## TODO`/`## Next steps`/
|
||||
`## Action items`. Show the list, allow editing/removing/adding. If 0
|
||||
action-items — continue, don't block.
|
||||
|
||||
7. **Task creation:**
|
||||
|
||||
> **NB:** create tasks **SEQUENTIALLY**, not batched. One `task_create` →
|
||||
> wait for the response → the next one.
|
||||
|
||||
- **Ordinary target:** for each action-item —
|
||||
`mcp__mappa__task_create(project=<target>, slug=<kebab>, title, description,
|
||||
status='ready')`. Create — carve-out, no lease needed (wiki:2660/#1054).
|
||||
The impl task description references the spec (wiki:NNNN from step 5).
|
||||
- **Skill:** three baseline tasks in `project='skills'`:
|
||||
- `[<name>-install]` — run `install.sh` in `~/projects/skills/`,
|
||||
verify activation in a new session.
|
||||
- `[<name>-hermes-mapping]` — a record in
|
||||
`~/projects/skills/hermes/mapping.yaml` (mode `auto` for style ones,
|
||||
`pending` if it touches tools/environment).
|
||||
- `[<name>-test-trigger]` — run the trigger phrases from the description:
|
||||
activates on its own, doesn't activate on 2-3 close foreign ones.
|
||||
Plus content tasks from the buffer (if any) — also in `project='skills'`,
|
||||
slug-prefix `<name>-`.
|
||||
- If the N-th task failed — continue the rest, report at the end which were
|
||||
created / which weren't. Remember the slugs for the review-umbrella.
|
||||
|
||||
8. **Review-umbrella (for a target with impl tasks, and for skill — always):**
|
||||
|
||||
`mcp__mappa__task_create(project=<target>, slug=<topic>-review,
|
||||
status='blocked', blocker=<impl task numbers separated by commas>, description=<checklist>)`
|
||||
|
||||
- **Who does it:** not the implementer. The next session in this project (a
|
||||
different model / different day / different agent) with a clean context.
|
||||
The "I just wrote this" bias is the main risk.
|
||||
- Checklist: read the spec (wiki:NNNN from step 5), `git log` of the
|
||||
shipped commits, for each impl task run the tests and cross-check with
|
||||
acceptance criteria, findings → follow-up tasks via `task_create`.
|
||||
- Closing: all findings filed OR the reviewer confirmed "no findings" in
|
||||
the close-note.
|
||||
- If the review task failed — report, **continue** to step 9 (the promotion
|
||||
is already done, the buffer is in archive).
|
||||
|
||||
9. **Covering letter to the target's inbox (mappa-delegation canon).** A task
|
||||
on the board doesn't ping a live session, a letter = ping + context:
|
||||
|
||||
`mcp__mappa__inbox_send(project=<target>, from=<your folder>, subject='Promotion
|
||||
<topic>: tasks <#N…>', body=<list + wiki:NNNN spec>)`
|
||||
|
||||
10. **Final report to the user:**
|
||||
- Where it was promoted: `wiki:NNNN` (spec in the target wiki).
|
||||
- Archive: `brainstorm:N` (status=archive, number stable).
|
||||
- Which tasks were created (ref, title, project).
|
||||
- **For skill:** remind about the second pass "let's flesh out `<name>`".
|
||||
|
||||
## Failure modes
|
||||
|
||||
- Buffer not found in mappa (no brainstorm entity) → abort, report: create via
|
||||
`brainstorm_create` (step 1) or HTTP POST /entities.
|
||||
- `entity_search`/`entity_get` failed (API error, not an empty result) → abort
|
||||
with the error text; don't create a buffer by guess.
|
||||
- Target project doesn't exist in mappa → abort before promotion.
|
||||
- `brainstorm_promote` failed (409 version / stable refusal) → retry with the
|
||||
fresh internal id; on a repeated failure — abort before creating tasks. The
|
||||
buffer stays in buffer — retried later.
|
||||
- Buffer already `archive` (repeated call) → abort: promotion is one-shot,
|
||||
idempotence via status (decision 7).
|
||||
- `task_create` failed on the N-th content task → continue the rest, report
|
||||
partial. The promotion is already done — the buffer is not rolled back.
|
||||
- `task_create` review-umbrella failed → don't block, report to the user
|
||||
(create manually from step 8).
|
||||
- `inbox_send` (covering letter) failed → promotion and tasks are not rolled
|
||||
back; report to the user, the letter can be sent later (the promotion is
|
||||
already visible in the graph/inbox of the target).
|
||||
- **Skill:** `~/projects/skills/` doesn't exist → abort.
|
||||
- **Skill:** `~/projects/skills/skills/<name>/` already exists → abort.
|
||||
- **Skill:** user didn't confirm the preview → abort, state unchanged.
|
||||
- **Skill:** local `git commit` in `~/projects/skills/` failed → the file
|
||||
stays, report that the commit needs to be done by hand; the buffer promotion
|
||||
is not blocked.
|
||||
|
||||
## Side effects
|
||||
|
||||
- **Always:** `brainstorm_promote` — atomically wiki page in the target +
|
||||
buffer → `archive` + parent_of edges (wiki→buffer, refs→buffer) +
|
||||
`brainstorm.promoted` event.
|
||||
- **Ordinary target:** spec page in the target project's wiki (with
|
||||
frontmatter-summary, wiki:2661) + impl tasks + review-umbrella + covering letter.
|
||||
- **Skill:** skeleton `~/projects/skills/skills/<name>/SKILL.md` (only header +
|
||||
empty 6-section skeleton) + local commit in `~/projects/skills/`.
|
||||
**Without** install.sh, push, build-hermes — those are in the baseline tasks.
|
||||
- Creates N tasks in the target via `mcp__mappa__task_create` (carve-out).
|
||||
- Creates a review-umbrella task (status=blocked, blocker=impl#).
|
||||
- Sends a covering letter to the target's inbox.
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- **Don't use file channels** — the buffer lives in a mappa brainstorm entity,
|
||||
no `.brainstorm/`/`.archive/` records.
|
||||
- **Don't use `mcp__projects-meta__tasks_create` / `knowledge_ingest` /
|
||||
`knowledge_promote`** — file channels are removed. Tasks —
|
||||
`mcp__mappa__task_create`, wiki — `brainstorm_promote` (content) +
|
||||
`wiki_create`/`wiki_update` (extra pages).
|
||||
- Don't `git mv` the buffer into the archive — the promotion archives it itself.
|
||||
- Don't delete the buffer instead of promoting — the graph history is lost
|
||||
(parent_of, refs).
|
||||
- Don't batch `task_create` (race; incident 2026-08-24: 6/7 failed) — only
|
||||
sequentially.
|
||||
- Don't forget the covering letter — a task on the board doesn't ping a live session.
|
||||
- **Skill:** don't automatically reformat the buffer body into the skeleton —
|
||||
the body is written in a second pass by eye.
|
||||
- **Skill:** don't run `install.sh`, don't push, don't edit
|
||||
`hermes/mapping.yaml` — those are baseline tasks.
|
||||
- **Skill:** don't promote into an existing skill (abort).
|
||||
@@ -1,152 +0,0 @@
|
||||
---
|
||||
name: mappa-closing-ritual
|
||||
author: ours
|
||||
version: 1.3.0
|
||||
description: >
|
||||
Finish phase of the forkflow: handoff write (mcp__mappa__handoff_write,
|
||||
version h:N) + PROPOSE wiki-ingest + PROPOSE task closes. Mutations — only
|
||||
after user confirmation. Ad-hoc: mode=light — an explicit question "Write
|
||||
handoff?" at the end of the session (NOT an automatic sweep), the decision
|
||||
is the human's. Old name — trigger-synonym: session-handoff (write part).
|
||||
Reading at start — mappa-session-orient. Triggers (bilingual): «завершаем
|
||||
сессию», «сворачиваемся», «закругляемся», "wrap up session", "end session",
|
||||
"we're done for now". Anti-triggers (task-zone, not session-end):
|
||||
«закрываем эту таску», «pause», «отбой», «разбегаемся».
|
||||
---
|
||||
|
||||
# mappa-closing-ritual
|
||||
|
||||
Finish phase of the agent cycle: **write handoff → propose wiki-ingest →
|
||||
propose task closes**. Start ≠ finish: reading the handoff at start —
|
||||
`mappa-session-orient`; here — the write path with procedure and confirmation.
|
||||
|
||||
Channel — the **Mappa handoff entity** (`mcp__mappa__handoff_write`, type
|
||||
`h:`, per-project): fields `session_id`/`date`/`status`/`summary`/
|
||||
`open_treks[]`/`ask_user[]`/`guards[]`/`recent_commits[]`. Each write = a **new
|
||||
version** (append-only, versioned history). The file-based
|
||||
`.tasks/NEXT_SESSION.md` no longer exists.
|
||||
|
||||
## When to use
|
||||
|
||||
- Session-end phrases: «завершаем сессию», «сворачиваемся», «закругляемся»,
|
||||
"wrap up session", "end session", "we're done for now".
|
||||
- Ad-hoc session without a track/task at the end: **mode=light** — an explicit
|
||||
question "Write handoff?" (not an automatic sweep), the human decides.
|
||||
- The project's AGENTS.md contains the trigger line
|
||||
`session handoff: read on start, write on end`.
|
||||
|
||||
**Skip (task-zone, not session-end):** «закрываем эту таску» (task close →
|
||||
`mappa-task-work`), «pause», «приостанови» (task-pause), «отбой», «разбегаемся»
|
||||
(too broad), "let me finish one task first, then we'll talk" (partial finish).
|
||||
On ambiguity — **ASK**: "are we closing the session or a task?"
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Scope check
|
||||
|
||||
This is the current project (cwd). No global mutations, no other projects.
|
||||
|
||||
### 2. Mid-task capture
|
||||
|
||||
If there is a 🔴 active task of the project (mappa board / `.tasks/`) — capture
|
||||
into summary:
|
||||
```
|
||||
left mid-task: <slug>
|
||||
where_stopped: <one line>
|
||||
```
|
||||
No board — write the handoff without the mid-task section, don't block.
|
||||
|
||||
### 3. Compose content (handoff fields)
|
||||
|
||||
- `session_id` — `<ISO date>` or session identifier;
|
||||
- `status` — `active` (work continues) / `paused` (frozen) / `done` (finished);
|
||||
- `summary` — the link: where we stopped, mid-task, key decisions;
|
||||
- `open_treks` — array of open tracks (readiness + entry-point);
|
||||
- `ask_user` — pending decisions / expected resolutions;
|
||||
- `guards` — "don't do" (preemptive guards);
|
||||
- `recent_commits` — 3–5 latest commits (`<slug>: <subject>`).
|
||||
|
||||
Forward-looking, not a timeline: handoff = a link of new things specifically
|
||||
for the next turn, not an overview of the whole project. The mappa board / wiki
|
||||
remain authoritative for their own scope — don't duplicate them in the handoff.
|
||||
|
||||
### 4. Append
|
||||
|
||||
`mcp__mappa__handoff_write(project=<name>, session_id, status, summary, open_treks?, ask_user?, guards?, recent_commits?)` — the service creates a new `h:N` version (previous ones remain; reading the latest — `entity_search(type='handoff', project, limit=1)`). Pass array fields as `[]` when empty (the next agent sees: empty, not forgotten). Written without a lease (like inbox).
|
||||
|
||||
> **Confirm gate:** handoff write is a mutation. In mode=light (ad-hoc) and on
|
||||
> ambiguity — first an explicit question "Write handoff?", only after "yes" —
|
||||
> write.
|
||||
|
||||
### 5. Propose wiki-ingest (don't write!)
|
||||
|
||||
If durable knowledge appeared during the session — **PROPOSE** the ingest
|
||||
(`mappa-knowledge`: mappa wiki create — carve-out, update — version+409,
|
||||
wiki:2660), listing the candidates. Don't write anything without confirmation.
|
||||
|
||||
### 6. Propose task-board closes (don't close!)
|
||||
|
||||
If there are tasks that look closed — **PROPOSE** the closes
|
||||
(`mappa-task-work`: task_close with version+409, wiki:2660). Respect ralph-loop:
|
||||
verifier tasks close only via the verifier (attempt/harness-record).
|
||||
|
||||
### 7. Proposal format — one block
|
||||
|
||||
```
|
||||
Closing ritual:
|
||||
(a) ingest X into the wiki?
|
||||
(b) close Y?
|
||||
(c) nothing.
|
||||
```
|
||||
Wait for the answer. Refusal = skip (don't insist, don't repeat in this session).
|
||||
|
||||
---
|
||||
|
||||
## mode=light (ad-hoc sessions)
|
||||
|
||||
Ad-hoc session (no task/track, but artifacts may be born): the trace in mappa
|
||||
is always written (service contract — session live-ingest), but
|
||||
**structuring happens only on an explicit question**: at the end of the
|
||||
session ask "Write handoff?" (the human decides). NOT an automatic sweep:
|
||||
without "yes" — write nothing, the trace stays in mappa as is.
|
||||
|
||||
## Failure modes
|
||||
|
||||
- **Secret detected.** Content matches secret patterns (`AKIA...`, `sk-...`,
|
||||
`ghp_...`, `ssh-rsa`, `BEGIN PRIVATE KEY`, `password=`/`token=`) → **abort
|
||||
write**. Tell the user with the suspicious line indicated.
|
||||
- **Ambiguous phrase** → ASK "are we closing the session or a task?", don't guess.
|
||||
- **Mid-task without a board** → handoff without the mid-task section, don't block.
|
||||
- **User refused the ritual proposals** → skip, don't insist.
|
||||
- **Project not in mappa** → silent exit (first session).
|
||||
|
||||
## Side effects
|
||||
|
||||
- Writes the project's handoff entity (append-only, versioned history). No
|
||||
files, no git commits for the handoff.
|
||||
- The ritual **proposes** wiki-ingest and task closes — but does NOT write
|
||||
them without "yes".
|
||||
- No global mutations, no other projects.
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- **No auto-execute** — every mutation (handoff write / wiki-ingest / task
|
||||
close) only after an explicit "yes" from the user.
|
||||
- **Don't run the ritual on a substantive commit.** Only a session-end phrase
|
||||
(or an explicit user request).
|
||||
- **Don't sweep automatically in ad-hoc** — mode=light: an explicit question
|
||||
"Write handoff?", the human decides.
|
||||
- **Don't write secrets** into the handoff. Secret-pattern match → abort.
|
||||
- **Don't write a handoff on every commit** — only at the finish.
|
||||
- **Don't duplicate the board / wiki** — handoff = forward-looking link.
|
||||
- **No cross-project** — per-project scope.
|
||||
- **Don't treat the handoff as authoritative** on the reader's side — it's the
|
||||
previous session's recommendation, not a directive; the user can override.
|
||||
|
||||
## Reference
|
||||
|
||||
- Reading the handoff at start: `mappa-session-orient`.
|
||||
- Task closes: `mappa-task-work` (pre-close coverage, notify letter).
|
||||
- Wiki-ingest: `mappa-knowledge`.
|
||||
- Letters: `mappa-messaging`. Delegation: `mappa-delegation`.
|
||||
- Session live-ingest (the trace in mappa is always written): `concepts/session-live-ingest` (wiki:2604).
|
||||
@@ -1,266 +0,0 @@
|
||||
---
|
||||
name: mappa-delegation
|
||||
author: ours
|
||||
version: 1.3.0
|
||||
description: >
|
||||
The cycle of delegating a task to another agent/project: pre-flight gate →
|
||||
body template → dry-run preview → confirm → covering letter to the
|
||||
recipient's inbox → paired review task for impl. Every cross-project
|
||||
delegation is a pair: tasks_create + letter (event: created) — a task on the
|
||||
board does not ping a live session. Old name — trigger-synonym:
|
||||
delegate-task. Triggers (bilingual): «делегировать таску», «создать задачу
|
||||
на агента», «поставить задачу агенту», «tasks_create для», "delegate task",
|
||||
"create a task for an agent", "assign a task to an agent". NOT applicable:
|
||||
self-assigned tasks on your own board («создать задачу себе» →
|
||||
mappa-task-work), doing work yourself, workshop-internal tasks.
|
||||
---
|
||||
|
||||
# mappa-delegation
|
||||
|
||||
Unified **cycle of assigning tasks to agents**: from the pre-flight gate to
|
||||
the covering letter to the recipient. Guarantees that every delegated task
|
||||
carries: mandatory skills (imperative invoke), pre-flight permissions,
|
||||
steering-loop fields (notify/weight), a paired review task for impl — and that
|
||||
the recipient actually learns about the task (letter, not just the board).
|
||||
|
||||
## When to use
|
||||
|
||||
Before every `tasks_create` call for another project or agent.
|
||||
|
||||
**Activates:** «делегировать таску», «создать задачу на агента», «поставить задачу агенту», «tasks_create для», "delegate task", "create a task for an agent".
|
||||
|
||||
**Not applicable:**
|
||||
- Work you do yourself in the current session.
|
||||
- Self-assigned tasks on your own board («создать задачу себе», "task for myself") → `mappa-task-work`, not delegation. Disambiguator: «на агента»/«агенту»/«в проект X» = delegation; «себе»/"myself" = your own board.
|
||||
- Workshop-internal tasks (`.workshop/.tasks/` — workshop-meta, not delegation).
|
||||
- `tasks_create` with `target=agenda` (cross-project agenda — not delegation to an agent).
|
||||
|
||||
## Inputs
|
||||
|
||||
- `target_project` — qualified `<owner>/<repo>` (required)
|
||||
- `slug` — kebab-case latin
|
||||
- Short task description (goal + acceptance criteria)
|
||||
- `weight` — `cheap-ok | needs-claude | needs-human`
|
||||
- `notify` — commissioning project slug (who gets the inbox letter on close/park)
|
||||
|
||||
The task number is assigned by the server (`tasks_create` from the
|
||||
agenda/task-counter) — the assigner neither invents nor reserves it. The
|
||||
returned `#n` from preview/confirm is the task's machine key: blockers,
|
||||
letters, and the decision trail reference it.
|
||||
|
||||
> **Contract (interactive, wiki:2660).** `task_create` — **carve-out without a
|
||||
> lease** (create-without-lease as a principle); update/close — version+409
|
||||
> (409 → re-GET → retry). file channel — sha-CAS via Gitea. No claim/TTL —
|
||||
> "take a task" = conditional update by version (poller outside mappa).
|
||||
|
||||
## Steps (the cycle)
|
||||
|
||||
### 1. Pre-flight gate (6 questions to the user)
|
||||
|
||||
Ask **before** composing the task body:
|
||||
|
||||
0. **Critical infrastructure?** — the task changes: poller/agent-runner, MCP
|
||||
servers (projects-meta, interns), claim/close/heartbeat mechanics, deploy
|
||||
infra (traefik, docker, systemd), CI/CD pipelines, git hooks.
|
||||
- If **yes** → force `weight: needs-human`, no discussion. Explain to the
|
||||
user why.
|
||||
- If **no** → continue.
|
||||
1. **Interns — allowed?** (yes/no, per task)
|
||||
2. **Auto-push — allowed?** (yes/no, per task)
|
||||
3. **Contextual skills beyond defaults?** — propose per task content (e.g.
|
||||
`claude-api` for Anthropic SDK work, `frontend-design` for UI,
|
||||
`using-interns` if interns are allowed); the user approves.
|
||||
4. **notify — who gets the completion/block report?** (project slug; usually
|
||||
`.workshop` or `OpeItcLoc03/workshop`)
|
||||
5. **Session-break after this task?** — is a session break needed after it
|
||||
closes (domain-switch, milestone, heavy infra)?
|
||||
- If **yes** → set `session_break` in the task body (see template): `true`
|
||||
or a string-hint with the next track's name. `mappa-task-work` will stop
|
||||
after close and propose ending the session, without claiming the next task.
|
||||
- If **no** → don't add the field (default — the agent continues the cycle).
|
||||
|
||||
### 2. Compose the task body per template
|
||||
|
||||
Sections strictly in order:
|
||||
|
||||
```
|
||||
<Goal — one or two sentences. Acceptance criteria if any.>
|
||||
**Spec:** <path to the design solution or .brainstorm/…> — mandatory for tasks
|
||||
from design/decision: the impl reads the design, doesn't guess
|
||||
|
||||
## Mandatory skills — invoke before starting work
|
||||
|
||||
- invoke `tdd-criteria` — before writing code
|
||||
- invoke `mappa-task-work` — for task status management
|
||||
- invoke `project-discipline` — commit/push discipline
|
||||
- invoke `mappa-knowledge` after closing — ingest .wiki/concepts/<slug>.md
|
||||
[if cross-project: - invoke `using-projects-meta` — cross-project tasks/wiki]
|
||||
[contextual skills from step 1.3]
|
||||
|
||||
**TDD:** yes | no — <reason>
|
||||
**Permissions:** interns: yes/no | auto-push: yes/no
|
||||
**weight:** cheap-ok | needs-claude | needs-human
|
||||
**notify:** <commissioning-project-slug>
|
||||
[**allow_upgrade:** true/false]
|
||||
[**session_break:** true | "<next track / hint>"] # optional — mappa-task-work stops after close, doesn't claim the next task
|
||||
```
|
||||
|
||||
**When to set `session_break`** (optional; by default DON'T set it — it marks
|
||||
a real boundary, not a default). Three cases:
|
||||
|
||||
1. **Domain / repo switch** — the task finishes one track before moving to an
|
||||
unrelated one.
|
||||
2. **Milestone task** — the last in a group of sub-tasks of one feature.
|
||||
3. **Heavy infra task** — shared checkout, migrations, deploy — where it's
|
||||
reasonable to stop and check the state.
|
||||
|
||||
Value: `true` (next track = "see STATUS.md") or a string-hint with the next
|
||||
track's name. Consumer — `mappa-task-work`: after close it prints
|
||||
`🔚 SESSION BOUNDARY …` and stops, without claiming the next task. Design:
|
||||
`.wiki/concepts/delegate-task-session-break.md`.
|
||||
|
||||
**Staged breakdown:** if the solution splits into stages (1 → 1b → 3), create
|
||||
each stage as a separate task with `status: blocked` + `blocker:
|
||||
<predecessor numbers> (#n1, #n2 — numbers, not slugs; the number = machine
|
||||
key)`. The board shows the order, the poller won't take dependent work early.
|
||||
Create tasks in one repo sequentially, not in parallel (otherwise sha-lock
|
||||
conflict — see Failure modes).
|
||||
|
||||
**Why `invoke` and not a trigger phrase:** AGENTS.md is unreliable (drifts
|
||||
under compression, weak models ignore it). The task body is read actively —
|
||||
the imperative `invoke` is a direct command, not passive matching.
|
||||
|
||||
### 3. Dry-run preview
|
||||
|
||||
`tasks_create(confirm=false)` — show the user the preview before the real commit.
|
||||
|
||||
### 4. Confirmation and creation
|
||||
|
||||
After the user's OK: `tasks_create(confirm=true)`.
|
||||
|
||||
### 5. Covering letter — mandatory for cross-project delegation
|
||||
|
||||
After creation, **every cross-project delegation** is duplicated by a letter to
|
||||
the recipient's inbox (canon — `mappa-messaging`: Mappa channel, address from
|
||||
the address book `~/projects/.wiki/concepts/projects-address-book.md`):
|
||||
|
||||
```
|
||||
mcp__mappa__inbox_send(
|
||||
project: <recipient address>, # folder name, from the address book
|
||||
from: <your folder>,
|
||||
subject: "[event: created] #n slug",
|
||||
body: "1-2 lines — what the task is, why, slug; «sort it out and take it»"
|
||||
)
|
||||
```
|
||||
|
||||
(«sort it out and take it» — intentionally bilingual placeholder: the covering
|
||||
letter body may be written in the recipient's language (Russian for
|
||||
Russian-speaking projects, English otherwise). Not a trigger phrase.)
|
||||
```
|
||||
|
||||
(update/close mutations — version-based (409 → retry); letter delivery —
|
||||
carve-out, requires no lease.)
|
||||
|
||||
Reason: a task on the board **does not ping the recipient's live session**.
|
||||
The poller will pick it up by `Weight`/`Notify`, but a live interactive
|
||||
session learns only through the inbox monitor — i.e. through the letter. The
|
||||
rule "task + letter, not just task" — is the general case (step 7 is its
|
||||
particular case for downstream tasks).
|
||||
|
||||
Skip: self-assigned tasks on your own board; `target=agenda` (shared board,
|
||||
no concrete recipient — steering-loop via `Notify`).
|
||||
|
||||
### 6. Paired review task (impl tasks only)
|
||||
|
||||
If the task is implementation — create the paired `<slug>-review`
|
||||
(status=blocked, blocker=`#n` — the impl task number). Skip for: pointer
|
||||
tasks, ops tasks, research tasks, any non-impl.
|
||||
|
||||
**`weight` of the review task — inherit from the impl task, but never below
|
||||
`needs-claude`** (set explicitly at `tasks_create`):
|
||||
|
||||
- impl `needs-human` → review `needs-human` (a critical-infra change cannot
|
||||
be reviewed by a weak tier — the review inherits the impl's strictness).
|
||||
- impl `needs-claude` → review `needs-claude`.
|
||||
- impl `cheap-ok` → review `needs-claude` (floor: review is
|
||||
discipline-critical, see What NOT to do — don't drop to cheap-ok).
|
||||
|
||||
Without an explicit `weight` the poller won't route the review task (the
|
||||
reconciler skips it) — so always set it, even when impl and review are at the
|
||||
same tier.
|
||||
|
||||
### 7. Downstream task for a LIVE session → require task + inbox letter
|
||||
|
||||
If the task body **instructs the agent to create a downstream task itself** for
|
||||
another project where a **live interactive session** is working (e.g. the
|
||||
programmer sets a deploy task for the admin) — in the spec **explicitly require
|
||||
BOTH `tasks_create` AND the inbox letter** to that project
|
||||
(`mcp__mappa__inbox_send(project=<target>, from=<yours>, subject="[event: created] #n slug", ...)`).
|
||||
|
||||
Reason: a task on the board does **NOT** ping the live session. The poller
|
||||
will pick it up by `Weight`/`Notify`, but a live interactive session learns
|
||||
only through the inbox monitor / Stop-hook — i.e. through the letter. A spec
|
||||
that requires only `tasks_create` leaves the downstream task hanging unnoticed,
|
||||
and someone finishes the ping by hand.
|
||||
|
||||
Rule: poller-driven target → `Weight`/`Notify` mandatory; live session → inbox
|
||||
letter mandatory; **not sure poller or live — require BOTH.** Apply the same
|
||||
rule when you ping a peer yourself: task + letter, not just task.
|
||||
|
||||
## Failure modes
|
||||
|
||||
- **User refuses the pre-flight** → abort, don't create the task.
|
||||
- **User rejects the dry-run preview** → abort.
|
||||
- **notify not specified** → re-ask, don't skip silently. Without notify the
|
||||
steering-loop doesn't close.
|
||||
- **weight not specified** → re-ask. Without weight the poller doesn't know who
|
||||
to give the task to.
|
||||
- **tasks_create failed** → distinguish: **PushRejected** (sha-lock conflict —
|
||||
the repo moved between preview and confirm; happens on parallel creation into
|
||||
one repo) → **retry**: repeat the confirm — the server re-reads the actual
|
||||
base_sha. Other errors → tell the user, don't retry without an explicit request.
|
||||
|
||||
## Side effects
|
||||
|
||||
- Creates a task in the target project via `tasks_create` (file channel —
|
||||
Gitea commit; service channel — mappa entity, create = carve-out without a
|
||||
lease wiki:2660).
|
||||
- Optionally creates the paired review task (status=blocked).
|
||||
- Covering letter to the recipient's inbox (cross-project delegation).
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- Don't skip the pre-flight gate — even if everything seems obvious.
|
||||
- Don't use passive trigger phrases instead of `invoke` — "tdd-criteria" in
|
||||
text is weaker than "invoke `tdd-criteria`".
|
||||
- Don't skip `notify` — without it the boss won't learn about completion.
|
||||
- Don't skip `weight` — without it fleet routing is blind.
|
||||
- Don't create a review task for pointer/ops/research tasks — impl only.
|
||||
- Don't create a review task without `weight` — the reconciler/poller will
|
||||
skip it. Inherit from impl, floor `needs-claude` (see Step 6).
|
||||
- Don't assign `weight: cheap-ok` where discipline is critical (review,
|
||||
security, schema migration) — weak models may ignore invoke instructions.
|
||||
- Don't assign `weight: needs-claude` or `cheap-ok` to tasks changing critical
|
||||
infrastructure (poller, MCP servers, deploy, CI/CD) — only `needs-human`.
|
||||
- Don't set `session_break` routinely on every task — it marks a real
|
||||
boundary (domain-switch / milestone / heavy infra), not a default; otherwise
|
||||
`mappa-task-work` breaks the session after every close.
|
||||
- **Don't create tasks from design/decision without a `**Spec:**` reference**
|
||||
— the impl agent guesses thresholds/scope instead of reading the design.
|
||||
- **Don't create several tasks in one repo in parallel** — sha-lock conflicts
|
||||
(PushRejected); serialize the confirms.
|
||||
- **Don't delegate a cross-project task without the covering letter** to the
|
||||
recipient's inbox (step 5, Mappa `inbox_send`). `tasks_create` into a foreign
|
||||
board doesn't ping the live session — a task without a letter stays unnoticed
|
||||
until the poller/hand.
|
||||
- **Don't instruct the agent to create a downstream task for a live session
|
||||
without the paired inbox letter** (see Step 7). `tasks_create` into a foreign
|
||||
board doesn't ping the live session — the spec must require BOTH the task
|
||||
and the letter, otherwise the downstream task hangs unnoticed.
|
||||
|
||||
## Reference
|
||||
|
||||
- Letters: `mappa-messaging` (inbox_send canon, address book).
|
||||
- Tasks/board: `mappa-task-work`.
|
||||
- Knowledge: `mappa-knowledge` (wiki after closing).
|
||||
- Promotion: `mappa-brainstorm-promote` (review-umbrella through it too).
|
||||
@@ -1,277 +0,0 @@
|
||||
---
|
||||
name: mappa-knowledge
|
||||
author: ours
|
||||
version: 1.5.0
|
||||
description: >
|
||||
The cycle of working with a project's knowledge in Mappa (Karpathy LLM Wiki,
|
||||
channel = mappa entities): ingest → query → lint + a graph layer for
|
||||
relational/structural questions. Absorbs using-wiki + using-wiki-graph (old
|
||||
names — trigger-synonyms). Triggers (bilingual): «заингесть», «обнови вики»,
|
||||
«запроси вики», «проверь вики», "use project wiki", "query the wiki",
|
||||
«что связывает X и Y», «как связаны», «путь между X и Y», "what connects
|
||||
X and Y", «что ссылается на X», «backlinks of X», «сироты», «битые ссылки»,
|
||||
"orphan pages". Wiki = entities type=wiki (read — carve-out; create —
|
||||
carve-out, update — version+409; contract wiki:2660). Relational questions —
|
||||
via graph_* (BFS server-side), guarded failure-mode: one page and stop,
|
||||
no multi-hop chains by reading. Skip for single-page content questions.
|
||||
---
|
||||
|
||||
# mappa-knowledge
|
||||
|
||||
The single cycle of working with a project's knowledge in **Mappa**: three
|
||||
operations (ingest / query / lint) + a **graph layer** for relational and
|
||||
structural questions. The skill is a cycle, not a tool: knowledge is
|
||||
**compiled once and kept current** (ingest), queried (query), checked (lint),
|
||||
and the links between entities are read through the graph (graph_*).
|
||||
|
||||
Channel — Mappa (`mcp__mappa__*`), NOT files. A page is an entity `type=wiki`
|
||||
(`wiki:N`); read — carve-out; **create — carve-out without a lease; update —
|
||||
optimistic concurrency (version+409 → retry)** (contract wiki:2660, v0.12.0).
|
||||
The file-based `.wiki/` no longer exists; `setup-wiki` is dead (nothing to set up).
|
||||
|
||||
## When to use
|
||||
|
||||
- Ingest a document/source into the wiki («заингесть X», «обнови вики»).
|
||||
- Answer from the wiki / check the wiki («запроси вики», «проверь вики», lint).
|
||||
- Relational/structural question («что связывает X и Y», «backlinks», «сироты») — the graph layer.
|
||||
- Modify any page — formats below are mandatory; project conventions live in
|
||||
the `AGENTS` entity (legacy — `CLAUDE` pointer).
|
||||
|
||||
**NOT for:** one-off code questions (normal file reading), single-file
|
||||
README/ADR (not a persistent knowledge base), a project without a wiki in mappa.
|
||||
|
||||
## Three layers (don't mix)
|
||||
|
||||
1. **Raw sources** — `summaries/<slug>` pages. Immutable: read, don't edit
|
||||
(the only exception — the `> Status` blockquote on an explicit user request).
|
||||
2. **Wiki** — the other pages (entities/concepts/packages/contradictions/open-questions/overview).
|
||||
3. **Schema** — the `AGENTS` entity (canon, slug `AGENTS`) + `CLAUDE` (legacy
|
||||
pointer "Canon is AGENTS"). Read `AGENTS` first; it overrides this skill on
|
||||
conflict.
|
||||
|
||||
## First step of any operation
|
||||
|
||||
1. `mcp__mappa__wiki_get(project, 'AGENTS')` — if present, read it (canon; if
|
||||
not — `wiki_get(project, 'CLAUDE')`, the legacy pointer).
|
||||
2. `mcp__mappa__wiki_get(project, 'index')` — the catalog; find the needed
|
||||
pages. (Default catalog — `entity_search`, decision 1; `index` is an
|
||||
orientation aid.)
|
||||
3. Only then act.
|
||||
|
||||
If `AGENTS`/`CLAUDE` is missing — the wiki is either new or unmaintained:
|
||||
don't improvise the structure, the first ingest creates `AGENTS`
|
||||
(+ `CLAUDE` pointer).
|
||||
|
||||
## MCP surface
|
||||
|
||||
| Operation | Tool | Note |
|
||||
|---|---|---|
|
||||
| Read a page | `mcp__mappa__wiki_get(project?, slug)` | read — carve-out |
|
||||
| Search pages | `mcp__mappa__entity_search(q, type='wiki', project?, scope?, limit)` | ILIKE over body/title (full bodies) |
|
||||
| Card search | `mcp__mappa__wiki.search(q, scope?, project?, projects?, limit?)` | cards {ref, project, slug, title, summary, snippet, related} — without bodies (wiki:2661) |
|
||||
| Create a page | `mcp__mappa__wiki_create(project, slug, body)` | **carve-out without a lease** |
|
||||
| Update a page | `mcp__mappa__wiki_update(project, id, title?, body?, version)` | **version-based**: conflict → 409 → retry with the fresh version from wiki_get |
|
||||
| Path between entities | `mcp__mappa__graph_path({from, to})` | shortest chain, BFS |
|
||||
| Neighbors / outgoing | `mcp__mappa__graph_neighbors({id})` | node edges with target resolution |
|
||||
| Incoming links | `mcp__mappa__graph_backlinks({id})` | who references the node |
|
||||
| Graph health | `mcp__mappa__graph_stats()` | nodes/edges/components |
|
||||
|
||||
**Writing — carve-out (create) / version-based (update), no lease (interactive
|
||||
contract, wiki:2660).** `wiki_create` requires no claim_token; `wiki_update`
|
||||
takes the expected `version` (fresh from `wiki_get`) — conflict → 409 →
|
||||
re-GET → retry.
|
||||
|
||||
**Frontmatter-summary (wiki:2661, card search).** On create/update/promote
|
||||
write `summary:` — ONE essence line in the page frontmatter (`---\ntitle: …\nsummary: one line\n---`). The `wiki.search` cards read it (without summary the
|
||||
card is poorer — snippet fallback only). Don't insert duplicate info into the
|
||||
body: the summary is compiled once, in the frontmatter.
|
||||
|
||||
**Refs and ids (#1037/#1028).** The public surface carries the per-type ref by
|
||||
full name as the first field: `ref: "wiki:3"` (decision 20, convention #1028),
|
||||
`num` next, the global `id` — internal (last). `wiki_update` needs the
|
||||
internal `id` — from the `wiki_get`/`entity_search` response. In prose —
|
||||
slug/name first, ref as anchor: "the spec `concepts/session-live-ingest`
|
||||
(wiki:2604)". In page bodies — wikilinks by slug (`[[concepts/foo]]`,
|
||||
decision 4) or per-type refs by full names (`[[task:N]]`/`[[inbox:N]]`).
|
||||
|
||||
---
|
||||
|
||||
## The cycle: three operations
|
||||
|
||||
### Ingest — «заингесть X»
|
||||
|
||||
1. Read the source completely.
|
||||
2. Extract: entities, concepts, packages, cross-results.
|
||||
3. Create `summaries/<slug>` — one summary page per source (~50–150 lines;
|
||||
put the raw link in frontmatter `raw_path` + `ingested:`).
|
||||
4. For every affected page:
|
||||
- exists → update (`wiki_update(project, id, body, version)` — version
|
||||
fresh from `wiki_get`; 409 → re-GET → retry). **Mark contradictions
|
||||
explicitly** with a `> **Contradiction:** source A says X, source B — Y`
|
||||
block. Don't overwrite silently.
|
||||
- missing → create (`wiki_create`, carve-out).
|
||||
5. Update `index` (catalog: one line per page) — optional; the default catalog
|
||||
is `entity_search` (decision 1).
|
||||
6. Report to the user: what was created, what updated, which contradictions.
|
||||
First ingest of a new wiki: create `AGENTS` (canon) + `CLAUDE` (pointer).
|
||||
|
||||
**Op-log — automatic.** Every write operation is already logged by the service
|
||||
into the `logs` table (component=entity type, message=slug+operation; to view —
|
||||
`mcp__mappa__admin_logs`). Don't maintain a manual `log` page — it's a
|
||||
duplicate, the audit trail lives in the service (decision 12, ratified
|
||||
2026-08-24).
|
||||
|
||||
**One ingest can touch 10–15 pages. That's normal — that's what LLMs are for.**
|
||||
|
||||
Write order: all wiki mutations in one cycle; create — carve-out, update — with
|
||||
version (fresh from `wiki_get`); 409 → re-GET → retry. No lease/claim needed
|
||||
for writing (wiki:2660).
|
||||
|
||||
### Query — a question to the wiki
|
||||
|
||||
1. Read `index` first, then dig into pages (`wiki_get` by slug).
|
||||
2. Answer with quote-wikilinks: `[[concepts/foo]]` (edges are created on
|
||||
write, decision 4).
|
||||
3. **Compounding the wiki.** If the answer is a real synthesis (comparison,
|
||||
analysis, new link) — ask the user: "Save as a wiki page?" Good questions
|
||||
become pages in `concepts/`.
|
||||
|
||||
**Relational/structural questions — don't read, call the graph** (next
|
||||
section): links form a graph that an LLM doesn't traverse reliably by reading.
|
||||
|
||||
### Lint — «проверь вики»
|
||||
|
||||
Look for:
|
||||
- **Contradictions** between pages.
|
||||
- **Orphans** — pages without incoming links: `graph_backlinks(id)` (id from
|
||||
`wiki_get`) → no incoming edges = orphan.
|
||||
- **Stale-claims** — a page's `updated_at` older than the source it summarizes.
|
||||
- **Lost entities** — concepts from the text without their own page
|
||||
(`entity_search` by name → empty).
|
||||
- **Empty/TODO sections.**
|
||||
|
||||
Report — a punch list. Don't delete anything automatically.
|
||||
|
||||
---
|
||||
|
||||
## Graph layer (relational/structural questions)
|
||||
|
||||
**Stop and call the graph.** On a relational/structural question about the wiki
|
||||
or any mappa entities (tasks, letters, sessions) **don't answer after reading
|
||||
one page** — that's the 0%-recall failure the graph layer exists for. The
|
||||
service walks the edges deterministically (BFS) and returns the answer in a few
|
||||
lines; context doesn't get polluted.
|
||||
|
||||
Question form → tool:
|
||||
|
||||
| Question | Tool |
|
||||
|---|---|
|
||||
| relational — «что связывает X и Y», «путь между», "what connects", "shortest path" | `graph_path({from, to})` |
|
||||
| neighbourhood — «соседи X», "neighbours of X" | `graph_neighbors({id})` |
|
||||
| incoming — «кто ссылается на X», «backlinks», "what links to X" | `graph_backlinks({id})` |
|
||||
| health — «сироты», «битые ссылки», «здоровье вики», "orphan pages" | `graph_stats()` + `graph_backlinks(id)` |
|
||||
|
||||
**Addressing: slug → internal id.** Resolve `id` via `wiki_get`/`entity_search`
|
||||
(the last response field; `ref`/`num` — for display). Graph responses carry
|
||||
per-type refs by full names (`task:N`/`inbox:N`/`wiki:N`, convention #1028) —
|
||||
reference them, not ids. An empty `path` = the link genuinely doesn't exist —
|
||||
say so; don't invent a chain from textual proximity.
|
||||
|
||||
**Precondition — the graph is actually connected.** If unsure — first
|
||||
`graph_stats()`: `edges` ≈ 0 ⇒ empty graph, answer by reading. (Slugs without
|
||||
[[links]] create no edges; orphans are normal for sparse wikis.)
|
||||
|
||||
---
|
||||
|
||||
## Page formats (MANDATORY)
|
||||
|
||||
### Frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: Human-readable name
|
||||
type: entity | concept | package | summary | contradiction | open-question | overview
|
||||
tags: [short, tokens]
|
||||
sources: [concepts/mappa.md]
|
||||
updated: 2026-08-24
|
||||
---
|
||||
```
|
||||
|
||||
`summaries/` pages additionally carry `ingested: YYYY-MM-DD` and `raw_path: …`.
|
||||
`contradictions/` — `status: open | resolved | accepted-divergence` and
|
||||
`affects:`. `open-questions/` — `status: open | answered | obsolete` and
|
||||
`touches:`.
|
||||
|
||||
### Slugs
|
||||
|
||||
- `kebab-case`, **Latin only**. Transliterate Cyrillic/other scripts
|
||||
(«план переписывания» → `ozon-client-rewrite`). The original title — in H1
|
||||
and frontmatter.
|
||||
- `entities/<name>`, `concepts/<name>`, `packages/<name>`, `summaries/<slug>`,
|
||||
`contradictions/<slug>`, `open-questions/<slug>`.
|
||||
|
||||
### Op-log — the `logs` table, not a page
|
||||
|
||||
File-based `log.md` is dead (decision 12/15, ratified 2026-08-24). The service
|
||||
writes the op-log itself on every write operation: `mcp__mappa__admin_logs`
|
||||
(filters level/since/component/entity, retention 14d). Don't create, append,
|
||||
or parse a manual `log` page.
|
||||
|
||||
### `index` — catalog via search
|
||||
|
||||
Catalog = `entity_search(q, type='wiki', project)` (decision 1). The `index`
|
||||
page — optional orientation aid: one line per page
|
||||
`- [Title](concepts/foo.md) — hook.`, sections by type. Update only if the
|
||||
page already exists; don't proliferate catalog duplicates.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Situation | What we touch |
|
||||
|---|---|
|
||||
| Ingest one document | `summaries/<slug>` (new) + 3–15 entities/concepts/packages (+ optional `index`) |
|
||||
| Query | (read) + possibly a new page |
|
||||
| Query relational | graph_* (BFS), not reading |
|
||||
| Lint | (read) + graph_backlinks/stats for orphans |
|
||||
| New project wiki | the first ingest creates `AGENTS` + `CLAUDE` pointer; op-log — automatic |
|
||||
|
||||
## Common mistakes
|
||||
|
||||
- **Editing `summaries/`.** Not allowed. Only a status block on an explicit request.
|
||||
- **Dumping raw content into `summaries/`.** A summary is a summary. Reference
|
||||
the raw, don't copy it.
|
||||
- **Silent overwrites.** A new source contradicts a page — mark with a
|
||||
`> **Contradiction:**` block; don't wipe it.
|
||||
- **Narrative op-log.** Don't maintain it by hand: the service writes logs
|
||||
itself (admin.logs).
|
||||
- **Non-ASCII slugs.** Break grep and cross-platform compatibility. Transliterate.
|
||||
- **Missed contradictions in lint.** The wiki's value is in exposed tensions,
|
||||
not in false consensus.
|
||||
- **Writing without version (update).** `wiki_update` without a fresh version →
|
||||
last-write-wins, risk of wiping someone else's work; take the version from
|
||||
`wiki_get`, 409 → retry.
|
||||
- **Holding a claim for reading/thinking.** A claim is for the duration of
|
||||
work; reading — carve-out.
|
||||
- **Answering a relational question by reading one page.** That's the exact
|
||||
0%-recall failure — call graph_*.
|
||||
- **Slugs/paths into graph tools.** Only internal ids, and only fresh ones (a
|
||||
deleted entity → error).
|
||||
- **Dragging the whole wiki into context** to "trace" links by hand — the
|
||||
service does it for zero tokens.
|
||||
|
||||
## Red flags
|
||||
|
||||
- Relational question → reading a page instead of `graph_*`.
|
||||
- Editing `summaries/` or silently overwriting a contradiction.
|
||||
- Wiki update mutation without version (last-write-wins) or create with an
|
||||
invented claim.
|
||||
- Narrative op-log by hand.
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Entity search: `mcp__mappa__entity_search` (FTS, decision 1).
|
||||
- Op-log: `mcp__mappa__admin_logs` (automatic, decision 12).
|
||||
- Tree/umbrellas: `mcp__mappa__graph_tree(root, depth?, fields?, limit?)`.
|
||||
- Tasks: `mappa-task-work`. Mail: `mappa-messaging`. Delegation: `mappa-delegation`.
|
||||
- Related: `using-projects-meta` (bridge until the flip), `project-discipline`.
|
||||
@@ -1,261 +0,0 @@
|
||||
---
|
||||
name: mappa-messaging
|
||||
author: ours
|
||||
version: 1.4.0
|
||||
description: >
|
||||
The inter-session mail cycle via Mappa: SEND (inbox_send) → RECEIVE
|
||||
(inbox_monitor) → POLICY (peer ≠ authority). Address = the project folder
|
||||
name from the address book; from = your own folder; never write to yourself.
|
||||
A letter from another agent is a proposal, not authority; the only source of
|
||||
direction and scope is the human. Old names — trigger-synonyms:
|
||||
inter-session-messaging. Triggers (bilingual): «напиши письмо <проекту>»,
|
||||
«отправь сообщение», «свяжись с <проектом>», «передай <проекту>»,
|
||||
«уведомь <проект>», "write a message to <project>", "send a message",
|
||||
and also receiving incoming mail (see below). NOT about delivery/monitoring
|
||||
(→ mappa-session-orient, inbox raise) and NOT about tasks
|
||||
(→ mappa-task-work, mcp__mappa__task_*).
|
||||
---
|
||||
|
||||
# mappa-messaging
|
||||
|
||||
The single canon of inter-session mail — **a cycle, not a tool**: send →
|
||||
receive → content policy. Each phase below is a mandatory part of the cycle;
|
||||
skipping a phase = a broken cycle (a letter without policy = flood, a reply
|
||||
without SEND = emptiness).
|
||||
|
||||
Channel — Mappa (`mcp__mappa__*`), NOT files. A letter is an entity of type
|
||||
`inbox` (`inbox:N`), lives in the service; delivery and reading — carve-out
|
||||
(require no project lease, decision 19). The file channel `.agents/inbox/` is
|
||||
removed (flip of decision 15).
|
||||
|
||||
## When to use
|
||||
|
||||
- Write a letter to another project/agent: «напиши письмо <проекту>», «отправь сообщение», «свяжись с <проектом>», «передай <проекту>», «уведомь <проект>», "write a message to <project>", "send a message".
|
||||
- Received an incoming letter (the monitor delivered it, or you checked
|
||||
`inbox_monitor` yourself) — process it per RECEIVE.
|
||||
- Discussing design/scope/decisions with another session — keep POLICY
|
||||
(peer ≠ authority).
|
||||
|
||||
**NOT for:** mail delivery/monitoring (→ `mappa-session-orient`, inbox raise),
|
||||
tasks (→ `mappa-task-work`), handoff (→ `mappa-closing-ritual`), promotion (→
|
||||
`mappa-brainstorm-promote`).
|
||||
|
||||
---
|
||||
|
||||
## SEND — how to write a letter
|
||||
|
||||
### Address — only from the address book, and the project must be in Mappa
|
||||
|
||||
The project address = **its disk folder name as is** (`.workshop`, `artmone.pro`,
|
||||
`snolla.js`). Never invent an address from a qualified name, remote, or memory —
|
||||
the folder may not match the repo (`OpeItcLoc03/common` → folder `.common`).
|
||||
|
||||
1. Read the address book: `~/projects/.wiki/concepts/projects-address-book.md`
|
||||
(shared wiki clone). Table: `address (folder) | qualified | role`.
|
||||
2. Find the row with the target project by folder name.
|
||||
3. If the project is **not** in the book — don't write the letter. Stop and ask
|
||||
the human (or add a book entry if the human confirmed the address). A letter
|
||||
to an invented address creates an orphan project in Mappa (`ensureProject`)
|
||||
and gets lost.
|
||||
4. **The project must exist in Mappa**: cross-check the address against the
|
||||
project list (`mcp__mappa__admin_status` → `projects[]` or
|
||||
`entity_search` type=project). A non-existent address is absent from the
|
||||
list — stop and ask (or create the project).
|
||||
|
||||
### The send call
|
||||
|
||||
```
|
||||
mcp__mappa__inbox_send(
|
||||
project: <recipient address>, # project folder name (from the address book)
|
||||
from: <sender address>, # YOUR folder name (just the name, no owner/topic)
|
||||
subject: <topic>, # optional — short topic
|
||||
body: <markdown body> # free markdown
|
||||
)
|
||||
```
|
||||
|
||||
- `from` — **only your folder name**. No owner, no description. NOT
|
||||
`reviewer-command-index-done-ack` (the letter topic is not an address). A
|
||||
letter with an invented `from` cannot be replied to.
|
||||
- Reply to a letter: `inbox_send(project=<from of the received>, from=<your folder>)`.
|
||||
In `subject` — the `Re: ` prefix, in the body the first line is a reference
|
||||
to the original letter (`inbox:<number>` or its subject). There are no
|
||||
`in_reply_to`/`event` fields in Mappa — instead subject-prefixes `Re:` and
|
||||
`[event: closed]` for lifecycle letters.
|
||||
|
||||
### Ref format: slug/name first, full ref name as anchor
|
||||
|
||||
Convention for prose and links: **name/slug first, ref as anchor** — "the
|
||||
letter about the deploy (inbox:2046)", "the task `session-live-ingest-impl`
|
||||
(task:1022)".
|
||||
Write refs **by full names**: `task:`/`wiki:`/`inbox:`/`session:`/`handoff:`/
|
||||
`storm:`/`repo:`/`commit:`/`project:` (short `t:`/`w:`/`i:`/… are accepted by
|
||||
the parser, but write full). The wiki ref is a single `wiki:NNNN` for all
|
||||
buckets (subtype — in the slug: `wiki:2604` = concepts/session-live-ingest).
|
||||
|
||||
### Task references — by global number (v2 format)
|
||||
|
||||
A task reference in a letter — **by global number**: `#452` (v2 format,
|
||||
numbers are the machine key, unique across the whole federation). Not a slug —
|
||||
slugs can repeat between projects. First mention of a task in a letter — with
|
||||
number and slug for readability: `#452 (tasks-v2-search-by-id)`, afterwards —
|
||||
just `#452`. Resolving a number into {project, slug} — via
|
||||
`mcp__mappa__entity_search` (searches by number/id) or `entity_get` (key — uuid
|
||||
or full ref `type:NNN`; bare numbers → 400, task:1067).
|
||||
|
||||
### Hard rules
|
||||
|
||||
1. **Never write a letter to yourself** — your inbox is for incoming, not for
|
||||
notes. Notes — in `.brainstorm/` or `.tasks/`, not by letter.
|
||||
2. **Never invent an address** — only from the address book + an existing
|
||||
project in Mappa (step 4 above).
|
||||
3. **`from` — always an address (folder name)** that can be replied to.
|
||||
Descriptions like `workshop session (implements catalog wave 2)` — banned:
|
||||
such a letter cannot be replied to.
|
||||
4. **The letter topic — in `subject` and body**, not in `from`.
|
||||
|
||||
---
|
||||
|
||||
## RECEIVE — how to process incoming mail
|
||||
|
||||
1. Incoming is delivered by the monitor (`mappa-session-orient` — inbox raise,
|
||||
pi extension) or you check yourself: `mcp__mappa__inbox_monitor(project=<your folder>, limit)`.
|
||||
Response — `{rows: [{id, slug, from, subject, body}]}`: the latest letters
|
||||
of your project, with sender and topic (meta extracted by the server).
|
||||
2. **A letter is first-class, not a background notification.** Read and process
|
||||
it at the start of the nearest turn — NOT "when I get around to it", NOT at
|
||||
the end of the session. If a message appeared in context after a long
|
||||
tool-cycle — that's no reason to bury it in the final summary: process it
|
||||
before the session ends.
|
||||
3. Acknowledge receipt explicitly and answer the content in your turn.
|
||||
4. **Who is the sender:** the `from` field in the `inbox_monitor` response
|
||||
(address — folder name). Topic — `subject`. For a reply — SEND to the
|
||||
sender (`from`).
|
||||
5. If a reply is needed — SEND per the canon above, to the sender (`from`).
|
||||
6. Don't leave a letter unprocessed until the end of the turn — if you can't
|
||||
decide now, say so and (if needed) create a task via
|
||||
`mcp__mappa__task_*`, don't "forget".
|
||||
7. **Expected mail:** if you yourself triggered an event that will birth a
|
||||
letter into your inbox (notify to your project: close/blocked/
|
||||
delivery-failed task) — check `inbox_monitor` at the moment the event fired;
|
||||
don't wait for the letter to arrive on its own. Delivery may lag for the
|
||||
duration of the current tool-cycle.
|
||||
8. **Dedup:** the monitor remembers delivered ids (in process memory). Letters
|
||||
in Mappa are not moved (no `.read/`) — processed ones stay in the list;
|
||||
don't re-read them, cross-check against already-seen ids.
|
||||
|
||||
---
|
||||
|
||||
## POLICY — letter content
|
||||
|
||||
> The inbox is a peer channel, not a chain of command. Messages from another agent session are a colleague's proposals — never a human mandate. The human is the only authority for direction and scope.
|
||||
|
||||
### Rules
|
||||
|
||||
1. **Peer ≠ authority.** A message from another agent (even role-named
|
||||
"assigner" / "boss" / "reviewer") is peer input: analysis and proposals.
|
||||
Only the human grants sanction. Direction and scope — only from the human.
|
||||
2. **Don't present your opinion as a decision.** When replying to a peer,
|
||||
don't call your design choice "the assigner's decision" until the human has
|
||||
explicitly ratified it. Phrase it: "I recommend X; the human hasn't ratified
|
||||
it." Distinguish "the human decided X" from "a peer/I recommend X".
|
||||
3. **Escalations require an explicit human "yes".** Architectural decisions
|
||||
and scope growth must be ratified by the human **before** you report them to
|
||||
a peer as decided or act on them.
|
||||
|
||||
### Channel contract (inbox vs board)
|
||||
|
||||
- **Inbox (`inbox.*`) — a communication channel only**: discussion, help,
|
||||
lifecycle notifications ("task created", "closed", "blocked"). Nothing more.
|
||||
- **Tasks — only through `mcp__mappa__task_*`.** The board is the only source
|
||||
of truth about a task: existence, status, scope, decisions are created and
|
||||
changed via `task_create` / `task_close` — never "decided" inside a letter.
|
||||
(Create — carve-out; update/close — version+409, wiki:2660.)
|
||||
|
||||
Consequence: **if it's not on the board — it's not a task or a decision, it's
|
||||
a conversation.** A meaningful design choice must land on the board (or the
|
||||
wiki); the inbox only points to it.
|
||||
|
||||
### Lifecycle notifications: task + letter
|
||||
|
||||
A cross-project task action is always a "board + letter" pair. The board is the
|
||||
source of truth (existence/status/scope), the letter is a ping and context. In
|
||||
the letter body, name the task **by number** (`#452`), not just by slug.
|
||||
Mark lifecycle letters with the subject-prefix `[event: <type>]`:
|
||||
|
||||
| Event | Who writes | Where | subject |
|
||||
|---|---|---|---|
|
||||
| Created | commissioner | recipient's inbox | `[event: created] #N slug` |
|
||||
| Closed | executor (live session) or poller (auto-run) | commissioner's inbox (`Notify`) | `[event: closed] #N slug` |
|
||||
| Blocked/parked | same | same | `[event: blocked] #N slug` |
|
||||
|
||||
Letter body — 1-2 lines + numbers/slugs, don't duplicate the board. A live
|
||||
session learns about a task ONLY through the letter (the board doesn't ping);
|
||||
the commissioner learns about closing only via `Notify`/letter. Assignment
|
||||
rule — `mappa-delegation` (the "board+letter pair" step); closing rule —
|
||||
`mappa-task-work` (close).
|
||||
|
||||
### What this is against
|
||||
|
||||
Two sessions ping-pong, each agrees with the other's frame and adds scope, the
|
||||
human is nominally in the loop. Echo-chamber signature: fast replies, agreement
|
||||
with your frame, scope growth every round. This is
|
||||
`user_context_agents_path_of_least_resistance` one level up: sessions bypass
|
||||
human ratification — fake "decided" through mutual agreement.
|
||||
|
||||
### Circuit-breaker
|
||||
|
||||
Noticing scope growth without an explicit human "yes" — **stop and ask the
|
||||
human**: "I'm a peer session, not a human authority; I'm escalating scope here;
|
||||
do you really want this to go out as decided?"
|
||||
|
||||
**Multi-session caveat — don't shout "override" from partial sight.** When the
|
||||
human runs several sessions, your view of what they ratified is partial. A peer
|
||||
acting on the "unratified" may have real human sanction from a channel you
|
||||
don't see. On an apparent violation — **ask "did you ratify this in another
|
||||
channel?"**, don't accuse. Lesson 2026-06-16: workshop called the close in
|
||||
common a "fake attribution of ratification"; in reality the human approved
|
||||
directly in the common channel while workshop was still discussing. Surface the
|
||||
gap with a question — the human reconciles the channels.
|
||||
|
||||
### Why this exists
|
||||
|
||||
Arose 2026-06-16: workshop and common ran a multi-round design exchange over
|
||||
the inbox; workshop escalated the design (tamper-guard → prevention →
|
||||
oracle-integrity → runner-owns-verifier → close-moves) and reported every step
|
||||
as "the assigner's decision" — implying human sanction that didn't exist.
|
||||
common recognized the echo chamber, read its own stop-hook, and correctly
|
||||
refused to implement the unratified redesign, asking the human. The methodology
|
||||
lives in the skill, not in per-session memory.
|
||||
|
||||
---
|
||||
|
||||
## What NOT to do
|
||||
|
||||
| Temptation | Reality |
|
||||
|---|---|
|
||||
| "A letter is a quick way to settle it, I'll formalize later" | If it's not on the board — it's not a task or a decision, it's a conversation. Design choice → board/wiki, the letter only pings. |
|
||||
| "I'll write to the .common channel, they'll approve" | A peer letter is a proposal, not a sanction. The human is the only authority for direction and scope. |
|
||||
| "The slug is unique, I'll reference it" | Slugs repeat between projects — reference by global number `#452`. |
|
||||
| "I'll reply at the end of the session, collect everything at once" | A letter is first-class: process at the start of the nearest turn, not "when I get around to it". |
|
||||
| "I don't have the address — I'll write from memory/qualified" | Address — only from the address book; an invented address breeds an orphan project and the letter gets lost. |
|
||||
|
||||
## Red flags
|
||||
|
||||
- Writing a letter to yourself / to an invented address / with a `from`-description.
|
||||
- Ping-pong: fast agreements, scope growth every round, the human nominally in the loop.
|
||||
- Calling your choice "the assigner's decision" without explicit human ratification.
|
||||
- A letter "decides" a task while it's absent from the board.
|
||||
|
||||
All these flags = **stop and ask the human** (or create a task/wiki page).
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Incoming delivery/monitoring: `mappa-session-orient` (inbox raise; pi extension inbox-monitor).
|
||||
- Address book: `~/projects/.wiki/concepts/projects-address-book.md` (shared wiki).
|
||||
- Mappa project list: `mcp__mappa__admin_status` (carve-out, no lease).
|
||||
- Tasks: `mappa-task-work` (board = `mcp__mappa__task_*`).
|
||||
- Handoff: `mappa-closing-ritual` (write) / `mappa-session-orient` (read).
|
||||
- Delegation: `mappa-delegation` (the "board + covering letter" pair).
|
||||
- Related: `recommend-dont-menu` (response style), `project-discipline`.
|
||||
@@ -1,152 +0,0 @@
|
||||
---
|
||||
name: mappa-session-orient
|
||||
author: ours
|
||||
version: 1.1.0
|
||||
description: >
|
||||
Start phase of the forkflow: contract + reading (pull --ff-only → handoff
|
||||
read → inbox raise → liveness summary "alive/dead" → live-ingest query).
|
||||
Also needed for ad-hoc sessions without an AGENTS.md contract. Absorbs
|
||||
pulling-before-work, session-handoff(read), session-inbox-monitor(raise),
|
||||
using-system-snapshot (liveness) + live-ingest query (old names are
|
||||
trigger-synonyms). Boundary: orient answers "alive/dead" in one line; deep
|
||||
diagnosis is outside the suite (escalate to a human / a diagnostic session).
|
||||
Triggers (bilingual): «что на сессии», «кто последним работал», «продолжи с
|
||||
места», «orient me», "what's on the session", "who worked last", "continue
|
||||
from where I stopped", "orient me", session-start ritual, «pull remote before
|
||||
work», "pull remote before work".
|
||||
---
|
||||
|
||||
# mappa-session-orient
|
||||
|
||||
Start phase of the agent cycle: **contract + reading**, a thin layer — answers
|
||||
the question "alive/dead" (one line per section), does not go deep. Also needed
|
||||
for ad-hoc sessions (where there is no AGENTS.md contract — orientation is
|
||||
still mandatory).
|
||||
|
||||
> **Boundary session-orient / ops (w:2605, round 3):** orient = "alive/dead";
|
||||
> ops = "why and what's next". A problem at start → **do not dig deeper**:
|
||||
> hand it to the human or to a diagnostic session (outside the suite).
|
||||
|
||||
## When to use
|
||||
|
||||
- Session start (ritual, order strictly per Steps).
|
||||
- «что на сессии», «кто последним работал», «продолжи с места», «orient me»,
|
||||
"what's on the session", "who worked last", "continue from where I stopped".
|
||||
- Ad-hoc session without a track/task — orientation anyway (contract + reading).
|
||||
|
||||
## Steps (order — the ritual)
|
||||
|
||||
### 1. Contract
|
||||
|
||||
Read the project's `AGENTS.md` (canon; `CLAUDE.md` — legacy pointer). If there
|
||||
is no AGENTS.md — ad-hoc: no contract, but orientation continues (steps 2–6
|
||||
don't depend on it).
|
||||
|
||||
### 2. Pull (pulling-before-work, full cycle)
|
||||
|
||||
`git pull --ff-only` — once at start. Checks in order: git work-tree? (no →
|
||||
silent exit), pull policy (`pull.rebase=true` + `pull.ff=only`, set-if-absent),
|
||||
origin remote? (no → skip), clean tree? (dirty → skip, no stash), HEAD
|
||||
attached? (no → skip), upstream? (no → skip), `git pull --ff-only`.
|
||||
**Never auto-merge/rebase, never stash.** Repeat pull — only on explicit
|
||||
"sync".
|
||||
|
||||
### 3. Handoff read (session-handoff read part)
|
||||
|
||||
1. `mcp__mappa__entity_search(q='', type='handoff', project=<name>, limit=1)` —
|
||||
if empty, silent exit (project's first session).
|
||||
2. **Staleness:** `meta.date` > 7 days → ask the user "the handoff is stale,
|
||||
override or continue?".
|
||||
3. **Summarize + Orient:** retell in one block (summary / open_treks /
|
||||
ask_user / guards / recent_commits): "the previous session proposed X. Do we do it?"
|
||||
4. **Wait.** No actions until the user confirms. Default = orient + ask, no
|
||||
auto-execute.
|
||||
|
||||
### 4. Inbox raise + sweep (session-inbox-monitor)
|
||||
|
||||
Raise the persistent monitor on the project's inbox (pi: the inbox-monitor
|
||||
extension polls `GET /inbox?project=<cwd>`; opt-in — the string
|
||||
`inbox monitor: raise on start` in AGENTS.md, live re-check every tick). Sweep:
|
||||
`mcp__mappa__inbox_monitor(project=<name>)` — unread letters may change the
|
||||
plan; handle each per `mappa-messaging` (a letter is first-class, at the start
|
||||
of the nearest turn).
|
||||
|
||||
### 5. Liveness summary (using-system-snapshot) — "alive/dead"
|
||||
|
||||
One or two probes in the current turn, compress into 3–4 lines, no raw dumps:
|
||||
|
||||
```
|
||||
mcp__mappa__meta_health → 🟢/🔴 Mappa alive (header on outage)
|
||||
mcp__mappa__admin_status → counters by type/project (load)
|
||||
mcp__projects-meta__meta_system_snapshot → poller (running? + projects) / docker (N/N up,
|
||||
else the problematic ones) / tasks (Σ active/blocked,
|
||||
cache — may be stale)
|
||||
```
|
||||
|
||||
**Never assert liveness from memory** — only a tool call in this same turn. If
|
||||
the snapshot shows a problem → **escalate, don't dig**: "problem at start, not
|
||||
investigating — handing to the human / a diagnostic session" (ops outside the
|
||||
suite).
|
||||
|
||||
### 6. Live-ingest query (consumer of session-live-ingest, #1022/#1024)
|
||||
|
||||
Dependency: server #1022 (v0.8.0) + client part #1024 (pi session-sync,
|
||||
.session written by the client). Contract — w:2604.
|
||||
|
||||
1. `mcp__mappa__session_list(project=<name>, stale_minutes?)` — the project's
|
||||
latest sessions, latest-first (`updated_at DESC`), with
|
||||
end-state/ts/meta-triple {project, runtime, machine, folder}.
|
||||
2. **Stale-active detect:** end-state≠clean AND updated_at < now−X →
|
||||
"<runtime>@<machine> was running, not finished" (crash-detect).
|
||||
3. **"Different triple + not finished"** → propose (peer canon, human's
|
||||
decision): ignore / nudge by letter (`mappa-messaging`: letter to that
|
||||
triple) / continue yourself.
|
||||
4. **Same-triple (`/resume`):** same triple {runtime, machine, folder} → load
|
||||
the remainder (pi-native resume or a brief from mappa).
|
||||
|
||||
**Note (2026-08-24):** the `/session` routes are not yet deployed to prod
|
||||
(server #1022 in repo, deploy awaits #1055) — on 404/"no route" the live-ingest
|
||||
query is skipped without failing: orient continues (steps 1–5), the query part
|
||||
— per actual availability.
|
||||
|
||||
## Failure modes
|
||||
|
||||
- **Problem at start** (service down, red snapshot, pull conflict) → don't dig:
|
||||
escalate to the human / a diagnostic session (ops outside the suite).
|
||||
- **Pull diverged** → "⚠️ diverged — resolve manually"; no auto-merge/rebase.
|
||||
- **Handoff stale (>7 days)** → ask the user, don't silently override.
|
||||
- **Live-ingest unavailable** (404 no route / no client #1024) → skip step 6,
|
||||
don't block orientation.
|
||||
- **Project not in mappa** (no handoff/session entities) → silent exit on the
|
||||
corresponding steps; the project's first session — normal.
|
||||
|
||||
## Side effects
|
||||
|
||||
- Writes nothing, mutates nothing (orientation read-only: pull — local ff,
|
||||
inbox-raise — monitor, liveness — probes, live-ingest — read).
|
||||
- Raises the persistent inbox monitor (lives until the end of the session).
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- **No auto-execute** from the handoff — orient + ask, no auto-action.
|
||||
- **Don't dig into diagnosis** — orient = "alive/dead"; "why" is outside the suite.
|
||||
- **Don't assert liveness from memory** — only a probe in this same turn.
|
||||
- **No stash / no auto-merge / no auto-rebase** on pull — only `--ff-only`.
|
||||
- **No repeated pull** in the session without an explicit "sync".
|
||||
- **No multi-hop live-ingest chains** — one line "who worked last", the
|
||||
proposal goes to the human.
|
||||
- **Don't write** (handoff/wiki/tasks) at orientation — that's the finish phase
|
||||
(`mappa-closing-ritual`).
|
||||
|
||||
## Reference
|
||||
|
||||
- Finish phase: `mappa-closing-ritual` (handoff write + PROPOSE).
|
||||
- Tasks: `mappa-task-work` (board after orientation).
|
||||
- Mail: `mappa-messaging` (letter replies, nudge a triple).
|
||||
- Knowledge: `mappa-knowledge`. Delegation: `mappa-delegation`.
|
||||
- Live-ingest spec: `concepts/session-live-ingest` (wiki:2604).
|
||||
- **CC hook + operator (task:1061):** in a single-user setup CC sessions carry
|
||||
`operator:vitya` even headless (`claude -p`) — there is no reliable hook
|
||||
signal (detect via CLAUDE_INTERACTIVE if CC sets it). Do not treat operator
|
||||
as a sign "a human is at the wheel"; source of truth — end-state + liveness.
|
||||
- Deep diagnosis (outside the suite): `using-vds-ops` (VDS containers).
|
||||
@@ -1,326 +0,0 @@
|
||||
---
|
||||
name: mappa-task-work
|
||||
author: ours
|
||||
version: 1.4.0
|
||||
description: >
|
||||
The central cycle of working with tasks in Mappa: orientation → work
|
||||
selection (priority/due) → execution → handover (close + review-umbrella) +
|
||||
loop-mode "work the queue". Board = mappa entities (decisions 14/15/19/20);
|
||||
create — carve-out, update/close — optimistic concurrency (version+409),
|
||||
owner = persistent assignee, liveness — from the owner's session (interactive
|
||||
contract, wiki:2660). Absorbs using-tasks + task-format + task-loop (loop-mode
|
||||
INSIDE) + priority-due section (old names — trigger-synonyms).
|
||||
Triggers (bilingual): «что на досках», «возьми таску», «какой статус»,
|
||||
«update status», «pause», «switch to X», «где мы остановились», "work the
|
||||
queue", «поработай очередь», «прогони доску», "what's on the boards", "take
|
||||
a task". Priority = the human's territory: agents set P0-P2/deadline only at
|
||||
creation, default P1; overdue → notify, no auto-bump. NOT about delegation
|
||||
(→ mappa-delegation), NOT about board overview (→ ops/using-system-snapshot).
|
||||
---
|
||||
|
||||
# mappa-task-work
|
||||
|
||||
The central cycle of working with tasks: **orientation → work selection →
|
||||
execution → handover**. Board — mappa entities (`type=task`, `task:N`): read —
|
||||
carve-out; **create — carve-out without a lease; update/close — optimistic
|
||||
concurrency (version+409 → retry)**; **owner = persistent assignee; task
|
||||
liveness — from the owner's session** (interactive contract, wiki:2660; poller
|
||||
outside mappa). The skill is a cycle, not a tool: one mechanics for
|
||||
selection/execution/handover, plus **loop-mode** («поработай очередь») inside —
|
||||
no separate skill is created.
|
||||
|
||||
> **Transitional (file channel).** While the poller/cache reads file boards
|
||||
> (`.tasks/STATUS.md`), the legacy channel lives: file blocks must obey a
|
||||
> strict format (see "Task format" below), mutations — via
|
||||
> `mcp__projects-meta__tasks_*` (Gitea commits). New tasks — via
|
||||
> `mcp__mappa__task_create`. Don't mix.
|
||||
|
||||
## When to use
|
||||
|
||||
- «что на досках», «возьми таску», «какой статус», «update status», «pause», «switch to X», «где мы остановились», "what's on the boards", "take a task".
|
||||
- "work the queue", «поработай очередь», «прогони доску» → **loop-mode**.
|
||||
- Task switch / pause / session end — keep the board consistent.
|
||||
|
||||
**NOT for:** delegating to another agent/project (→ `mappa-delegation`),
|
||||
promotion (→ `mappa-brainstorm-promote`), infra diagnosis (→ `using-vds-ops`),
|
||||
cross-project overview (→ `using-system-snapshot`).
|
||||
|
||||
## MCP surface
|
||||
|
||||
| Operation | Tool | Note |
|
||||
|---|---|---|
|
||||
| Take the next ready task | `mcp__mappa__task_update(project, id, owner=<yours>, status=active…, version)` | **conditional update**: status→active, owner=X, version+409 (whoever is first with the right version — takes it) |
|
||||
| Create a task | `mcp__mappa__task_create(project, slug, title?, description?, status?, priority?, due?)` | **carve-out without a lease**; per-type number (decision 20) |
|
||||
| Close a task | `mcp__mappa__task_close(project, id, version)` | **version-based**: conflict → 409 → retry with the fresh version from task_get |
|
||||
| Update a task | `mcp__mappa__task_update(project, id, parent?, owner?, status?, reason?, version)` | version-based; 409 → retry |
|
||||
| Read a task | `mcp__mappa__entity_get(id)` | key: uuid or full ref `task:NNN` (task:1067); bare number → 400 |
|
||||
| Read a task compact | `mcp__mappa__task_get(id)` | key: uuid or `task:NNN`; bare number → 400 |
|
||||
| Board list | `mcp__mappa__entity_search(q, type='task', project=<name>, limit)` | all statuses |
|
||||
| parent_of tree | `mcp__mappa__graph_tree(root, depth?, fields?, limit?)` | umbrellas/hierarchy |
|
||||
| Related entities | `mcp__mappa__graph_neighbors/backlinks(id)` | refs to the task |
|
||||
| Overdue | `mcp__mappa__admin_overdue_scan(project?)` | P2 job: notify to inbox, no mutations |
|
||||
| Owner liveness | `mcp__mappa__session_list(project, stale_minutes?)` | owner's session alive/stale → task active or not |
|
||||
| Close notification | `mcp__mappa__inbox_send(project=<notify>, from=<yours>, subject, body)` | letter to the commissioner |
|
||||
|
||||
**Owner = persistent assignee; liveness — from the session (interactive
|
||||
contract, wiki:2660).** No TTL/claim/timer on the task: "took a task" =
|
||||
conditional update (`status→active, owner=X` + version, 409 on conflict —
|
||||
whoever is first takes it). Owner liveness — the session: a task with owner=X
|
||||
is active while session X is alive (end-state≠clean, not stale;
|
||||
`session_list(project)`); crash = stale → the human/orchestrator decides
|
||||
(poller outside mappa, works on projects-meta file boards). Write paths
|
||||
(create/update/close) require no leases/claims — only version on update/close
|
||||
(409 on conflict).
|
||||
|
||||
**Refs and ids (#1037/#1028).** Tasks carry `ref: "t:N"` by full name as the
|
||||
first field (`task:N`, convention #1028), `num` next, the global `id` —
|
||||
internal (last, for addressing in tools). Reference a task as
|
||||
`[[task:N]]`/`task:N` in prose (slug/name first, ref as anchor: "the task
|
||||
`mappa-task-work` (task:1062)"), never `#<global id>`.
|
||||
|
||||
## Statuses (emojis for presentation)
|
||||
|
||||
| Emoji | Status | Meaning |
|
||||
|---|---|---|
|
||||
| ⚪ | `ready` | not started, fully defined |
|
||||
| 🔴 | `active` | in progress (usually one) |
|
||||
| 🟡 | `paused` | in progress, resumable |
|
||||
| 🔵 | `blocked` | waiting on external input |
|
||||
| 🟢 | `done` | closed |
|
||||
|
||||
Don't confuse: 🟢 — *done*, not "ready". Ready is ⚪.
|
||||
|
||||
---
|
||||
|
||||
## The cycle
|
||||
|
||||
### Phase 0 — Orientation
|
||||
|
||||
1. **Inbox sweep** — `mcp__mappa__inbox_monitor(project=<name>)`: unread
|
||||
letters may change the plan. Handle each per `mappa-messaging`.
|
||||
2. **Board** — `entity_search(q, type='task', project=<name>, limit=50)`: sort
|
||||
by status (🔴 → 🟡 → ⚪), one line per task, quote the slug.
|
||||
3. If the user named a task — `entity_get(key)` by its ref/uuid (`task:NNN` or uuid; task:1067 — bare numbers rejected).
|
||||
4. Confirm in one sentence: "We're in the middle of X, next step — Y".
|
||||
5. Ask whether the plan is right before acting.
|
||||
|
||||
### Phase 1 — Work selection (priority/due)
|
||||
|
||||
1. **Task selection — from the board list** (`entity_search(q, type='task', project)`):
|
||||
order — **P0 pool first, within it by deadline (overdue first),
|
||||
then P1, then P2**; missing priority = P1 (task-priority-due).
|
||||
"Take a task" = `task_update(project, id, owner=<yours>, status='active',
|
||||
version)` — conditional update: status→active, owner=X, version+409;
|
||||
whoever is first with the right version takes it (interactive contract,
|
||||
wiki:2660). `owner` = `<machine>:<runtime>:<session>`.
|
||||
2. **Local-first recommendation** — cwd board first; cross-project — a footnote
|
||||
(`Cross-project: N 🔴 active — see tasks_aggregate`) only if N>0 and there's
|
||||
no active 🔴 in cwd. Cross-project urgencies are information, not a driver
|
||||
for "what to do here".
|
||||
3. **Priority/Due — the human's territory (task-priority-due section):**
|
||||
- An agent sets `priority`/`due` **only at task creation** (explicit
|
||||
parameters or `**Priority:** P0|P1|P2` / `**Due:** yyyy-mm-dd` lines in
|
||||
the description). Absent → default P1, no deadline.
|
||||
- **After creation an agent doesn't change** priority/deadline — the
|
||||
human's precedent is structural (agent updates are rejected by the
|
||||
server). If you discover the task is actually P0 → park the question to
|
||||
the human, don't bump it yourself.
|
||||
- **Overdue:** due < today with ready/active → `admin_overdue_scan`
|
||||
notifies the inbox **once, without mutations** — no auto-bump/auto-priority
|
||||
change.
|
||||
|
||||
### Phase 2 — Execution
|
||||
|
||||
- **One active task** 🔴 per project. No parallelism.
|
||||
- Read description + per-task file (`<slug>.md`, where present) before starting.
|
||||
- Liveness — from the owner's session (`session_list(project)`), not a timer;
|
||||
long tasks need no heartbeat (interactive contract, wiki:2660).
|
||||
- **`session_break` gate** (from task-loop): if the task description has the
|
||||
`session_break` marker — after close DON'T claim the next one: print
|
||||
`🔚 SESSION BOUNDARY …` and stop (domain-switch / milestone / heavy infra).
|
||||
|
||||
### Phase 3 — Handover (close + review-umbrella)
|
||||
|
||||
1. **Pre-close coverage check.** Collect acceptance criteria from the
|
||||
description. For each — evidence: a test in the diff, an artifact, a design
|
||||
reference. No evidence for a criterion → ask the user "close or wait for
|
||||
coverage".
|
||||
2. Resolve/drop open questions.
|
||||
3. `task_close(project, id, version)` → status `done`. `version` — fresh from
|
||||
`entity_get(key)`/`task_get(key)` (key = uuid or `task:NNN`); conflict (409) → re-GET → retry.
|
||||
4. **Notify letter (cross-project tasks).** If the task came from another
|
||||
project (there's `from:`/`notify:` in description/meta) — `inbox_send` to
|
||||
the commissioner: `project=<notify>`, `subject="[event: closed] <slug>"`,
|
||||
body = the outcome (done, acceptance, references). A live session writes it
|
||||
itself. Task 🟢 ≠ commissioner learned.
|
||||
5. **Review-umbrella for impl tasks** (canon `mappa-delegation`): if the task
|
||||
is implementation and closed — the paired `<slug>-review` should already
|
||||
have been created at assignment (status=blocked, blocker=impl#); closing the
|
||||
impl unblocks the review. Don't create a review yourself if it didn't exist
|
||||
— that's the assigner's job; mention it in the close-note.
|
||||
6. Add a summary line to the handoff/wiki if present.
|
||||
|
||||
### Pause / switch / session end
|
||||
|
||||
1. Current 🔴 → `task_close` if finished (see Phase 3), otherwise mark
|
||||
`status=paused` (owner stays; "where stopped" — in the description or handoff).
|
||||
2. **Inbox sweep** at the task boundary (`inbox_monitor`).
|
||||
3. Take the next one: `task_update(owner, status='active', version)` — the
|
||||
previous stays 🟡.
|
||||
4. Confirm the orientation before starting.
|
||||
|
||||
> **Never lose Where I stopped** — critical field: in the description (last
|
||||
> paragraph) or in the handoff entity (`mappa-closing-ritual`). Before the end
|
||||
> of the session, definitely write the handoff.
|
||||
|
||||
---
|
||||
|
||||
## Loop-mode — «поработай очередь»
|
||||
|
||||
One trigger surface: «поработай очередь» / "work the queue" / «прогони доску»
|
||||
→ this mode. Work the board **in this session**: take → work → close → take,
|
||||
until the queue is empty or the user said stop. **Interactive cycle, not a daemon.**
|
||||
|
||||
```
|
||||
task_update(owner, status=active, version) → 409? re-GET → retry → empty? → STOP "board is empty"
|
||||
↓ task
|
||||
work in this session (read description + <slug>.md)
|
||||
↓
|
||||
finished? no → park: blocked (external) | paused (resumable) → next
|
||||
↓ yes
|
||||
consult_policy: human-only/strict-human → STOP before close/commit, ask the user
|
||||
↓ auto
|
||||
pre-close coverage check → task_close
|
||||
↓
|
||||
session_break on the task? → yes: print 🔚 SESSION BOUNDARY, STOP
|
||||
↓ no
|
||||
next …
|
||||
```
|
||||
|
||||
- **An empty queue is a natural stop, not a wait-loop.** No `CronCreate`, no
|
||||
subagent spawn, no short pollers — that's the work of a separate poller.
|
||||
Long watch ("keep working until I say stop" + explicit "keep checking") —
|
||||
only one `ScheduleWakeup` with an interval ≥1200s, never `CronCreate`.
|
||||
- **Non-finishable task:** external blocker → `status=blocked` + blocker
|
||||
(concrete fact + what's needed); you interrupted (budget/stop) →
|
||||
`status=paused` + where_stopped. One fallen task doesn't stop the cycle —
|
||||
park and continue.
|
||||
- **No heartbeat needed** — owner liveness from the session (wiki:2660); a
|
||||
long task with a live session doesn't "expire".
|
||||
- **Consult gate:** `auto` → autopilot up to close; `human-only`/`strict-human`
|
||||
→ work, then **STOP before close/commit** and ask the user. Push is never
|
||||
automatic (project-discipline Rule 4: commit freely, push on explicit grant).
|
||||
|
||||
---
|
||||
|
||||
## Task format (from task-format)
|
||||
|
||||
### Primary: mappa task_create
|
||||
|
||||
Task creation — **via the tool, not by hand** (decision 20): carve-out, no
|
||||
lease/claim needed for create (wiki:2660). The number `task:N` is assigned by
|
||||
the server — don't invent it.
|
||||
|
||||
```
|
||||
mcp__mappa__task_create(
|
||||
project: <project name>, // required
|
||||
slug: <kebab-case>, // required, latin
|
||||
title: <one line>, // optional
|
||||
description: <markdown>, // body; [[refs]] → edges (decision 4)
|
||||
status: ready | active | paused | blocked | done, // default ready
|
||||
priority: P0 | P1 | P2, // only at creation; absent → P1
|
||||
due: yyyy-mm-dd // only at creation; absent = none
|
||||
)
|
||||
```
|
||||
|
||||
Slug rules: short, lowercase, kebab-case, latin. Description — markdown,
|
||||
`[[refs]]` to related. Priority/Due — at creation OR as lines in the
|
||||
description (`**Priority:** P0|P1|P2`, `**Due:** yyyy-mm-dd`; explicit
|
||||
parameters override).
|
||||
|
||||
### Legacy: .tasks/STATUS.md block (interim until the poller flips)
|
||||
|
||||
While the file poller is not switched to mappa (#984), blocks in
|
||||
`.tasks/STATUS.md` must obey a strict format — otherwise the poller silently
|
||||
skips:
|
||||
|
||||
```markdown
|
||||
## ⚪ [#1234 my-task-slug] — One-line description.
|
||||
|
||||
**Status:** ready
|
||||
**Created:** 2026-08-23
|
||||
**Where I stopped:** (not started)
|
||||
**Next action:** First concrete step the claiming agent runs.
|
||||
**Branch:** master
|
||||
**Weight:** needs-claude
|
||||
**Notify:** OpeItcLoc03/workshop
|
||||
<!-- created-by: you@machine / from: OpeItcLoc03/workshop / 2026-08-23 -->
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
Three load-bearing rules: **(1)** the header is exactly `## <emoji> [#<n>
|
||||
<slug>] — <desc>` (h2, one emoji, `[#<n> <slug>]`, separator ` — `); **(2)**
|
||||
fields are `**Label:** value` lines, bullets are ignored; **(3)**
|
||||
`**Created:**` is mandatory.
|
||||
|
||||
Fields the poller parses: `**Weight:**` (cheap-ok | needs-claude |
|
||||
needs-human — **mandatory** for auto-claim), `**Notify:**` (<owner>/<repo>),
|
||||
`**Requirements:**`, `**Runtime allowed:**`, `**Consult policy:**`,
|
||||
`**Blocker:**` (only on 🔵), `**Priority:**`/`**Due:**` (as above).
|
||||
`**Owner:**/`**Claim token:**/`**Claim expires at:**` — claim stamp, written
|
||||
and cleared by the poller; a stuck stamp on ⚪ blocks the poller.
|
||||
|
||||
**Weight — the field that decides the take:** without `**Weight:**` the poller
|
||||
parks to 🔵 (`no backend for weight_tier: unknown`). Ordinary code →
|
||||
`needs-claude`; critical infra (poller, MCP servers, deploy, CI, git hooks) →
|
||||
`needs-human`.
|
||||
|
||||
---
|
||||
|
||||
## Failure modes
|
||||
|
||||
- **version conflict** on update/close → version is stale; re-GET the fresh
|
||||
version, retry. Don't "resolve the conflict" by overwriting without version
|
||||
(last-write-wins).
|
||||
- **task_close on an unfinished task** → never. Park (blocked/paused).
|
||||
- **owner on a task without a live session** → the task is formally active but
|
||||
the owner is stale; ask the human (advisory, not a lock).
|
||||
- **notify not specified (legacy)** → without it the boss won't learn about completion.
|
||||
- **weight not specified (legacy)** → the poller parks (no backend for weight_tier).
|
||||
- **update Priority/Due after creation** → the server rejects; park the
|
||||
question to the human, don't bump yourself.
|
||||
|
||||
## What NOT to do
|
||||
|
||||
- **Don't invent numbers** — `task:N` is assigned by the server (decision 20).
|
||||
- **Version discipline:** update/close — always with version (409 on conflict →
|
||||
re-GET → retry). Create — carve-out without a lease (wiki:2660).
|
||||
- **One active task** — only one 🔴 per project.
|
||||
- **Never close without a coverage check** — evidence for every acceptance criterion.
|
||||
- **Don't close unfinished work** — park, not close.
|
||||
- **Don't bump priority/due after creation** — the human's territory.
|
||||
- **Don't "settle" tasks by letter/in chat** — the board is the only source of
|
||||
truth (mappa-messaging canon: "if it's not on the board — it's not a task").
|
||||
- **Don't poll an empty queue** — empty = stop and report; no daemon/CronCreate.
|
||||
- **Don't autopilot human-only/strict-human** through close/commit; push — only on grant.
|
||||
- **Don't batch tasks_create into one repo** — sha-lock conflicts; serialize.
|
||||
|
||||
## Red flags — STOP
|
||||
|
||||
- "I'll set a timer to check for new tasks" → no. Stop on an empty queue.
|
||||
- "I'll spawn a background worker to run the board" → no. One cycle, this session.
|
||||
- "The task isn't ready, but I'll close and mark it" → never. Park.
|
||||
- "The task is clearly P0, I'll bump it myself" → no. Ask the human.
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
- Delegation (assigning to agents): `mappa-delegation`.
|
||||
- Mail (covering letters, notify): `mappa-messaging`.
|
||||
- Knowledge (wiki-ingest after closing): `mappa-knowledge`.
|
||||
- Session finish (handoff write): `mappa-closing-ritual`.
|
||||
- Session start (pull/handoff/inbox/snapshot): `mappa-session-orient`.
|
||||
- Promotion: `mappa-brainstorm-promote` (review-umbrella for promotions).
|
||||
- Cross-project overview: `using-system-snapshot` (liveness) / `mcp__projects-meta__tasks_aggregate`.
|
||||
90
skills/ops-browser/SKILL.md
Normal file
90
skills/ops-browser/SKILL.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: ops-browser
|
||||
author: ours
|
||||
version: 0.1.0
|
||||
description: >
|
||||
Use when a task needs a real browser — личный кабинет, заказы, чеки, yt-digest,
|
||||
скриншот живого сайта, «дёрни внутренний API». Свой СКРЫТЫЙ браузер агента:
|
||||
отдельный профиль, CDP, `fetch()` ИЗ страницы, хендофф человеку для пароля/капчи.
|
||||
---
|
||||
|
||||
# ops-browser — свой скрытый браузер агента
|
||||
|
||||
**Есть задача, которой нужен браузер? Работай в своём браузере, скрыто, и не подсовывай куки.**
|
||||
Профиль владеет сессией сам (куки продлеваются браузером, а не руками) — это и есть лечение
|
||||
боли «куки протухли».
|
||||
|
||||
## Когда ЭТОТ инструмент, а когда другой
|
||||
|
||||
| Ситуация | Чем работать |
|
||||
|---|---|
|
||||
| Публичная страница без логина | `web_search` / `web_extract` / curl — браузер не нужен |
|
||||
| Личный кабинет, заказы, чеки, внутренний API сайта, антибот | **`ops-browser.sh`** (этот скил) |
|
||||
| Нужны ЕГО логины и ЕГО Chrome (Avito, кабинеты поставщиков) | канал оператора: Hermes `browser_exec` / pi тул `browser` / CC `chrome-devtools` — всё под арендой `driver.lock` |
|
||||
| Посмотреть глазами, кликнуть по живому сайту, показать ему | панель предпросмотра (`desktop_preview` + `drive_preview`) — без JS, только текст/клики |
|
||||
|
||||
`ops-browser` не заменяет канал оператора: там его сессии, здесь — **мой** профиль.
|
||||
И то и другое живёт под правилом «один водитель» (у ops свой замок `ops.lock`).
|
||||
|
||||
## Команды
|
||||
|
||||
```bash
|
||||
OB="$HOME/.config/browser-harness/bin/ops-browser.sh"
|
||||
bash "$OB" start # headless + аренда (окон нет)
|
||||
bash "$OB" open https://site/path # перейти
|
||||
bash "$OB" read 2000 # видимый текст страницы
|
||||
bash "$OB" eval 'fetch("/api/x",{credentials:"include"}).then(r=>r.json()).then(j=>JSON.stringify(j))'
|
||||
bash "$OB" screenshot [path] # PNG, печатает путь
|
||||
bash "$OB" cookies https://site # имена кук (без значений)
|
||||
bash "$OB" status # аренда + CDP + текущая страница
|
||||
bash "$OB" stop # ШТАТНО закрыть и отпустить
|
||||
bash "$OB" handoff <url> # человеку: пароль/2FA/капча (см. ниже)
|
||||
```
|
||||
|
||||
`eval` выполняет JS **в контексте страницы** — значит `fetch()` идёт с её куками и заголовками:
|
||||
так дёргают внутренние эндпоинты приложения (то, что недоступно извне и чего не умеет панель).
|
||||
|
||||
## Хендофф человеку (пароль, 2FA, капча, согласие)
|
||||
|
||||
Останавливаюсь и отдаю экран **сам**, без «скажи готово»:
|
||||
|
||||
1. `bash "$OB" handoff <url>` — закрывает headless (профиль нельзя открыть дважды) и поднимает
|
||||
**видимое** окно ТОГО ЖЕ профиля на нужной странице;
|
||||
2. человек вводит, что нужно (пароль/2FA/капчу вводит только он);
|
||||
3. признак успеха ловлю сам: URL ушёл со страницы логина/auth **или** выросли куки домена;
|
||||
4. штатно закрываю окно и возвращаюсь в headless — логин остался в профиле.
|
||||
|
||||
Скриншот делаю **до** шага (что от человека хотят) и **после** (что получилось); **во время ввода
|
||||
пароля не снимаю**. Если признак успеха не увидел — окно остаётся открытым, а я говорю об этом прямо.
|
||||
|
||||
## Границы (не двигаются)
|
||||
|
||||
- Пароли, 2FA, капчу — **вводит человек**. Я к ним не подхожу.
|
||||
- Деньги, оформление заказов, формы с перс.данными — только с явного согласия оператора.
|
||||
- Куки не выгружаю и не расшифровываю; содержимое залогиненных страниц не уходит в логи/вику.
|
||||
- Антибот-челленджи не обхожу; если сайт невзлюбил `headless` — это повод для `handoff`, а не для трюков.
|
||||
|
||||
## Грабли (стоили времени, проверены живьём)
|
||||
|
||||
- **Закрывать только штатно** (`stop` → `Browser.close`). Жёсткий kill может не сбросить профиль →
|
||||
потеря логина. Сессионные куки (без срока) перезапуск не переживают по дизайну — настоящие логины живут.
|
||||
- **MSYS-пути**: нативным программам нужен вид `C:/...` (`cygpath -m`), а `powershell -File` —
|
||||
только `C:\...` (`cygpath -w`). Иначе «модуль не найден» / «файл .ps1 не найден».
|
||||
- **Экранирование PowerShell внутри bash-строки тихо ломается** (`$_` подменяется) — PS-запросы
|
||||
держим в отдельных `.ps1`.
|
||||
- **Профиль нельзя открыть дважды** — перед видимым окном headless обязан остановиться (это делает `handoff`).
|
||||
- **Профиль**: `~/.config/browser-harness/profiles/ops`; порт CDP: `OPS_CDP_PORT` (9346).
|
||||
|
||||
## Аренда
|
||||
|
||||
```bash
|
||||
BL="$HOME/.config/browser-harness/bin/browser-lease.sh"
|
||||
bash "$BL" --lock ops status # состояние ops-замка (у Chrome оператора — driver.lock)
|
||||
```
|
||||
|
||||
Чужой держатель `ops.lock` → `start` честно отказывает (rc=1). Мёртвый держатель отпускается по PID/TTL.
|
||||
Идёшь в браузер **сам** (в обход `ops-browser.sh`)? Возьми `ops.lock` так же, как любой другой харнесс.
|
||||
|
||||
Контракт: mappa `concepts/ops-browser-contract` (проект `pi-extensions`),
|
||||
требования `requirements:42`, план `plan:35`. Исходники: репо `pi-extensions/scripts/browser/`
|
||||
(установка `just install-browser`).
|
||||
@@ -39,7 +39,7 @@ Karpathy / `.tasks/` (см. using-wiki/using-tasks, legacy-раздел).
|
||||
| `README.md` | minimal stub | Skipped if file exists. |
|
||||
| вики | mappa (`using-wiki`) | сущности `type=wiki` в сервисе; файловый layout — только вне mappa (легаси). |
|
||||
| таски | mappa (`using-tasks`) | сущности `type=task` в сервисе; файловый `.tasks/` — только вне mappa (легаси). |
|
||||
| `AGENTS.md` | `assets/AGENTS.md.template` | **Canon** — skill triggers (`use project wiki`, `use task management system`, etc.). On non-Windows hosts, swap the `we're on Windows` line for `we're on Linux` / `we're on macOS`. On upgrade, the template is treated as a canonical set and merged idempotently — only missing trigger lines are appended after user confirm. Re-runs are no-ops. |
|
||||
| `AGENTS.md` | `assets/AGENTS.md.template` | **Canon** — mappa-agnostic generic triggers (caveman, pull remote before work, tdd, interns, recommend, platform). Mappa-специфичные триггеры (`inbox monitor: raise on start`, `session sync: write to mappa`, `use project wiki`, `use task management system`, `check across all projects`) инжектятся через mappa-bootstrap (Step 5.7.2). On non-Windows hosts, swap the `we're on Windows` line for `we're on Linux` / `we're on macOS`. On upgrade, the template is treated as a canonical set and merged idempotently — only missing generic trigger lines are appended after user confirm. Re-runs are no-ops. |
|
||||
| `CLAUDE.md` | generated pointer | `Canon is AGENTS.md. Read AGENTS.md.` — legacy pointer for tooling that looks for the old name. |
|
||||
| `.wiki/concepts/bootstrap-manifest.md` | generated | Records which skill versions initialized the project, so cross-project layout drift is debuggable. |
|
||||
|
||||
@@ -53,7 +53,7 @@ Karpathy / `.tasks/` (см. using-wiki/using-tasks, legacy-раздел).
|
||||
3. **Steps 1–5.** Create or skip each piece in order — git, README, вики/таски
|
||||
(mappa, см. using-wiki/using-tasks), `AGENTS.md` + `CLAUDE.md` pointer.
|
||||
4. **Step 5.5.** Write `bootstrap-manifest.md` recording the versions of
|
||||
`project-bootstrap`, `project-discipline`, `setup-interns`, and
|
||||
`project-bootstrap`, `setup-interns`, and
|
||||
`using-interns` used.
|
||||
5. **Step 5.6.** Skill dependencies check. Walk the canonical trigger list
|
||||
in `AGENTS.md`, look each up in an embedded `trigger → fulfiller` map,
|
||||
@@ -102,8 +102,9 @@ target with `CLAUDE_SKILLS_DIR=/path bash scripts/install.sh …`.
|
||||
|
||||
- [`using-wiki`](../using-wiki/) — runtime policy for the mappa wiki (v2).
|
||||
- [`using-tasks`](../using-tasks/) — runtime policy for the mappa task board (v2).
|
||||
- [`project-discipline`](../project-discipline/) — cross-project rules
|
||||
activated by the `follow project discipline` trigger.
|
||||
- kzntsv-flavored cross-project discipline (activated per-project by
|
||||
`mappa-bootstrap`, which selects the methodology flavor) — moved to
|
||||
`victor/mappa-vitya-skills` (легаси, поглощено монорепо) — mappa-kzntsv-project-discipline.
|
||||
- [`setup-interns`](../setup-interns/), [`using-interns`](../using-interns/) —
|
||||
pair behind the `delegate to interns when allowed` trigger; cheap-LLM
|
||||
delegation under a per-session permission grant.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: project-bootstrap
|
||||
author: ours
|
||||
version: 3.0.1
|
||||
version: 3.3.0
|
||||
description: >
|
||||
Initializes or upgrades a project in the current folder: git, .gitignore, README.md,
|
||||
.wiki/ using Karpathy's method, .tasks/ for task tracking, AGENTS.md (canon) with
|
||||
@@ -9,6 +9,7 @@ description: >
|
||||
Creates remote Gitea repo and syncs projects-meta cache for greenfield projects.
|
||||
Includes the mappa-bootstrap-project module (v3, решение 4 mappa-as-product):
|
||||
mappa MCP connect + mappa-конвенции + методика-install (версия в манифест).
|
||||
Creates the `.mappa` marker (wiki:3340) so the folder is a mappa project.
|
||||
Use this skill when the user says "initialize project", "bootstrap", "setup project",
|
||||
"upgrade project", "add wiki", "add tasks", "start project", "set everything up",
|
||||
"create new project", or launches the agent in a new folder and wants a full setup.
|
||||
@@ -36,6 +37,7 @@ ls -A 2>/dev/null | grep -q . && echo "empty:no" || echo "empty:yes"
|
||||
[ -d .tasks ] && echo "tasks:yes" || echo "tasks:no"
|
||||
[ -f CLAUDE.md ] && echo "claude:yes" || echo "claude:no"
|
||||
[ -f README.md ] && echo "readme:yes" || echo "readme:no"
|
||||
[ -d .mappa ] && echo "mappa-marker:yes" || echo "mappa-marker:no"
|
||||
```
|
||||
|
||||
Determine mode:
|
||||
@@ -114,7 +116,7 @@ source ~/.config/projects-mcp/auth.toml 2>/dev/null || true
|
||||
Get-Content ~/.config/projects-mcp/auth.toml | Select-String "base_url|token"
|
||||
```
|
||||
|
||||
If auth file missing → stop and tell user: run `/setup-projects-meta` first.
|
||||
If auth file missing → stop and tell the user (нужны Gitea-креды; скил `setup-projects-meta` удалён 2026-08-25).
|
||||
|
||||
### Validate project name
|
||||
|
||||
@@ -387,22 +389,24 @@ Template contents (`assets/AGENTS.md.template` — source of truth):
|
||||
# Agent instructions. Each line is a trigger for an installed skill.
|
||||
|
||||
talk like a caveman
|
||||
use project wiki
|
||||
use task management system
|
||||
check across all projects
|
||||
pull remote before work
|
||||
follow project discipline
|
||||
follow tdd-criteria
|
||||
delegate to interns when allowed
|
||||
recommend, don't menu
|
||||
we're on Windows
|
||||
```
|
||||
|
||||
The `check across all projects` line activates the `using-projects-meta` skill
|
||||
so cross-project task aggregation and the shared `projects-wiki` are available
|
||||
without an explicit verbal trigger. The skill is a no-op until the
|
||||
`projects-meta-mcp` server is registered — install via `setup-projects-meta`
|
||||
on a fresh machine if `mcp__projects-meta__*` tools are missing.
|
||||
Mappa-специфичные триггеры (`check across all projects`, `inbox monitor:
|
||||
raise on start`, `use project wiki`, `use task management system`, `session
|
||||
sync: write to mappa`) **не в шаблоне** — инжектятся через mappa-bootstrap
|
||||
(Step 5.7.2), project-bootstrap mappa-agnostic. `check across all projects`
|
||||
активирует **mappa** tooling (`mcp__mappa__*`) — cross-project boards, shared
|
||||
wiki и реестр проектов живут в mappa.
|
||||
|
||||
The file-based `projects-meta-mcp` and its skills (`using-projects-meta`,
|
||||
`setup-projects-meta`, `meta-host-routing`, `setup-wiki`,
|
||||
`setup-tasks`) were removed 2026-08-25; the shared `projects-wiki` files are
|
||||
stubs «не читать, не править» — канон mappa shared-scope.
|
||||
|
||||
The `pull remote before work` line activates the `pulling-before-work` skill,
|
||||
which runs one `git pull --ff-only` at session start (and on explicit re-sync
|
||||
@@ -412,15 +416,6 @@ upstream — never auto-merges, stashes, or pushes. Install the skill on the hos
|
||||
if `pulling-before-work` is not in `~/.claude/skills/`; otherwise the trigger is
|
||||
silently dead like any other absent skill.
|
||||
|
||||
The `follow project discipline` line activates the `project-discipline` skill,
|
||||
which codifies four cross-project rules: (1) project AGENTS.md / CLAUDE.md /
|
||||
.wiki/CLAUDE.md / .tasks/ override defaults from any other skill; (2) all work on master/main,
|
||||
no feature branches without explicit user approval; (3) version bump on every
|
||||
edit of versioned artifacts per semver, recorded in commit; (4) commit freely,
|
||||
push only after explicit per-session approval. Install the skill on the host
|
||||
if `project-discipline` is not in `~/.claude/skills/`; otherwise the trigger is
|
||||
silently dead like any other absent skill.
|
||||
|
||||
The `follow tdd-criteria` line activates the `tdd-criteria` skill, which enforces
|
||||
test-driven development by default with four bright-line carve-outs (visual CSS,
|
||||
spike exploration, oneshot scripts, pure wrappers) and four anti-loophole rules
|
||||
@@ -434,8 +429,8 @@ which lets Claude offload predictable bulk I/O and summarization tasks
|
||||
(reading 3+ files, distilling long transcripts) to cheap intern LLMs via the
|
||||
local `interns` MCP server (`mcp__interns__bulk_text_read`,
|
||||
`mcp__interns__transcript_distill`, etc.) — saves Anthropic quota at ~125× the
|
||||
per-call cost reduction on bulk reads. Per-session permission grant mirrors
|
||||
`project-discipline` Rule 4: ask-mode default, conversational grant / revoke,
|
||||
per-call cost reduction on bulk reads. Per-session permission grant mirrors the
|
||||
`mappa-kzntsv-project-discipline` Rule 4 (skill moved from `victor/mappa-vitya-skills`, легаси, поглощено монорепо): ask-mode default, conversational grant / revoke,
|
||||
always-ask paths for `.env` / secrets / keys / SSH credentials even with an
|
||||
active grant, session-end reset. The skill is a no-op until the `interns` MCP
|
||||
server is registered — install via `setup-interns` on a fresh machine if
|
||||
@@ -478,7 +473,6 @@ Skills used to initialize this project's `.wiki/` and `.tasks/` layout, with the
|
||||
| Skill | Version | Role |
|
||||
|---|---|---|
|
||||
| `project-bootstrap` | <version> | orchestrator |
|
||||
| `project-discipline` | <version> | cross-project policy |
|
||||
| `setup-interns` | <version> | interns MCP server install (one-time, per machine) |
|
||||
| `using-interns` | <version> | interns runtime policy + per-session permission grant |
|
||||
| `mappa-*` (методика, модуль 5.7) | <version of reference-package skills> | mappa-циклы: session-orient / task-work / knowledge / messaging / delegation / brainstorm-promote / closing-ritual |
|
||||
@@ -515,16 +509,16 @@ Mismatch between template and map → silent gaps in the recommendation.
|
||||
| `talk like a caveman` | `caveman` | skill | `~/.claude/skills/caveman/SKILL.md` | `bash scripts/install.sh caveman` |
|
||||
| `use project wiki` | `using-wiki` | skill | `~/.claude/skills/using-wiki/SKILL.md` | `bash scripts/install.sh using-wiki` |
|
||||
| `use task management system` | `using-tasks` | skill | `~/.claude/skills/using-tasks/SKILL.md` | `bash scripts/install.sh using-tasks` |
|
||||
| `check across all projects` | `using-projects-meta` | skill | `~/.claude/skills/using-projects-meta/SKILL.md` | `bash scripts/install.sh using-projects-meta` |
|
||||
| `check across all projects` | mappa (`mcp__mappa__*`) | MCP | `mcpServers.mappa` in `~/.claude.json` | — |
|
||||
| `pull remote before work` | `pulling-before-work` | skill | `~/.claude/skills/pulling-before-work/SKILL.md` | `bash scripts/install.sh pulling-before-work` |
|
||||
| `session handoff: read on start, write on end` | `session-handoff` | skill | `~/.claude/skills/session-handoff/SKILL.md` | `bash scripts/install.sh session-handoff` |
|
||||
| `follow project discipline` | `project-discipline` | skill | `~/.claude/skills/project-discipline/SKILL.md` | `bash scripts/install.sh project-discipline` |
|
||||
| `follow tdd-criteria` | `tdd-criteria` | skill | `~/.claude/skills/tdd-criteria/SKILL.md` | `bash scripts/install.sh tdd-criteria` |
|
||||
| `delegate to interns when allowed` | `using-interns` | skill | `~/.claude/skills/using-interns/SKILL.md` | `bash scripts/install.sh using-interns` |
|
||||
| `recommend, don't menu` | `recommend-dont-menu` | skill | `~/.claude/skills/recommend-dont-menu/SKILL.md` | `bash scripts/install.sh recommend-dont-menu` |
|
||||
| `use project wiki` | `mappa-knowledge` | skill | `~/.claude/skills/mappa-knowledge/SKILL.md` | `bash scripts/install.sh mappa-knowledge` |
|
||||
| `use task management system` | `mappa-task-work` | skill | `~/.claude/skills/mappa-task-work/SKILL.md` | `bash scripts/install.sh mappa-task-work` |
|
||||
| `inbox monitor: raise on start` | `mappa-session-orient` | skill | `~/.claude/skills/mappa-session-orient/SKILL.md` | `bash scripts/install.sh mappa-session-orient` |
|
||||
| `use project wiki` | `mappa-knowledge` | skill | см. mappa-bootstrap (репо mappa) | `cd <mappa-repo> && bash skills/mappa-bootstrap/assets/install.sh` |
|
||||
| `use task management system` | `mappa-task-work` | skill | см. mappa-bootstrap (репо mappa) | `cd <mappa-repo> && bash skills/mappa-bootstrap/assets/install.sh` |
|
||||
| `inbox monitor: raise on start` | `mappa-session-orient` | skill | см. mappa-bootstrap (репо mappa) | `cd <mappa-repo> && bash skills/mappa-bootstrap/assets/install.sh` |
|
||||
| `session sync: write to mappa` | `mappa-session-orient` | skill | см. mappa-bootstrap (репо mappa) | `cd <mappa-repo> && bash skills/mappa-bootstrap/assets/install.sh` |
|
||||
| `we're on Windows` / `we're on Linux` / `we're on macOS` | `active-platform` | skill | `~/.claude/skills/active-platform/SKILL.md` | `bash scripts/install.sh active-platform` |
|
||||
|
||||
### Algorithm
|
||||
@@ -635,32 +629,45 @@ python -c "import json; d=json.load(open('$HOME/.claude.json')); print('mappa' i
|
||||
mappa-конвенции в AGENTS.md (5.7.2) можно добавлять и без MCP-регистрации
|
||||
— триггеры будут ждать установки сервера (как любой absent-скилл).
|
||||
|
||||
### 5.7.2 — mappa-конвенции в AGENTS.md (idempotent merge)
|
||||
### 5.7.2 — mappa-конвенции в AGENTS.md (инъекция через mappa-bootstrap)
|
||||
|
||||
mappa-специфичные триггеры уже в каноне шаблона (Step 5) — `inbox monitor:
|
||||
raise on start`, `use project wiki`, `use task management system`. Это не
|
||||
отдельный merge: существующая идемпотентная машинерия Step 5 покрывает их.
|
||||
Модуль только **верифицирует**: после Step 5 убедиться, что строки на месте
|
||||
(та же substring-проверка что в Step 5 upgrade-merge). Если пользователь
|
||||
сознательно убрал их из AGENTS.md — не возвращать (уважать выбор).
|
||||
`project-bootstrap` — mappa-agnostic: mappa-триггеры **не хардкодятся** в
|
||||
шаблоне (см. `assets/AGENTS.md.template`). Инъекция канонического набора
|
||||
mappa-триггеров (`inbox monitor: raise on start`, `session sync: write to
|
||||
mappa`, `use project wiki`, `use task management system`, `check across all
|
||||
projects`) — через скил `mappa-bootstrap` (репо mappa, единый источник):
|
||||
|
||||
### 5.7.3 — методика-install (пакет из репо, версия в манифест)
|
||||
```bash
|
||||
bash <mappa>/skills-core/mappa-bootstrap/assets/install.sh --triggers <project-dir>
|
||||
```
|
||||
|
||||
Методика = reference-пакет (wiki:2672 решение 3/6): plain skills живут в
|
||||
репо skills (`scripts/install.sh <name>...`). project-bootstrap НЕ хранит
|
||||
тела — только указывает на пакет.
|
||||
После инъекции — **верифицировать** наличие строк (substring-проверка, та же
|
||||
машинерия что Step 5 upgrade-merge). Если пользователь сознательно убрал
|
||||
mappa-триггеры из AGENTS.md — не возвращать (уважать выбор). Источник истины
|
||||
набора — mappa-bootstrap, НЕ шаблон project-bootstrap.
|
||||
|
||||
### 5.7.3 — методика-install (пакет из репо mappa, версия в манифест)
|
||||
|
||||
Методика = reference-пакет (wiki:2672 решение 3/6): mappa-скилы живут в
|
||||
**репо mappa** (`mappa/skills/`, релокация task:1323, коммит 5301e85) — НЕ
|
||||
в репо skills. project-bootstrap НЕ хранит тела и НЕ дублирует пути
|
||||
(мёртвый маппинг на `scripts/install.sh mappa-*` убран, task:1339):
|
||||
установка/триггеры/deps-check mappa-скилов делегируются скилу
|
||||
`mappa-bootstrap` (репо mappa, спека wiki:3265).
|
||||
|
||||
1. Определить список mappa-циклов: `mappa-session-orient`,
|
||||
`mappa-task-work`, `mappa-knowledge`, `mappa-messaging`,
|
||||
`mappa-delegation`, `mappa-brainstorm-promote`, `mappa-closing-ritual`.
|
||||
`mappa-delegation`, `mappa-brainstorm-promote`, `mappa-closing-ritual`
|
||||
(плюс остальные из `mappa/skills/`).
|
||||
2. Проверить установку по правилу детекта Step 5.6 (оба пути:
|
||||
`~/.claude/skills/` и `~/.agents/skills/`)? → да: пропустить
|
||||
(upgrade-императив не дублировать).
|
||||
3. Нет → печать информационного блока (НЕ авто-инсталл, правило 5.6):
|
||||
|
||||
```
|
||||
ℹ️ Методика mappa не установлена. Установка:
|
||||
git clone <skills-repo> && scripts/install.sh mappa-session-orient mappa-task-work ...
|
||||
ℹ️ Методика mappa не установлена. Установка (скил mappa-bootstrap,
|
||||
репо mappa, НЕ skills-репо):
|
||||
cd <mappa-repo> && bash skills/mappa-bootstrap/assets/install.sh
|
||||
```
|
||||
|
||||
4. Версия методики фиксируется в bootstrap-manifest (5.7.4): читать
|
||||
@@ -669,23 +676,62 @@ raise on start`, `use project wiki`, `use task management system`. Это не
|
||||
### 5.7.4 — manifest/deps-check
|
||||
|
||||
Манифест (Step 5.5) дополняется строкой методики — версия = версия
|
||||
reference-пакета (репо skills, `version` из frontmatter скиллов). Добавить в
|
||||
таблицу манифеста:
|
||||
reference-пакета (репо mappa, `version` из frontmatter скиллов; пакетная
|
||||
версия — `mappa-bootstrap` из `mappa/skills/mappa-bootstrap/SKILL.md`).
|
||||
Добавить в таблицу манифеста:
|
||||
|
||||
| Skill | Version | Role |
|
||||
|---|---|---|
|
||||
| `mappa-bootstrap-project` (модуль) | <project-bootstrap version> | connect + конвенции + методика-install |
|
||||
| `mappa-bootstrap` (скил, репо mappa) | <mappa-bootstrap version> | установка/триггеры/deps-check mappa-скилов |
|
||||
|
||||
Deps-check (Step 5.6): mappa-триггеры (`inbox monitor: raise on start`,
|
||||
`use project wiki`, `use task management system`) маппятся на fulfiller'ы:
|
||||
`use project wiki`, `use task management system`) маппятся на fulfiller'ы
|
||||
через скил `mappa-bootstrap` — `bash <mappa>/skills/mappa-bootstrap/assets/install.sh --check`
|
||||
(источник истины — `mappa/skills/`, НЕ репо skills; мёртвые пути
|
||||
`~/.claude/skills/mappa-*/SKILL.md` из таблицы убраны, task:1339).
|
||||
Недостающие mappa-скиллы → в блок рекомендаций 5.6 (тем же форматом,
|
||||
install-команда — скил mappa-bootstrap, см. 5.7.3).
|
||||
|
||||
| Trigger line | Fulfiller | Kind | Detection |
|
||||
|---|---|---|---|
|
||||
| `use project wiki` | `mappa-knowledge` | skill | `~/.claude/skills/mappa-knowledge/SKILL.md` |
|
||||
| `use task management system` | `mappa-task-work` | skill | `~/.claude/skills/mappa-task-work/SKILL.md` |
|
||||
| `inbox monitor: raise on start` | `mappa-session-orient` | skill | `~/.claude/skills/mappa-session-orient/SKILL.md` |
|
||||
---
|
||||
|
||||
Недостающие mappa-скиллы → в блок рекомендаций 5.6 (тем же форматом).
|
||||
## Step 5.8 — `.mappa` маркер (контракт wiki:3340)
|
||||
|
||||
Машиночитаемый маркер проекта в корне папки (схема v1 — `.mappa/config.yaml`):
|
||||
гейт mappa-скилов («без маркера папка не участвует в mappa-операциях»,
|
||||
task:1546) + признак корня проекта для харнессов. Создаётся на bootstrap —
|
||||
без ручного прогона генератора (task:1583). Детерминированный рендер:
|
||||
фиксированный порядок полей, без секретов, без timestamp — повторный запуск
|
||||
no-op (`keep`).
|
||||
|
||||
1. **Собрать значения** (реестр mappa → локальное знание):
|
||||
- `project` — канон папки (`basename "$PWD"`); если проект уже в реестре
|
||||
(`projects_resolve`) — сверить, не расходится ли;
|
||||
- `tenant` — `MAPPA_TENANT` (по умолчанию `vitya`);
|
||||
- `url` — `MAPPA_CORE_URL` (без trailing slash);
|
||||
- `git_provider` — из реестра `projects.git_provider` (например `gitea`),
|
||||
иначе из шага 1.5 (создано через Gitea API → gitea); опционально;
|
||||
- `git` — `projects.qualified` (owner/repo) из реестра, иначе из remote
|
||||
шага 1.5; опционально (опустить, если неизвестно).
|
||||
2. **Записать маркер** (скрипт — ассет этого скила, реализует контракт
|
||||
wiki:3340; в репо: `skills/project-bootstrap/assets/dot_mappa_marker.py`):
|
||||
|
||||
```bash
|
||||
python assets/dot_mappa_marker.py write \
|
||||
--project "$(basename "$PWD")" --tenant vitya --url "$MAPPA_CORE_URL" \
|
||||
--git-provider gitea --git "$OWNER/$REPO"
|
||||
```
|
||||
|
||||
Без `--git-provider`/`--git`, если поля неизвестны. Повторный прогон —
|
||||
no-op (`keep`); отличающийся существующий маркер без `--force` НЕ
|
||||
перезаписывается — покажи diff и спроси (правило «never overwrite»).
|
||||
3. **Верифицировать**: `python assets/dot_mappa_marker.py check` → exit 0.
|
||||
4. **Контракт-тест** (TDD, task:1583): `python assets/test_dot_mappa_marker.py`
|
||||
— «после bootstrap есть `.mappa/config.yaml`», детерминизм, без секретов,
|
||||
порядок полей, идемпотентность.
|
||||
|
||||
Маркер публичен (без секретов) и попадает в коммит шага 6. Валидный
|
||||
существующий маркер не трогаем.
|
||||
|
||||
---
|
||||
|
||||
@@ -713,6 +759,7 @@ Print a final report:
|
||||
✅ Done! Created:
|
||||
.wiki/ — project wiki (Karpathy method)
|
||||
.tasks/ — task tracking system
|
||||
.mappa/ — mappa project marker (wiki:3340, schema v1)
|
||||
AGENTS.md — skill triggers (canon)
|
||||
CLAUDE.md — legacy pointer
|
||||
.gitignore — standard template
|
||||
@@ -734,10 +781,10 @@ Remote: <Gitea URL>
|
||||
|
||||
---
|
||||
|
||||
## Step 8 — projects-meta sync (greenfield-full mode)
|
||||
## Step 8 — mappa registry (greenfield-full mode)
|
||||
|
||||
Only in **greenfield-full** mode. Re-sync the projects-meta cache so the new
|
||||
project becomes visible to `mcp__projects-meta__*` tools.
|
||||
Only in **greenfield-full** mode. Register the new project in mappa
|
||||
(`mcp__mappa__projects_register`) so it becomes visible in the registry.
|
||||
|
||||
```bash
|
||||
# POSIX:
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
# AGENTS.md
|
||||
# Agent instructions. Each line is a trigger for an installed skill.
|
||||
#
|
||||
# Inter-session mail channel is Mappa (mcp__mappa__inbox_send/inbox_monitor),
|
||||
# NOT files. This line opts the project into inbox delivery at session start:
|
||||
inbox monitor: raise on start
|
||||
# mappa-специфичные триггеры (inbox monitor: raise on start, session sync:
|
||||
# write to mappa, use project wiki, use task management system, check across
|
||||
# all projects) НЕ хардкодятся здесь — project-bootstrap mappa-agnostic. Они
|
||||
# инжектятся при создании/апгрейде проекта через mappa-bootstrap:
|
||||
# bash <mappa>/skills-core/mappa-bootstrap/assets/install.sh --triggers <dir>
|
||||
# (см. Step 5.7.2 — mappa-конвенции в AGENTS.md, делегирование в mappa-bootstrap)
|
||||
|
||||
# Search rule
|
||||
|
||||
⛔ Never walk `node_modules/`, `dist/`, `build/`, `.nuxt/` (or any parent tree
|
||||
containing them) with non-gitignore-aware search tools — measured 3701s vs rg
|
||||
0s (2026-08-26). Use `rg` and NEVER `rg --no-ignore`/`-u` (those bypass the
|
||||
ignore protection and walk node_modules again). Also no `grep -r`/`-R`/
|
||||
`--recursive`, `ag`, `ack`, `find … -exec grep`. `grep --include` filters
|
||||
result file names, NOT directory traversal — it still walks every node_modules
|
||||
entry; `| grep -v node_modules` filters after the walk, doesn't save you.
|
||||
|
||||
talk like a caveman
|
||||
use project wiki
|
||||
use task management system
|
||||
check across all projects
|
||||
pull remote before work
|
||||
session handoff: read on start, write on end
|
||||
follow project discipline
|
||||
follow tdd-criteria
|
||||
delegate to interns when allowed
|
||||
recommend, don't menu
|
||||
|
||||
263
skills/project-bootstrap/assets/dot_mappa_marker.py
Normal file
263
skills/project-bootstrap/assets/dot_mappa_marker.py
Normal file
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python3
|
||||
"""dot_mappa_marker.py — deterministic render + write of the `.mappa` marker.
|
||||
|
||||
Contract: mappa wiki:3340 (concepts/dot-mappa-marker), schema v1.
|
||||
Used by project-create (step 5.5) and project-bootstrap (step 5.8) so a project
|
||||
folder gets its marker at create time — no manual generator run needed
|
||||
(task:1583). The batch generator (mappa `server/scripts/gen-dot-mappa-markers.ts`)
|
||||
remains for registry-wide migration; this is the per-project create path.
|
||||
|
||||
Guarantees (the contract):
|
||||
* `.mappa/config.yaml` — каталог + файл внутри
|
||||
* fixed field order (schema_version, protocol_version, project, tenant, url,
|
||||
git_provider?, git?)
|
||||
* deterministic render — no timestamps, same input → same bytes
|
||||
* NO secrets — only public registry fields; url with credentials is rejected
|
||||
* optional fields (`git_provider`, `git`) omitted when absent
|
||||
* idempotent write: same content → no-op (keep); different content → refuse
|
||||
without --force
|
||||
|
||||
Usage:
|
||||
python dot_mappa_marker.py render --project NAME --tenant TENANT --url URL \
|
||||
[--git-provider P] [--git OWNER/REPO] # print content to stdout
|
||||
python dot_mappa_marker.py write --project NAME --tenant TENANT --url URL \
|
||||
[--git-provider P] [--git OWNER/REPO] [--dir PATH] [--force] # write marker
|
||||
python dot_mappa_marker.py check --dir PATH # verify existing marker
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
SCHEMA_VERSION = 1
|
||||
PROTOCOL_VERSION = 1
|
||||
|
||||
# Canonical header comment — same as the contract example (wiki:3340).
|
||||
HEADER = "# mappa project marker — machine-readable identifier of a mappa project folder"
|
||||
|
||||
# YAML: these are indicator characters / reserved tokens — never plain.
|
||||
_INDICATOR_START = set("!&*{}[],#|>@`\"'%?:~-")
|
||||
_RESERVED_PLAIN = {"null", "Null", "NULL", "~", "true", "True", "TRUE", "false",
|
||||
"False", "FALSE", "yes", "Yes", "YES", "no", "No", "NO",
|
||||
"on", "On", "ON", "off", "Off", "OFF", "-", "?", ":"}
|
||||
|
||||
|
||||
class MarkerConflict(Exception):
|
||||
"""An existing marker differs from the requested one and --force is absent."""
|
||||
|
||||
|
||||
def yaml_scalar(value: str) -> str:
|
||||
"""YAML plain-safe scalar: plain without quotes when safe, else double-quoted.
|
||||
|
||||
Plain-safe = non-empty, no leading indicator, not a reserved token, no flow
|
||||
chars, no embedded newlines, no surrounding whitespace. Double-quoting uses
|
||||
JSON escaping, which is a valid subset of YAML double-quoted style.
|
||||
"""
|
||||
s = str(value)
|
||||
if s == "":
|
||||
return '""'
|
||||
if s.strip() != s:
|
||||
return json.dumps(s)
|
||||
if s[0] in _INDICATOR_START or s in _RESERVED_PLAIN:
|
||||
return json.dumps(s)
|
||||
if s.startswith(("- ", "? ", ": ")):
|
||||
return json.dumps(s)
|
||||
# plain scalars stay plain unless they would confuse the parser:
|
||||
# ": " (mapping indicator), trailing ":", " #" (comment), newlines/tabs
|
||||
if ": " in s or s.endswith(":") or " #" in s or "\n" in s or "\t" in s:
|
||||
return json.dumps(s)
|
||||
return s
|
||||
|
||||
|
||||
def validate_folder_name(name: str) -> str:
|
||||
"""A folder name (canon/tenant/git_provider) must be a single sane segment."""
|
||||
if not name or name in (".", ".."):
|
||||
raise ValueError(f"invalid name {name!r}: must be a non-empty folder name")
|
||||
if any(sep in name for sep in ("/", "\\", "\x00")):
|
||||
raise ValueError(f"invalid name {name!r}: must be a single path segment")
|
||||
if name != name.strip():
|
||||
raise ValueError(f"invalid name {name!r}: no surrounding whitespace allowed")
|
||||
return name
|
||||
|
||||
|
||||
def validate_git_ref(git: str) -> str:
|
||||
"""`git` = projects.qualified (owner/repo) — no credentials, no colon."""
|
||||
g = str(git)
|
||||
if not g or "/" not in g:
|
||||
raise ValueError(f"invalid git ref {g!r}: expected owner/repo")
|
||||
if any(c in g for c in ("@", ":", " ", "\t", "\n", "\\")):
|
||||
raise ValueError(f"invalid git ref {g!r}: no credentials / separators allowed")
|
||||
if g.startswith("/") or g.endswith("/") or ".." in g.split("/"):
|
||||
raise ValueError(f"invalid git ref {g!r}: must be owner/repo, not a path")
|
||||
return g
|
||||
|
||||
|
||||
def normalize_url(url: str) -> str:
|
||||
"""Absolute http(s) URL without credentials and without trailing slash."""
|
||||
u = str(url).strip()
|
||||
if not (u.startswith("http://") or u.startswith("https://")):
|
||||
raise ValueError(f"invalid url {u!r}: must be http(s)://host...")
|
||||
authority = u.split("://", 1)[1].split("/", 1)[0]
|
||||
if "@" in authority:
|
||||
raise ValueError("url must not contain credentials (no secrets in the marker)")
|
||||
return u.rstrip("/")
|
||||
|
||||
|
||||
def render(
|
||||
project: str,
|
||||
tenant: str,
|
||||
url: str,
|
||||
git_provider: str | None = None,
|
||||
git: str | None = None,
|
||||
) -> str:
|
||||
"""Deterministic `.mappa/config.yaml` content per wiki:3340 schema v1."""
|
||||
project = validate_folder_name(project)
|
||||
tenant = validate_folder_name(tenant)
|
||||
url = normalize_url(url)
|
||||
lines = [
|
||||
HEADER,
|
||||
f"schema_version: {SCHEMA_VERSION}",
|
||||
f"protocol_version: {PROTOCOL_VERSION}",
|
||||
f"project: {yaml_scalar(project)}",
|
||||
f"tenant: {yaml_scalar(tenant)}",
|
||||
f"url: {yaml_scalar(url)}",
|
||||
]
|
||||
if git_provider:
|
||||
lines.append(f"git_provider: {yaml_scalar(validate_folder_name(git_provider))}")
|
||||
if git:
|
||||
lines.append(f"git: {yaml_scalar(validate_git_ref(git))}")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def _sane_dir(directory: str | Path) -> Path:
|
||||
"""Resolve the target directory; reject `..` segments and non-dirs."""
|
||||
p = Path(directory)
|
||||
if ".." in p.parts:
|
||||
raise ValueError(f"invalid directory {str(directory)!r}: '..' segments not allowed")
|
||||
if p.exists() and not p.is_dir():
|
||||
raise ValueError(f"invalid directory {str(directory)!r}: not a directory")
|
||||
return p
|
||||
|
||||
|
||||
def write_marker(directory: str | Path, content: str, force: bool = False) -> tuple[Path, str]:
|
||||
"""Write `.mappa/config.yaml` under `directory`.
|
||||
|
||||
Returns (marker_path, outcome) where outcome is one of
|
||||
"created" | "keep" (idempotent no-op) | "overwrite" (force).
|
||||
Raises MarkerConflict when an existing marker differs and force is False.
|
||||
"""
|
||||
marker = _sane_dir(directory) / ".mappa" / "config.yaml"
|
||||
if marker.exists():
|
||||
existing = marker.read_text(encoding="utf-8")
|
||||
if existing == content:
|
||||
return marker, "keep"
|
||||
if not force:
|
||||
raise MarkerConflict(
|
||||
f"{marker} already exists with different content; "
|
||||
"pass --force to overwrite (contract: no silent overwrite)"
|
||||
)
|
||||
marker.write_text(content, encoding="utf-8")
|
||||
return marker, "overwrite"
|
||||
marker.parent.mkdir(parents=True, exist_ok=True)
|
||||
marker.write_text(content, encoding="utf-8")
|
||||
return marker, "created"
|
||||
|
||||
|
||||
def _parse_marker_lines(body: str) -> list[tuple[str, str]]:
|
||||
"""(key, value) pairs of data lines — comments skipped, first colon splits."""
|
||||
pairs = []
|
||||
for line in body.splitlines():
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
if ": " not in line:
|
||||
raise ValueError(f"malformed marker line (no 'key: value'): {line!r}")
|
||||
key, value = line.split(": ", 1)
|
||||
pairs.append((key, value.strip()))
|
||||
return pairs
|
||||
|
||||
|
||||
def check_marker(directory: str | Path) -> tuple[bool, str]:
|
||||
"""Gate check (wiki:3340 / task:1546): is `directory` a mappa project?
|
||||
|
||||
Returns (ok, message). ok means `.mappa/config.yaml` exists and its data
|
||||
lines start with exactly the required fields (schema_version,
|
||||
protocol_version, project, tenant, url) in canonical order with valid
|
||||
values; optional `git_provider`/`git` may follow.
|
||||
"""
|
||||
marker = _sane_dir(directory) / ".mappa" / "config.yaml"
|
||||
if not marker.is_file():
|
||||
return False, f"no marker: {marker} (folder without marker is not a mappa project)"
|
||||
try:
|
||||
pairs = _parse_marker_lines(marker.read_text(encoding="utf-8"))
|
||||
except ValueError as e:
|
||||
return False, f"marker {marker}: {e}"
|
||||
if len(pairs) < 5:
|
||||
return False, f"marker {marker}: fewer than the 5 required fields"
|
||||
required = ["schema_version", "protocol_version", "project", "tenant", "url"]
|
||||
if [k for k, _ in pairs[:5]] != required:
|
||||
return False, f"marker {marker}: field order mismatch ({[k for k, _ in pairs[:5]]})"
|
||||
values = dict(pairs)
|
||||
if values["schema_version"] != str(SCHEMA_VERSION):
|
||||
return False, f"marker {marker}: schema_version must be {SCHEMA_VERSION}"
|
||||
if values["protocol_version"] != str(PROTOCOL_VERSION):
|
||||
return False, f"marker {marker}: protocol_version must be {PROTOCOL_VERSION}"
|
||||
try:
|
||||
validate_folder_name(values["project"])
|
||||
validate_folder_name(values["tenant"])
|
||||
normalize_url(values["url"])
|
||||
if "git_provider" in values:
|
||||
validate_folder_name(values["git_provider"])
|
||||
if "git" in values:
|
||||
validate_git_ref(values["git"])
|
||||
except ValueError as e:
|
||||
return False, f"marker {marker}: {e}"
|
||||
return True, f"marker ok: {marker}"
|
||||
|
||||
|
||||
def _add_common(parser: argparse.ArgumentParser) -> None:
|
||||
parser.add_argument("--project", required=True, help="канон папки = реестр projects.name (slug)")
|
||||
parser.add_argument("--tenant", required=True, help="тенант, где живёт проект (MAPPA_TENANT)")
|
||||
parser.add_argument("--url", required=True, help="MAPPA_CORE_URL (без trailing slash)")
|
||||
parser.add_argument("--git-provider", default=None, help="projects.git_provider (gitea/...) — опционально")
|
||||
parser.add_argument("--git", default=None, help="projects.qualified (owner/repo) — опционально")
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description=".mappa marker per wiki:3340 (schema v1)")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
|
||||
p_render = sub.add_parser("render", help="print deterministic marker content")
|
||||
_add_common(p_render)
|
||||
|
||||
p_write = sub.add_parser("write", help="write .mappa/config.yaml into a folder")
|
||||
_add_common(p_write)
|
||||
p_write.add_argument("--dir", default=".", help="project folder (default: cwd)")
|
||||
p_write.add_argument("--force", action="store_true", help="overwrite a differing marker")
|
||||
|
||||
p_check = sub.add_parser("check", help="gate check: is the folder a mappa project?")
|
||||
p_check.add_argument("--dir", default=".", help="project folder (default: cwd)")
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.cmd in ("render", "write"):
|
||||
content = render(args.project, args.tenant, args.url, args.git_provider, args.git)
|
||||
if args.cmd == "render":
|
||||
sys.stdout.write(content)
|
||||
return 0
|
||||
marker, outcome = write_marker(args.dir, content, force=args.force)
|
||||
print(f"{outcome}: {marker}")
|
||||
return 0
|
||||
|
||||
if args.cmd == "check":
|
||||
ok, msg = check_marker(args.dir)
|
||||
print(msg)
|
||||
return 0 if ok else 1
|
||||
|
||||
return 2 # unreachable
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
290
skills/project-bootstrap/assets/test_dot_mappa_marker.py
Normal file
290
skills/project-bootstrap/assets/test_dot_mappa_marker.py
Normal file
@@ -0,0 +1,290 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Contract test for the `.mappa` marker — mappa wiki:3340 (concepts/dot-mappa-marker).
|
||||
|
||||
The contract under test (task:1583): after the project-create/bootstrap marker
|
||||
step, the project folder contains `.mappa/config.yaml` matching schema v1:
|
||||
fixed field order, deterministic render (no timestamps), NO secrets, optional
|
||||
fields (`git_provider`, `git`) omitted when absent, idempotent write.
|
||||
|
||||
Run: python test_dot_mappa_marker.py (or: python -m unittest test_dot_mappa_marker)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
import dot_mappa_marker as dmm # noqa: E402
|
||||
|
||||
CANON = "skills" # registry projects.name — канон папки (slug)
|
||||
TENANT = "vitya"
|
||||
URL = "https://mappa.vds.kzntsv.site"
|
||||
GIT_PROVIDER = "gitea"
|
||||
GIT = "OpeItcLoc03/skills"
|
||||
|
||||
FIELD_ORDER = [
|
||||
"schema_version",
|
||||
"protocol_version",
|
||||
"project",
|
||||
"tenant",
|
||||
"url",
|
||||
"git_provider",
|
||||
"git",
|
||||
]
|
||||
|
||||
|
||||
def field_keys(body: str) -> list[str]:
|
||||
return [
|
||||
line.split(":", 1)[0]
|
||||
for line in body.splitlines()
|
||||
if line and not line.startswith("#") and ": " in line
|
||||
]
|
||||
|
||||
|
||||
def write_contract_marker(tmp: str) -> Path:
|
||||
"""Helper: create a valid marker as the bootstrap step would."""
|
||||
marker, outcome = dmm.write_marker(tmp, dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT))
|
||||
assert outcome == "created"
|
||||
return marker
|
||||
|
||||
|
||||
class ContractTests(unittest.TestCase):
|
||||
"""Contract: after bootstrap there is `.mappa/config.yaml` (task:1583)."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.tmp = tempfile.mkdtemp(prefix="mappa-marker-test-")
|
||||
|
||||
def tearDown(self) -> None:
|
||||
shutil.rmtree(self.tmp, ignore_errors=True)
|
||||
|
||||
# --- presence / shape -------------------------------------------------
|
||||
|
||||
def test_bootstrap_marker_step_creates_config_yaml(self) -> None:
|
||||
"""The bootstrap marker step leaves `.mappa/config.yaml` in the folder."""
|
||||
content = dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT)
|
||||
marker, outcome = dmm.write_marker(self.tmp, content)
|
||||
self.assertEqual(outcome, "created")
|
||||
self.assertTrue(marker.is_file())
|
||||
self.assertEqual(marker.name, "config.yaml")
|
||||
self.assertEqual(marker.parent.name, ".mappa")
|
||||
|
||||
def test_fixed_field_order(self) -> None:
|
||||
body = dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT)
|
||||
self.assertEqual(field_keys(body), FIELD_ORDER)
|
||||
|
||||
def test_deterministic_render_no_timestamp(self) -> None:
|
||||
a = dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT)
|
||||
b = dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT)
|
||||
self.assertEqual(a, b)
|
||||
# no ISO-date-like content
|
||||
self.assertNotRegex(a, r"\d{4}-\d{2}-\d{2}")
|
||||
|
||||
def test_optional_fields_omitted_when_absent(self) -> None:
|
||||
body = dmm.render(CANON, TENANT, URL)
|
||||
self.assertEqual(field_keys(body), FIELD_ORDER[:5])
|
||||
self.assertNotIn("git_provider", body)
|
||||
self.assertNotIn("\ngit:", body)
|
||||
|
||||
def test_no_secrets_in_marker(self) -> None:
|
||||
body = dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT)
|
||||
lowered = body.lower()
|
||||
# credentials in the url authority are rejected separately
|
||||
for secret in ("token", "password", "secret", "api_key", "key:", "@"):
|
||||
self.assertNotIn(secret, lowered)
|
||||
|
||||
# --- idempotent write --------------------------------------------------
|
||||
|
||||
def test_idempotent_write_keeps_same_content(self) -> None:
|
||||
content = dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT)
|
||||
marker, first = dmm.write_marker(self.tmp, content)
|
||||
marker, second = dmm.write_marker(self.tmp, content)
|
||||
self.assertEqual(first, "created")
|
||||
self.assertEqual(second, "keep")
|
||||
self.assertEqual(marker.read_text(encoding="utf-8"), content)
|
||||
|
||||
def test_refuses_overwrite_of_different_marker_without_force(self) -> None:
|
||||
dmm.write_marker(self.tmp, dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT))
|
||||
with self.assertRaises(dmm.MarkerConflict):
|
||||
dmm.write_marker(self.tmp, dmm.render(CANON, TENANT, URL, "github", GIT))
|
||||
|
||||
def test_force_overwrites_different_marker(self) -> None:
|
||||
dmm.write_marker(self.tmp, dmm.render(CANON, TENANT, URL, GIT_PROVIDER, GIT))
|
||||
marker, outcome = dmm.write_marker(
|
||||
self.tmp, dmm.render(CANON, TENANT, URL, "github", GIT), force=True
|
||||
)
|
||||
self.assertEqual(outcome, "overwrite")
|
||||
self.assertIn("git_provider: github", marker.read_text(encoding="utf-8"))
|
||||
|
||||
# --- input validation ---------------------------------------------------
|
||||
|
||||
def test_folder_name_path_segments_rejected(self) -> None:
|
||||
for bad in ("../evil", "a/b", "a\\b", ".", "..", ""):
|
||||
with self.assertRaises(ValueError, msg=f"name {bad!r} must be rejected"):
|
||||
dmm.render(bad, TENANT, URL)
|
||||
|
||||
def test_url_trailing_slash_stripped_but_path_kept(self) -> None:
|
||||
body = dmm.render(CANON, TENANT, URL + "//")
|
||||
self.assertIn(f"url: {URL}", body)
|
||||
# a trailing slash after a path must be stripped, the path kept
|
||||
body2 = dmm.render(CANON, TENANT, "https://example.com/mappa/")
|
||||
self.assertIn("url: https://example.com/mappa", body2)
|
||||
|
||||
def test_url_with_credentials_rejected(self) -> None:
|
||||
with self.assertRaises(ValueError):
|
||||
dmm.render(CANON, TENANT, "https://user:pass@mappa.vds.kzntsv.site")
|
||||
|
||||
def test_url_scheme_restricted_to_http_https(self) -> None:
|
||||
for bad in ("ftp://mappa.example", "javascript://x", "mappa.vds.kzntsv.site", "://x"):
|
||||
with self.assertRaises(ValueError, msg=f"url {bad!r} must be rejected"):
|
||||
dmm.render(CANON, TENANT, bad)
|
||||
|
||||
def test_git_ref_with_credentials_rejected(self) -> None:
|
||||
for bad in ("user:pass@host/repo", "victor/repo@token", "../config", "/owner/repo", "owner/repo/", "owner repo", "norepo"):
|
||||
with self.assertRaises(ValueError, msg=f"git {bad!r} must be rejected"):
|
||||
dmm.render(CANON, TENANT, URL, GIT_PROVIDER, bad)
|
||||
|
||||
def test_directory_with_parent_segments_rejected(self) -> None:
|
||||
with self.assertRaises(ValueError):
|
||||
dmm.write_marker("some/../elsewhere", dmm.render(CANON, TENANT, URL))
|
||||
with self.assertRaises(ValueError):
|
||||
dmm.check_marker("../etc")
|
||||
|
||||
# --- YAML scalar edge cases -------------------------------------------
|
||||
|
||||
def test_yaml_scalar_quoting_edge_cases(self) -> None:
|
||||
# reserved tokens / indicators must be double-quoted (never plain)
|
||||
for special in ("~", "@host", "-", "?", ":", "null", "yes", "on", "true",
|
||||
"a: b", "ends:", " #lead", "has tab\tinside"):
|
||||
self.assertTrue(dmm.yaml_scalar(special).startswith('"'),
|
||||
f"{special!r} must be double-quoted, got {dmm.yaml_scalar(special)!r}")
|
||||
# plain-safe values stay plain
|
||||
for plain in ("vitya", "OpeItcLoc03/skills", "https://mappa.vds.kzntsv.site",
|
||||
"a:b", "x#y", "lead#ing", "my-proj"):
|
||||
self.assertEqual(dmm.yaml_scalar(plain), plain)
|
||||
self.assertEqual(dmm.yaml_scalar(""), '""')
|
||||
|
||||
# --- check_marker (gate) ----------------------------------------------
|
||||
|
||||
def test_check_ok_on_valid_marker(self) -> None:
|
||||
write_contract_marker(self.tmp)
|
||||
ok, msg = dmm.check_marker(self.tmp)
|
||||
self.assertTrue(ok, msg)
|
||||
|
||||
def test_check_fails_on_missing_marker(self) -> None:
|
||||
ok, _ = dmm.check_marker(self.tmp)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_check_fails_on_wrong_field_order(self) -> None:
|
||||
(Path(self.tmp) / ".mappa").mkdir()
|
||||
(Path(self.tmp) / ".mappa" / "config.yaml").write_text(
|
||||
"# c\nproject: skills\nschema_version: 1\nprotocol_version: 1\n"
|
||||
"tenant: vitya\nurl: https://mappa.vds.kzntsv.site\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
ok, _ = dmm.check_marker(self.tmp)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_check_fails_on_extra_field_before_required(self) -> None:
|
||||
(Path(self.tmp) / ".mappa").mkdir()
|
||||
(Path(self.tmp) / ".mappa" / "config.yaml").write_text(
|
||||
"extra: sneaky\nschema_version: 1\nprotocol_version: 1\n"
|
||||
"project: skills\ntenant: vitya\nurl: https://mappa.vds.kzntsv.site\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
ok, _ = dmm.check_marker(self.tmp)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_check_fails_on_wrong_versions(self) -> None:
|
||||
(Path(self.tmp) / ".mappa").mkdir()
|
||||
(Path(self.tmp) / ".mappa" / "config.yaml").write_text(
|
||||
"schema_version: 2\nprotocol_version: 1\nproject: skills\n"
|
||||
"tenant: vitya\nurl: https://mappa.vds.kzntsv.site\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
ok, _ = dmm.check_marker(self.tmp)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_check_fails_on_malicious_project_value(self) -> None:
|
||||
(Path(self.tmp) / ".mappa").mkdir()
|
||||
(Path(self.tmp) / ".mappa" / "config.yaml").write_text(
|
||||
"schema_version: 1\nprotocol_version: 1\nproject: ../../evil\n"
|
||||
"tenant: vitya\nurl: https://mappa.vds.kzntsv.site\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
ok, _ = dmm.check_marker(self.tmp)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_check_fails_on_malformed_line(self) -> None:
|
||||
(Path(self.tmp) / ".mappa").mkdir()
|
||||
(Path(self.tmp) / ".mappa" / "config.yaml").write_text(
|
||||
"schema_version: 1\nprotocol_version: 1\nproject skills\n"
|
||||
"tenant: vitya\nurl: https://mappa.vds.kzntsv.site\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
ok, _ = dmm.check_marker(self.tmp)
|
||||
self.assertFalse(ok)
|
||||
|
||||
def test_check_accepts_url_with_port(self) -> None:
|
||||
(Path(self.tmp) / ".mappa").mkdir()
|
||||
(Path(self.tmp) / ".mappa" / "config.yaml").write_text(
|
||||
"schema_version: 1\nprotocol_version: 1\nproject: skills\n"
|
||||
"tenant: vitya\nurl: https://mappa.example:8443\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
ok, _ = dmm.check_marker(self.tmp)
|
||||
self.assertTrue(ok)
|
||||
|
||||
# --- CLI end-to-end ----------------------------------------------------
|
||||
|
||||
def test_cli_write_creates_marker(self) -> None:
|
||||
"""End-to-end: the documented CLI command produces the marker."""
|
||||
proc = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
str(Path(__file__).resolve().parent / "dot_mappa_marker.py"),
|
||||
"write",
|
||||
"--project", CANON,
|
||||
"--tenant", TENANT,
|
||||
"--url", URL,
|
||||
"--git-provider", GIT_PROVIDER,
|
||||
"--git", GIT,
|
||||
"--dir", self.tmp,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(proc.returncode, 0, proc.stderr)
|
||||
marker = Path(self.tmp) / ".mappa" / "config.yaml"
|
||||
self.assertTrue(marker.is_file())
|
||||
self.assertEqual(field_keys(marker.read_text(encoding="utf-8")), FIELD_ORDER)
|
||||
|
||||
def test_cli_check_verifies_marker(self) -> None:
|
||||
write_contract_marker(self.tmp)
|
||||
script = Path(__file__).resolve().parent / "dot_mappa_marker.py"
|
||||
ok = subprocess.run(
|
||||
[sys.executable, str(script), "check", "--dir", self.tmp],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(ok.returncode, 0, ok.stderr)
|
||||
# check on an empty dir fails (gate semantics: no marker → not a mappa project)
|
||||
empty = tempfile.mkdtemp(prefix="mappa-marker-empty-")
|
||||
try:
|
||||
missing = subprocess.run(
|
||||
[sys.executable, str(script), "check", "--dir", empty],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertNotEqual(missing.returncode, 0)
|
||||
finally:
|
||||
shutil.rmtree(empty, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
@@ -1,29 +0,0 @@
|
||||
# project-discipline
|
||||
|
||||
Policy skill that codifies four cross-project discipline rules so the same
|
||||
guarantees that hold in a tightly-maintained repo apply everywhere.
|
||||
|
||||
## When it triggers
|
||||
|
||||
- **Session start** — when `AGENTS.md` contains the line `follow project discipline` (added by `project-bootstrap` v2.0.0+).
|
||||
- **In-chat** — when the user says "use project discipline", "соблюди дисциплину", "проектные правила", or close variants.
|
||||
|
||||
## The four rules
|
||||
|
||||
1. **Project conventions over skill defaults.** `AGENTS.md` / `.wiki/CLAUDE.md` / `.tasks/` override any other skill's defaults. Specs go to `.wiki/concepts/`, tasks to `.tasks/`.
|
||||
2. **Master-only.** All work on `master` (or `main`). No feature branches without explicit user approval.
|
||||
3. **Semver discipline.** Bump `version:` in `SKILL.md` / `package.json` / `pyproject.toml` on every edit per MAJOR / MINOR / PATCH; record in commit message; rebuild `dist/` artifacts after.
|
||||
4. **Push freely by default.** No confirmation needed for push; a local push-gate skill (e.g. books — auto-deploy) overrides per project. Force / delete / non-ff push always asks.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
None. The skill is a textual policy document; it takes no actions and has no
|
||||
external dependencies. Activate it by adding `follow project discipline` to
|
||||
`AGENTS.md` (or use `project-bootstrap` v2.0.0+ which adds it automatically).
|
||||
|
||||
## Related
|
||||
|
||||
- `project-bootstrap` (v2.0.0+) — adds the trigger line to new and existing projects' `AGENTS.md`.
|
||||
- `pulling-before-work` — companion skill activated by the canonical template; pulls origin once at session start (`git pull --ff-only`).
|
||||
- `using-tasks` / `using-wiki` — the format conventions Rule 1 routes work into.
|
||||
- `.wiki/concepts/project-discipline-design.md` (in `skills`) — full design rationale.
|
||||
@@ -1,127 +0,0 @@
|
||||
---
|
||||
name: project-discipline
|
||||
author: ours
|
||||
version: 0.2.2
|
||||
description: >
|
||||
Codifies five cross-project discipline rules: (1) project AGENTS.md /
|
||||
.wiki/CLAUDE.md / .tasks/ override defaults from other skills (specs →
|
||||
.wiki/concepts/, tasks → .tasks/); (2) master-only, no feature branches
|
||||
without approval; (3) semver bump on every edit of versioned artifacts
|
||||
(SKILL.md, package.json, pyproject.toml), recorded in commit;
|
||||
(4) push freely by default — no confirmation needed;
|
||||
a local push-gate skill (e.g. books — auto-deploy)
|
||||
overrides per project; force/delete/non-ff always ask;
|
||||
(5) transit/brainstorm workspaces — artifacts to .brainstorm/ or global wiki
|
||||
only via explicit user direction. Activated by "follow project discipline"
|
||||
trigger in AGENTS.md (added by project-bootstrap v2.0.0+).
|
||||
---
|
||||
|
||||
# project-discipline
|
||||
|
||||
> Four cross-project rules. Read at session start. Apply before any other skill's defaults touch paths, branches, versions, or remote pushes.
|
||||
|
||||
## When this runs
|
||||
|
||||
**At session start** — when `AGENTS.md` contains the line `follow project discipline`. The skill is a policy document; the agent reads it and applies the four rules to all subsequent work in the session.
|
||||
|
||||
**On explicit reference** — when the user says "use project discipline", "соблюди дисциплину", "проектные правила", "что у меня по правилам?", or close variants asking about/applying the rules.
|
||||
|
||||
The skill itself takes no actions and has no external side-effects. It instructs the agent how to behave.
|
||||
|
||||
## Rule 1 — Project conventions override skill defaults
|
||||
|
||||
Before applying defaults from any other skill (frontend-design, mcp-builder, etc.), read in this order:
|
||||
|
||||
1. `AGENTS.md` in the project root.
|
||||
2. `.wiki/CLAUDE.md` (if it exists).
|
||||
3. `.tasks/STATUS.md` (if it exists).
|
||||
|
||||
Any path, format, or workflow explicitly stated in those files **overrides the skill default**. (CLAUDE.md, where present, is a legacy pointer — read it, then follow the canon in AGENTS.md.)
|
||||
|
||||
Concrete consequences:
|
||||
|
||||
- **Specs / design documents** go to `.wiki/concepts/<topic>-design.md`.
|
||||
- **Task tracking / implementation plans** go to `.tasks/<slug>.md` plus a board entry in `.tasks/STATUS.md` (the `using-tasks` format).
|
||||
- **Frontmatter, naming conventions, log format** — as described in the project's `.wiki/CLAUDE.md`.
|
||||
|
||||
If no convention is stated explicitly — fall back to the skill default.
|
||||
|
||||
## Rule 2 — Master-only
|
||||
|
||||
All work happens on the repo's main integration branch — usually `master`, but if a project uses `main`, treat `main` as equivalent.
|
||||
|
||||
- No `git checkout -b feature/foo` for solo work.
|
||||
- Sync with remote: `git pull --ff-only` or `git pull --rebase`. **No merge commits** for solo work.
|
||||
- If a task genuinely requires isolation (large experiment, risky refactor with rollback potential, multi-day work with intermediate WIP commits) — **ask** the user: "this needs its own branch, ok?" — and wait for explicit approval. Without approval, work continues on master.
|
||||
|
||||
If the agent finds itself on a non-main branch (after a manual `git checkout`) or in detached HEAD — report it and ask whether to return to master before working.
|
||||
|
||||
## Rule 3 — Versioning discipline
|
||||
|
||||
When editing any artifact with a semver field, **bump the version before committing** per:
|
||||
|
||||
- **MAJOR** (`X+1.0.0`) — breaks the contract. Renames, removed triggers, layout changes, removed public functions, breaking API change.
|
||||
- **MINOR** (`X.Y+1.0`) — adds capability without breaking. New trigger, new optional step, new public function.
|
||||
- **PATCH** (`X.Y.Z+1`) — wording / clarity / typo fixes with no behavior change.
|
||||
|
||||
The bump is recorded in the commit message: `feat(<artifact>): … [vX.Y.Z]` or whatever convention the project uses (see Rule 1).
|
||||
|
||||
**Applies to:** `skills/<name>/SKILL.md` (`version:` in frontmatter), `package.json` (`"version":`), `pyproject.toml` (`version =`), `Cargo.toml` (`version =`), and any other semver field in any other manifest.
|
||||
|
||||
**If the artifact is packaged** as `dist/<name>.skill`, `dist/*.tgz`, etc. — **rebuild** the package in the same or the next commit. Forgotten dist artifacts are a common cause of deploying stale binaries.
|
||||
|
||||
**First edit of an unversioned artifact** that COULD have a semver field (a new skill without `version:`, a new `package.json` without `"version":`) — **add** `version: 0.1.0` (or its equivalent) before committing; do not bump anything.
|
||||
|
||||
**Does not apply to:** artifacts with no semver field and no potential for one (wiki concept pages, README.md, shell scripts without a public interface).
|
||||
|
||||
## Rule 4 — Push freely, gate only where a local gate exists
|
||||
|
||||
**Default: push freely.** An ordinary fast-forward `git push` to the configured
|
||||
upstream needs no per-push confirmation. No ask-before-push mode by default.
|
||||
|
||||
**Per-project push gate.** A project whose push triggers side effects carries a
|
||||
LOCAL project-scope skill (convention: `push-gate`) that replaces this default
|
||||
with ask-before-push for that project. Example: `books` — push to master runs
|
||||
Gitea Actions auto-deploy. Respect the local gate over this rule: if the project
|
||||
has a `push-gate` skill, its semantics win for that project; this rule's
|
||||
free-push default does not apply there.
|
||||
|
||||
**Always ask:**
|
||||
|
||||
- `git push --force` / `--force-with-lease` (history rewrite);
|
||||
- `git push origin --delete <branch>` (branch deletion);
|
||||
- push to a remote/branch other than the current tracked upstream (`git push other-remote ...`, `git push origin other-branch`);
|
||||
- push to the main branch that would require non-fast-forward (i.e. would need force).
|
||||
|
||||
Anything else is an ordinary fast-forward push and proceeds without a gate.
|
||||
|
||||
**What counts as "push":** only `git push` family commands. Local commits, `git stash push`, etc. are not push.
|
||||
|
||||
## Rule 5 — Transit-zone / brainstorm workspaces
|
||||
|
||||
Some workspaces are **transit zones** — discussion areas with no `.tasks/`, where brainstorm artifacts are explicitly NOT auto-promoted to project wikis.
|
||||
|
||||
**Default destination for brainstorm artifacts:**
|
||||
- **In-progress brainstorm outputs** → `.brainstorm/<topic>.md` (or whatever the workspace's README/AGENTS.md declares)
|
||||
- **Mature, cross-cutting outputs** → `~/projects/.wiki/concepts/<topic>-design.md` via `mcp__projects-meta__knowledge_ingest` — **only** when user explicitly directs this
|
||||
|
||||
**Agent must NOT auto-promote** brainstorm artifacts to global wikis by analogy with Rule 1. Convergence-moment (move from workspace to permanent wiki) is a user decision, not an automatic action.
|
||||
|
||||
**Example:** `~/projects/.meeting-room/` is a transit zone. Its AGENTS.md explicitly states "no `.tasks/`, transit zone, artifacts go to `.brainstorm/` or global wiki via user command." Rule 1's "project conventions override" applies, but the override is explicit in the workspace contract — auto-promotion by analogy would violate that contract.
|
||||
|
||||
**When in doubt:** ask the user "this goes to `.brainstorm/`, or should I promote to shared wiki?" rather than assuming.
|
||||
|
||||
## Out of scope
|
||||
|
||||
The skill **does not**:
|
||||
|
||||
- modify `AGENTS.md` (that's `project-bootstrap`'s job);
|
||||
- enforce rules via git hooks / pre-commit / CI (this is agent discipline, not tooling);
|
||||
- manage `settings.json` permissions (that's `update-config`);
|
||||
- check the existence of `.wiki/` / `.tasks/` on disk (мета в mappa, решение 14/15: вики/таски — сущности сервиса через `using-wiki` / `using-tasks`; файловый layout — легаси); if a project has neither, Rule 1 simply finds no overrides and falls back to skill defaults.
|
||||
|
||||
## Why this exists
|
||||
|
||||
In a tightly-disciplined repo (`skills`) the four rules already hold by accident — the agent reads `.wiki/CLAUDE.md`, knows specs go to `.wiki/concepts/`, knows to bump `version:`, knows not to push without confirmation. In **other** projects of the same user, that discipline does not transfer: the agent falls back to vendor-default paths (`docs/specs/`, `docs/plans/`), branches on a whim, forgets `version:` bumps, and pushes without asking. This skill makes the discipline explicit and portable.
|
||||
|
||||
Full design rationale (why one skill instead of four, why a skill instead of inline `AGENTS.md` lines, scope of each rule, push-permission mechanism choice) lives in `.wiki/concepts/project-discipline-design.md` (in this repo; in other projects bootstrapped from this repo, the design lives in `skills`).
|
||||
@@ -1,117 +0,0 @@
|
||||
---
|
||||
name: report-mappa-issue
|
||||
author: ours
|
||||
version: 0.2.0
|
||||
description: >
|
||||
Use when working with mappa (MCP tools `mcp__mappa__*`, HTTP routes, mappa
|
||||
skills) and anything deviates from the expected workflow: 500/5xx, "entity
|
||||
not found" for an id that must exist, unexpected response shape, timeouts,
|
||||
silent failures, wrong status, instability. Report it by mail to `mappa` AND
|
||||
`.workshop` — never swallow, never only-local-log, never only in-chat.
|
||||
TEMPORARY skill: active while mappa is unstable; retire when stabilized.
|
||||
Triggers (bilingual): «маппа отдала 500», «entity not found», «неожиданный
|
||||
ответ от mappa», «mappa вернула», "mappa returned 500", "entity not found",
|
||||
"unexpected mappa response".
|
||||
---
|
||||
|
||||
# report-mappa-issue
|
||||
|
||||
Any deviation from the expected mappa workflow is reported **by mail to `mappa`
|
||||
and `.workshop`** — immediately, with evidence. Never swallow, never hide it in
|
||||
a local log, never postpone "until a digest".
|
||||
|
||||
> ⚠️ **TEMPORARY skill:** active while mappa is unstable. It is a stopgap for
|
||||
> collecting signals toward stabilization. When mappa stabilizes (a week
|
||||
> without reports) — this skill is retired: reports become ordinary bug tasks.
|
||||
> The owner of the retirement decision is workshop.
|
||||
|
||||
## When to use
|
||||
|
||||
Report when, during work with mappa, **any** of the following happens:
|
||||
|
||||
- **5xx / 500 / 502** on any call (`task_*`, `wiki_*`, `inbox_*`, `entity_*`,
|
||||
`admin_*`, `graph_*`, HTTP routes).
|
||||
- **"Entity not found" / 404** for an id/ref that **must** exist (you know you
|
||||
created it; you see it in a fresh response; another letter/task references it).
|
||||
- **Unexpected response shape** — fields don't match the documented ones,
|
||||
empty `rows` where data was expected, a new/unexpected type in the response.
|
||||
- **Timeouts / hangs** on a call.
|
||||
- **Silent failure** — the call "succeeded" but had no effect (task not
|
||||
created, letter not delivered, status unchanged).
|
||||
- **Retry worked** — even if the repeated call succeeded: the instability
|
||||
itself is a signal for stabilization (mark `retry: resolved`).
|
||||
- **Wrong/unexpected entity status**, board vs reality desync.
|
||||
|
||||
**Retries are allowed** (1–2 with a pause), but the report happens regardless
|
||||
of the retry outcome: case 500 → report; case 500 → retry → ok → report with
|
||||
`retry: resolved`.
|
||||
|
||||
## When NOT to use
|
||||
|
||||
- **Expected 404** — the entity genuinely does not exist and should not
|
||||
(never created; deleted by design). Before reporting, check that the entity
|
||||
was required to exist.
|
||||
- **Documented known limitations** (e.g. "verify on prod is impossible by
|
||||
design", "prod is stale until redeploy" — if documented and known to the
|
||||
mappa team).
|
||||
- **Deviations NOT from mappa** — VDS/docker (→ using-vds-ops), projects-meta
|
||||
cache (documented staleness), model providers. Only mappa.
|
||||
- **The same incident already reported** — don't duplicate (see Dedup).
|
||||
|
||||
## Core pattern — the report
|
||||
|
||||
Each call: `mcp__mappa__inbox_send` to **both** addresses (`mappa` and
|
||||
`.workshop`, addresses from the address book
|
||||
`~/projects/.wiki/concepts/projects-address-book.md`), `from` = your own
|
||||
folder name. Letter format:
|
||||
|
||||
```
|
||||
Subject: [mappa-issue] <symptom> @ <tool/endpoint> (<date>)
|
||||
|
||||
Body:
|
||||
- Expected: <what should have happened per workflow/docs>
|
||||
- Actual: <error/status/response — message text or a short snippet>
|
||||
- Call: <tool + key parameters / endpoint + project>
|
||||
- Retry: <did the retry work, how many attempts>
|
||||
- Recurrence: <first time / repeats — how many times this session>
|
||||
- Context: <project, session, which flow was running>
|
||||
```
|
||||
|
||||
One letter = **one incident** (symptom × endpoint). Recurrence goes in the same
|
||||
letter (`recurrence: 5 times in 2 hours`), not a new report per call.
|
||||
|
||||
## Common mistakes / rationalizations
|
||||
|
||||
| Rationalization | Reality |
|
||||
|---|---|
|
||||
| "Mappa is down — the letter won't arrive, why write" | A letter is an entity in Mappa (carve-out, no lease). When the service revives, it will be in the recipient's inbox. Always write. |
|
||||
| "I'll tell the human in chat" | The human is not always in session; the mappa team doesn't see chat. A letter is durable and cross-session. |
|
||||
| "I'll write it in the local log" | The local log is invisible to the mappa team. The goal of the report is visibility for recipients. (Local recording is extra, not instead.) |
|
||||
| "The retry worked — so it's fine" | The instability itself is a signal. Report with `retry: resolved`. |
|
||||
| "It's a small thing, I won't spam" | While mappa is unstable — any signal is material for stabilization. Dedup protects against spam, silence does not. |
|
||||
| "I'll collect several and report at once" | First occurrence — immediately. Recurrence gets appended to the same letter. |
|
||||
| "Mappa surely already knows this" | Unknown until reported. The report is exactly how it becomes known. |
|
||||
|
||||
## Red flags — STOP
|
||||
|
||||
- Caught a mappa error and silently continued (no report).
|
||||
- Recorded only locally / said only in chat — no letter.
|
||||
- Skipped "entity not found" without checking whether the id must exist.
|
||||
- Postponed the report "for later" without a letter and without a task.
|
||||
- Reported but not to both addresses (`mappa` and `.workshop`).
|
||||
|
||||
## Cross-agent
|
||||
|
||||
Channel — mappa inbox (`inbox_send` / `inbox.monitor`), shared by all agents
|
||||
(pi: `mcp__mappa__inbox_send`; Claude Code: the same MCP tools; headless — the
|
||||
same). Addressing strictly from the address book (`inter-session-messaging`
|
||||
canon).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- **Does not fix mappa** — service diagnosis/repair is separate; this skill
|
||||
only reports. (Deep diagnosis — `diagnosing-bugs` / `using-vds-ops` for
|
||||
infra.)
|
||||
- **Does not report other services** — only deviations from the mappa workflow.
|
||||
- **Does not replace** `inter-session-messaging` (the send mechanics live
|
||||
there; this skill defines the policy "what counts as an incident").
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: review-kit-pi-method
|
||||
author: ours
|
||||
version: 0.1.1
|
||||
version: 0.1.2
|
||||
description: >
|
||||
Spawn clean-context non-implementer subagents for review, trigger-testing,
|
||||
and spec validation under pi — the pi-native port of the review-kit method.
|
||||
@@ -124,6 +124,26 @@ non-implementer subagents, anti-priming checklist, negative controls — is
|
||||
agent-agnostic and transfers to any runtime that can spawn a fresh-context
|
||||
subprocess (claude `-p`, codex exec, hermes headless).
|
||||
|
||||
### CC-спавн (claude-code через deepseek) — live-проверен 2026-09-01 (session:974)
|
||||
|
||||
CC (`claude -p`) умеет субагент-ревью БЕЗ скилов: тот же чистый контекст
|
||||
(флаги изоляции) через обёртку `claude-deepseek`
|
||||
(`.common/scripts/claude-switch.ps1`; env `ANTHROPIC_BASE_URL=
|
||||
https://api.deepseek.com/anthropic` + ключ из `~/.deepseek_api_key`):
|
||||
|
||||
```bash
|
||||
claude -p "<question>" \
|
||||
--output-format stream-json --verbose \
|
||||
--model deepseek-v4-flash-vision-exp \
|
||||
--tools "" --disable-slash-commands --no-session-persistence
|
||||
```
|
||||
|
||||
Флаги изоляции (аналог `-nc -ns -nt` pi): `--tools ""` (нет тулов —
|
||||
не читает файлы, не самопраймится), `--disable-slash-commands` (нет
|
||||
команд-интерпретатора), `--no-session-persistence` (эфемерно, без
|
||||
сессионного блода). Анти-прайминг-чеклист и правила промпта — те же, что
|
||||
выше (ask the behavior, one question per run, negative controls).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Does NOT define the review criteria themselves (skill-specific acceptance —
|
||||
|
||||
@@ -16,9 +16,10 @@ description: >
|
||||
|
||||
# session-health
|
||||
|
||||
Что делать, когда поллер pi (`extensions/session-health.ts`) прислал
|
||||
предупреждение о размере контекста — или когда сам агент подозревает, что
|
||||
сессия раздулась. Поллер — единственный источник точных цифр: footer-статус
|
||||
Что делать, когда поллер pi (`extensions/mappa.ts`, секция session-health —
|
||||
консолидация 6 расширений, task:1486, wiki:3325) прислал предупреждение о
|
||||
размере контекста — или когда сам агент подозревает, что сессия раздулась.
|
||||
Поллер — единственный источник точных цифр: footer-статус
|
||||
(`14.6%/1.0M`) и `/session` агент (LLM) **не видит** — это TUI для человека.
|
||||
|
||||
## When to use
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
# setup-projects-meta
|
||||
|
||||
One-time skill that brings up the local `projects-meta-mcp` stdio server on a
|
||||
new (or freshly broken) machine. Clones the server repo, builds it, writes
|
||||
`~/.config/projects-mcp/auth.toml` with the user's Gitea token, clones the
|
||||
shared wiki to `~/projects/.wiki`, and registers `mcpServers.projects-meta`
|
||||
in `~/.claude.json`.
|
||||
|
||||
The runtime policy for *using* the resulting tools lives in
|
||||
[`using-projects-meta`](../using-projects-meta/) — `setup-projects-meta` is the
|
||||
only place that touches user-level config or installs the server.
|
||||
|
||||
`projects-meta-mcp` reference (full):
|
||||
`mcp__projects-meta__knowledge_get slug=packages/projects-meta-mcp`
|
||||
|
||||
## When it triggers
|
||||
|
||||
- User says: "install projects-meta", "set up projects-meta", "configure projects-meta",
|
||||
"настрой projects-meta", "установи projects-meta", "projects-meta не работает",
|
||||
"projects-meta isn't working".
|
||||
- [`using-projects-meta`](../using-projects-meta/) detects missing
|
||||
`mcp__projects-meta__*` tools and delegates here via its Prerequisites
|
||||
section.
|
||||
- A new machine in the multi-machine fleet — install once per machine.
|
||||
|
||||
## What it installs
|
||||
|
||||
| Path | Role |
|
||||
|---|---|
|
||||
| `~/projects/.common/lib/projects-meta-mcp/` | server repo (cloned from Gitea) |
|
||||
| `~/projects/.common/lib/projects-meta-mcp/dist/server.js` | built stdio entry point |
|
||||
| `~/.config/projects-mcp/auth.toml` | Gitea credentials (token-bearing) |
|
||||
| `~/.cache/projects-mcp/tasks.json` | aggregated tasks cache |
|
||||
| `~/projects/.wiki/` | shared wiki clone (Gitea repo `projects-wiki`, content in root) |
|
||||
| `~/.claude.json` (`mcpServers.projects-meta`) | MCP registration |
|
||||
|
||||
## Hard rules
|
||||
|
||||
- **Never auto-mutate.** Phase 1 (discovery) and Phase 2 (plan) always pause
|
||||
for explicit confirmation. A trigger phrase grants permission to inspect,
|
||||
not to clone or write secrets.
|
||||
- **Never echo the Gitea token in chat.** Edit / Write tool calls inevitably
|
||||
contain it (that's how it lands in `auth.toml`); chat output must not.
|
||||
- **Never clone over an unrelated `~/projects/.wiki/`.**
|
||||
If it exists with a non-matching `origin`, stop and ask — the user
|
||||
may have an unrelated wiki there.
|
||||
- **Always `chmod 600` `auth.toml` on Linux / macOS.** Token leak otherwise.
|
||||
|
||||
## Procedure (high-level)
|
||||
|
||||
1. **Phase 0** — environment sanity (Node ≥ 18, git, npm, network to Gitea).
|
||||
2. **Phase 1** — discovery (token / repo / wiki clone / MCP registration / cache).
|
||||
3. **Phase 2** — plan + confirm. Wait for explicit "ok"/"go"/"поехали".
|
||||
4. **Phase 3** — backup (`~/.claude.json`, existing `auth.toml`).
|
||||
5. **Phase 4** — clone / pull repo + `npm install && npm run build`; clone
|
||||
shared wiki if absent.
|
||||
6. **Phase 5** — write `~/.config/projects-mcp/auth.toml` with `gitea_token`.
|
||||
7. **Phase 6** — register `mcpServers.projects-meta` in `~/.claude.json` with
|
||||
absolute path to `dist/server.js`.
|
||||
8. **Phase 7** — smoke test (`mcp__projects-meta__meta_status`) + `node dist/sync.js`
|
||||
to populate the cache.
|
||||
9. **Phase 8** — restart guidance + final report.
|
||||
|
||||
Full procedure with shell snippets and templates lives in [`SKILL.md`](SKILL.md).
|
||||
|
||||
## Rollback
|
||||
|
||||
1. Stop. Don't fix forward.
|
||||
2. `cp <file>.bak-<ts> <file>` for `~/.claude.json` and `~/.config/projects-mcp/auth.toml`.
|
||||
3. Optional: `rm -rf ~/projects/.common/lib/projects-meta-mcp` and `rm -rf ~/.cache/projects-mcp`.
|
||||
Keep `~/projects/.wiki/` — it's a useful clone regardless of MCP state.
|
||||
4. Restart Claude Code.
|
||||
5. Confirm `mcp__projects-meta__*` tools are gone (or back to the prior version).
|
||||
|
||||
## Install
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
bash scripts/install.sh setup-projects-meta
|
||||
```
|
||||
|
||||
Works on Windows under git-bash, Linux, macOS.
|
||||
|
||||
## See also
|
||||
|
||||
- [`using-projects-meta`](../using-projects-meta/) — runtime policy for
|
||||
cross-project task aggregation and shared-wiki query / ingest.
|
||||
- [`setup-context7`](../setup-context7/) — companion pattern for the context7
|
||||
MCP plugin (similar 8-phase shape).
|
||||
- Per-project вики/таски живут в mappa (мета в сервисе, решения 14/15) —
|
||||
setup-скилов `.wiki/`/`.tasks/` больше нет; операции — `using-wiki` /
|
||||
`using-tasks`.
|
||||
@@ -1,244 +0,0 @@
|
||||
---
|
||||
name: setup-projects-meta
|
||||
author: ours
|
||||
version: 1.1.0
|
||||
description: Installs and configures the local `projects-meta-mcp` stdio server — clones the repo to `~/projects/.common/lib/projects-meta-mcp`, builds it, writes `~/.config/projects-mcp/auth.toml` with the user's Gitea token, clones the shared wiki to `~/projects/.wiki/` (content lives in root), and registers `mcpServers.projects-meta` in `~/.claude.json`. Use this skill when the user says "install projects-meta", "set up projects-meta", "configure projects-meta", "настрой projects-meta", "установи projects-meta", "projects-meta не работает", "projects-meta isn't working", or whenever the `mcp__projects-meta__*` tools are missing in a session that needs cross-project task aggregation or the shared Gitea wiki. Cross-platform — Windows / Linux / macOS. Mutates user-level config and writes secrets; pauses for confirmation before every write.
|
||||
---
|
||||
|
||||
# setup-projects-meta
|
||||
|
||||
> One-time skill that gets the local `projects-meta-mcp` server running with the user's Gitea credentials. Stops at confirmation gates because the procedure clones a repo, writes a secret-bearing TOML file, and edits `~/.claude.json`.
|
||||
|
||||
Reference: full `projects-meta-mcp` docs live in the shared wiki at `packages/projects-meta-mcp` (fetch via `mcp__projects-meta__knowledge_get` once the server is up).
|
||||
|
||||
## When to use
|
||||
|
||||
- User explicitly asks: install / set up / configure projects-meta.
|
||||
- A `using-projects-meta`-driven task fails because `mcp__projects-meta__*` tools aren't available.
|
||||
- Migrating a stale install (token expired, repo moved, broken cache) — same procedure, Phase 1 detects what's already in place.
|
||||
- New machine in the user's multi-machine fleet (recall: this is a cross-machine workflow).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Issuing or rotating Gitea tokens. This skill *uses* a token the user already has; if there's no token, point them at Gitea's settings page (`https://git.kzntsv.site/user/settings/applications`) and stop until they paste one.
|
||||
- Running `projects-meta-mcp` itself (the MCP harness spawns it).
|
||||
- Editing `.tasks/STATUS.md` or wiki content — that's `using-projects-meta` / `using-tasks` / `using-wiki`.
|
||||
- Any other MCP server.
|
||||
|
||||
## Hard rule: don't auto-mutate config
|
||||
|
||||
The procedure clones a repo, writes `~/.config/projects-mcp/auth.toml` (carries the Gitea token), and edits `~/.claude.json`. **Always pause for explicit confirmation between Phase 1 (discovery, read-only) and Phase 2 (plan), and again before Phase 3 (backup + writes).** A trigger phrase is permission to run discovery, not permission to clone or write secrets.
|
||||
|
||||
## Procedure
|
||||
|
||||
### Phase 0 — Environment sanity
|
||||
|
||||
- Confirm Claude Code is the current harness (need `mcpServers` registration in `~/.claude.json`).
|
||||
- Confirm `git`, `node`, `npm` are on `PATH`. Node ≥ 18 (the server uses ES modules).
|
||||
- Confirm network reachability to `https://git.kzntsv.site` (the Gitea host). On HTTP 401/403 later, the token is dead — stop and ask for a new one.
|
||||
- Pick paths: `~/projects/.common/lib/projects-meta-mcp`, `~/.config/projects-mcp/`, `~/.cache/projects-mcp/`, `~/projects/.wiki/` (shared wiki clone). POSIX-style `~/...` resolves correctly under git-bash on Windows.
|
||||
|
||||
### Phase 1 — Discovery (read-only)
|
||||
|
||||
Search, in order. Report only "found at <path>", never echo token values.
|
||||
|
||||
**Existing Gitea token.** Look in priority order:
|
||||
|
||||
1. `~/.config/projects-mcp/auth.toml` → `gitea_token = "..."`
|
||||
2. Env var `PROJECTS_META_GITEA_TOKEN`
|
||||
3. Existing `~/.claude.json` → `mcpServers.projects-meta` block (rare; legacy installs sometimes inline `env.GITEA_TOKEN`)
|
||||
|
||||
The first hit wins. Capture internally for Phase 5; **never echo it in chat**.
|
||||
|
||||
**Repo install state.** Check whether `~/projects/.common/lib/projects-meta-mcp/.git` exists. If yes, `git -C ~/projects/.common/lib/projects-meta-mcp rev-parse HEAD` → record the SHA so Phase 4 can decide *clone* vs *pull*.
|
||||
|
||||
**Build artifact.** Check `~/projects/.common/lib/projects-meta-mcp/dist/server.js`. If absent, Phase 4 will run `npm install && npm run build`.
|
||||
|
||||
**Shared wiki clone.** Check if `~/projects/.wiki/.git` exists and `origin` matches `https://git.kzntsv.site/OpeItcLoc03/projects-wiki(.git)?`. If non-matching `origin`, stop and ask — the user may have an unrelated wiki there.
|
||||
|
||||
**MCP registration.** Read `~/.claude.json` and check `mcpServers.projects-meta`. Note the command + args. If args point at a stale path, Phase 6 will fix it.
|
||||
|
||||
**Cache state.** List `~/.cache/projects-mcp/` (if it exists). Just for the report — don't rely on it.
|
||||
|
||||
### Phase 2 — Plan + confirm
|
||||
|
||||
Present a single-block plan to the user:
|
||||
|
||||
```
|
||||
Token: <found at <path> | NOT FOUND — will ask>
|
||||
Repo: <installed at ~/projects/.common/lib/projects-meta-mcp@<sha> | will clone>
|
||||
Build artifact: <present | will run npm install && npm run build>
|
||||
Wiki clone: <present at ~/projects/.wiki | will clone | wrong remote — STOP>
|
||||
MCP entry: <present in ~/.claude.json | will add | will fix path>
|
||||
Backups: ~/.claude.json.bak-<ts>, ~/.config/projects-mcp/auth.toml.bak-<ts> (if exists)
|
||||
```
|
||||
|
||||
Wait for explicit confirmation ("ok", "go", "поехали"). Anything else → stop.
|
||||
|
||||
If no token was found in Phase 1 — first ask: "Paste a Gitea personal access token (scope: `read:repository` for read-only, `write:repository` to enable mutations), or open `https://git.kzntsv.site/user/settings/applications` to create one." Don't proceed past Phase 2 without a token.
|
||||
|
||||
### Phase 3 — Backup
|
||||
|
||||
Copy each file we will modify to `<file>.bak-YYYYMMDD-HHMMSS`:
|
||||
|
||||
```bash
|
||||
TS=$(date +%Y%m%d-%H%M%S)
|
||||
[ -f ~/.claude.json ] && cp ~/.claude.json ~/.claude.json.bak-$TS
|
||||
[ -f ~/.config/projects-mcp/auth.toml ] && cp ~/.config/projects-mcp/auth.toml ~/.config/projects-mcp/auth.toml.bak-$TS
|
||||
```
|
||||
|
||||
Confirm both backups exist (when their source existed) before any further edit. The repo and wiki clones don't need backup — git is the backup.
|
||||
|
||||
### Phase 4 — Clone + build
|
||||
|
||||
```bash
|
||||
# Server
|
||||
if [ -d ~/projects/.common/lib/projects-meta-mcp/.git ]; then
|
||||
git -C ~/projects/.common/lib/projects-meta-mcp pull --ff-only
|
||||
else
|
||||
git clone https://git.kzntsv.site/OpeItcLoc03/projects-meta-mcp ~/projects/.common/lib/projects-meta-mcp
|
||||
fi
|
||||
cd ~/projects/.common/lib/projects-meta-mcp
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
# Shared wiki
|
||||
mkdir -p ~/projects
|
||||
if [ -d ~/projects/.wiki/.git ]; then
|
||||
git -C ~/projects/.wiki pull --ff-only
|
||||
else
|
||||
git clone https://git.kzntsv.site/OpeItcLoc03/projects-wiki ~/projects/.wiki
|
||||
fi
|
||||
```
|
||||
|
||||
Verify `~/projects/.common/lib/projects-meta-mcp/dist/server.js` exists after build. If not — abort, the build failed; ask the user to run `npm run build` manually and paste the output.
|
||||
|
||||
### Phase 5 — Write `auth.toml`
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/projects-mcp
|
||||
```
|
||||
|
||||
If `~/.config/projects-mcp/auth.toml` already exists and Phase 1 found a valid `gitea_token` line — skip the write. Otherwise, write the file with the token captured in Phase 1 (or freshly pasted in Phase 2):
|
||||
|
||||
```toml
|
||||
gitea_url = "https://git.kzntsv.site"
|
||||
gitea_user = "OpeItcLoc03" # acting identity (commit author)
|
||||
gitea_token = "<TOKEN>"
|
||||
gitea_owners = ["victor", "cancel_music"] # additional Gitea owners to sync
|
||||
agenda_tasks_repo = "OpeItcLoc03/agenda" # cross-project meta-board (qualified)
|
||||
# gitea_aggregate_skip_owners = ["OpeItcLoc03"] # opt: sync but hide from `tasks_aggregate`
|
||||
```
|
||||
|
||||
**Schema notes (v2.x server):**
|
||||
|
||||
- `gitea_owners` is an array of owners whose repos are scanned by `sync.js` and surfaced in aggregation views. `gitea_user` is acting identity only (commit author footer), not necessarily aggregated.
|
||||
- `agenda_tasks_repo` is **qualified** (`<owner>/<repo>`). The literal `agenda` in `target_project` resolves through this field.
|
||||
- `gitea_aggregate_skip_owners` (optional, v2.2.0+) — visited by sync (so mutations work via cache lookup) but hidden from `tasks_aggregate` / `tasks_search`. Useful for keeping infra repos write-able without polluting the dashboard.
|
||||
- Backwards-compat: legacy installs with only `gitea_user = "X"` and no `gitea_owners` → server reads as `gitea_owners = ["X"]`.
|
||||
- Legacy `meta_tasks_repo` / `meta_wiki_repo` → renamed to `agenda_tasks_repo` / built-in `projects-wiki`. Old keys ignored on v2.x.
|
||||
|
||||
Permissions: on Linux / macOS run `chmod 600 ~/.config/projects-mcp/auth.toml`. On Windows the default ACL is per-user, no extra step.
|
||||
|
||||
### Phase 6 — Register in `~/.claude.json`
|
||||
|
||||
Edit `~/.claude.json`. Add or update the `mcpServers.projects-meta` block:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"projects-meta": {
|
||||
"command": "node",
|
||||
"args": ["<ABSOLUTE_PATH_TO>/dist/server.js"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Absolute path resolution:
|
||||
|
||||
| Platform | `<ABSOLUTE_PATH_TO>` |
|
||||
|---|---|
|
||||
| Windows | `C:/Users/<USER>/projects/.common/lib/projects-meta-mcp` (forward slashes; works in JSON without escaping) |
|
||||
| Linux | `/home/<USER>/projects/.common/lib/projects-meta-mcp` |
|
||||
| macOS | `/Users/<USER>/projects/.common/lib/projects-meta-mcp` |
|
||||
|
||||
After each edit, validate JSON:
|
||||
|
||||
```bash
|
||||
# Windows (git-bash)
|
||||
powershell.exe -NoProfile -c "Get-Content '<file>' -Raw | ConvertFrom-Json | Out-Null"
|
||||
# Linux / macOS
|
||||
jq empty <file>
|
||||
# fallback
|
||||
python -c "import json; json.load(open('<file>'))"
|
||||
```
|
||||
|
||||
If validation fails → restore from `.bak-*` and abort.
|
||||
|
||||
### Phase 7 — Smoke test (best-effort) + run initial sync
|
||||
|
||||
Best-effort: call `mcp__projects-meta__meta_status`. If it returns a JSON blob with `synced_at` / `wiki_pages_count` — the server is reachable in *this* session.
|
||||
|
||||
Then run a one-shot sync to populate the cache:
|
||||
|
||||
```bash
|
||||
cd ~/projects/.common/lib/projects-meta-mcp
|
||||
node dist/sync.js
|
||||
```
|
||||
|
||||
Expect a non-zero `projects_count` and a fresh `~/.cache/projects-mcp/tasks.json`. On 401 / 403 → token is wrong scope or expired; rotate via `https://git.kzntsv.site/user/settings/applications` and re-edit `auth.toml`.
|
||||
|
||||
**Important caveat to relay to the user:** in the *same* session that just ran setup, the MCP server you're talking to is whatever was bound at session start. So a passing `meta_status` only proves "some projects-meta is alive" — not "the registration we just wrote is what's serving it". The real test is after Claude Code restart.
|
||||
|
||||
### Phase 8 — Restart guidance + final report
|
||||
|
||||
Tell the user:
|
||||
|
||||
```
|
||||
✅ Setup complete. Restart Claude Code so the new mcpServers.projects-meta
|
||||
registration binds to a fresh stdio session.
|
||||
|
||||
After restart:
|
||||
• mcp__projects-meta__* tools serve from ~/projects/.common/lib/projects-meta-mcp/dist/server.js
|
||||
• Cache lives at ~/.cache/projects-mcp/tasks.json (refresh: node dist/sync.js)
|
||||
• Shared wiki clone at ~/projects/.wiki/ — `git -C ~/projects/.wiki pull --ff-only` for fresh anchors
|
||||
• Backups saved at ~/.claude.json.bak-<ts> (and auth.toml.bak-<ts> if it existed before)
|
||||
|
||||
If something breaks after restart:
|
||||
• Restore from .bak-* and tell me — we'll roll back together.
|
||||
```
|
||||
|
||||
## Rollback procedure
|
||||
|
||||
If a problem surfaces (now or after restart):
|
||||
|
||||
1. Stop. Don't try to fix forward.
|
||||
2. Find the most recent `.bak-YYYYMMDD-HHMMSS` next to `~/.claude.json` (and `~/.config/projects-mcp/auth.toml` if applicable).
|
||||
3. `cp <file>.bak-<ts> <file>` for each.
|
||||
4. Optional: `rm -rf ~/projects/.common/lib/projects-meta-mcp` and `rm -rf ~/.cache/projects-mcp`. Keep `~/projects/.wiki/` — it's a useful clone regardless of MCP state.
|
||||
5. Restart Claude Code.
|
||||
6. Confirm `mcp__projects-meta__*` is gone (or back to the pre-existing version).
|
||||
7. Report what went wrong so we can fix the procedure.
|
||||
|
||||
## Cross-platform notes
|
||||
|
||||
The procedure is platform-agnostic. Only auxiliary tooling differs:
|
||||
|
||||
| | JSON validate | Backup | Permissions on auth.toml |
|
||||
|---|---|---|---|
|
||||
| Windows (git-bash) | `powershell.exe -NoProfile -c "Get-Content '<f>' -Raw \| ConvertFrom-Json \| Out-Null"` | `cp` | per-user ACL by default |
|
||||
| Linux | `jq empty <f>` (or `python -c "import json; json.load(open('<f>'))"`) | `cp` | `chmod 600` |
|
||||
| macOS | same as Linux | `cp` | `chmod 600` |
|
||||
|
||||
Path forms (`~/.local/...`, `~/.config/...`, `~/projects/...`) are identical on all three.
|
||||
|
||||
## Common mistakes
|
||||
|
||||
- **Skipping Phase 1.** "User just said 'install projects-meta' — let's go." No — find existing token / repo / wiki first; re-cloning over an existing install loses any local commits in the wiki.
|
||||
- **Echoing the token.** It's a secret. Edit / Write tool calls inevitably contain it (that's how it gets into `auth.toml`), but no chat output should.
|
||||
- **Cloning over an unrelated `~/projects/.wiki/`.** If it exists with a different `origin`, stop. The user may have an unrelated wiki there.
|
||||
- **Writing `auth.toml` with `0644` perms on Linux/macOS.** Token leak. Always `chmod 600` after write.
|
||||
- **Treating in-session `meta_status` as proof.** Same as the context7 caveat — the active MCP connection was bound at session start.
|
||||
- **Auto-running on every "use projects-meta".** This skill is intrusive. Trigger only on explicit "install/setup/configure projects-meta", or when MCP tools are missing and the user is blocked.
|
||||
- **Forgetting `node dist/sync.js`.** Without an initial sync, the cache is empty and `tasks_aggregate` returns nothing — the user thinks setup failed.
|
||||
@@ -85,11 +85,10 @@ setup; pi loads `~/.agents/skills` by default. Verify with `pi --help` / a fresh
|
||||
The update scripts (`scripts/update.sh` and `scripts/update.ps1`) handle:
|
||||
|
||||
1. **git pull --ff-only** in `~/projects/skills/` (stash if dirty, pop after).
|
||||
2. **Conditionally rebuild projects-meta-mcp** — if `~/projects/.common/lib/projects-meta-mcp/` has a `.git` directory and source changed (`git pull` fetched new commits), run `npm run build`.
|
||||
3. **Conditionally rebuild interns-mcp** — same pattern, `pip install -e .`.
|
||||
4. **Install all skills** via `install.sh` / `install.ps1`.
|
||||
5. **Show version diff** — before/after `version:` frontmatter for each skill.
|
||||
6. **Print reload hints** — `/reload-mcp` if MCP changed, new session otherwise.
|
||||
2. **Conditionally rebuild interns-mcp** — same pattern, `pip install -e .`.
|
||||
3. **Install all skills** via `install.sh` / `install.ps1`.
|
||||
4. **Show version diff** — before/after `version:` frontmatter for each skill.
|
||||
5. **Print reload hints** — `/reload-mcp` if MCP changed, new session otherwise.
|
||||
|
||||
## Out of scope
|
||||
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
# using-projects-meta
|
||||
|
||||
Runtime policy for the local `projects-meta-mcp` stdio server. Two
|
||||
responsibilities, one server:
|
||||
|
||||
1. **Cross-project task aggregation** — reads / writes `.tasks/STATUS.md` in
|
||||
any of the user's Gitea repos.
|
||||
2. **Shared knowledge wiki** — query / ingest a single Gitea-backed wiki at
|
||||
`~/projects/projects-wiki/.wiki/` (clone root: `~/projects/projects-wiki/`,
|
||||
Gitea repo: `projects-wiki`).
|
||||
|
||||
`using-projects-meta` governs *usage* of an installed server. Initial setup
|
||||
(clone, build, `auth.toml`, MCP registration) is owned by
|
||||
[`setup-projects-meta`](../setup-projects-meta/).
|
||||
|
||||
Full server reference:
|
||||
`mcp__projects-meta__knowledge_get slug=packages/projects-meta-mcp`.
|
||||
|
||||
## When it triggers
|
||||
|
||||
- User asks for cross-project state ("what's on the boards", "across all
|
||||
projects", "что у меня на досках", "по всем проектам").
|
||||
- User wants to query / ingest the shared wiki ("check shared wiki", "search
|
||||
projects-wiki", "ingest into shared wiki", "общая вики", "заингесть в общую").
|
||||
- User wants to create / update / close a task in *another* project from the
|
||||
current cwd ("заведи в проекте X задачу", "close task Y in project Z").
|
||||
- User asks for sync diagnostics ("when did the cache last refresh", "are there
|
||||
sync errors").
|
||||
- If `mcp__projects-meta__*` tools are missing, this skill delegates to
|
||||
[`setup-projects-meta`](../setup-projects-meta/) before doing anything else.
|
||||
|
||||
## Local-first rule (critical)
|
||||
|
||||
For the **current** project — read disk directly (`.tasks/STATUS.md`,
|
||||
`.wiki/index.md`). The MCP cache:
|
||||
|
||||
- May be stale (sync runs only when triggered).
|
||||
- Hides `🟢 done` by default.
|
||||
- May not contain unpushed projects.
|
||||
|
||||
Use MCP only for **other** projects, **other** machines, or the **shared**
|
||||
wiki content. See the table below.
|
||||
|
||||
| Question | Where to read |
|
||||
|---|---|
|
||||
| "What's the status of *this* project?" | local `.tasks/STATUS.md` |
|
||||
| "What's on all my boards?" | `mcp__projects-meta__tasks_aggregate` |
|
||||
| "Has *this* project's wiki got X?" | local `.wiki/index.md` |
|
||||
| "Has the **shared** wiki got X?" | `mcp__projects-meta__knowledge_search` |
|
||||
| "Sync state across machines?" | `mcp__projects-meta__meta_status` |
|
||||
|
||||
## Step 0 — Freshness gate (v1.1.0, mandatory pre-flight)
|
||||
|
||||
`projects-meta` is a bus between machines — another host may have pushed
|
||||
minutes ago. Without this gate, reads return stale data and writes hit
|
||||
sha-based optimistic-lock 422s with no explanation.
|
||||
|
||||
Before **any** `tasks_*` or `knowledge_*` call:
|
||||
|
||||
1. `mcp__projects-meta__meta_status` — probe cache age + errors.
|
||||
2. If `cache_age_minutes` > 10 OR `errors_count` > 0 →
|
||||
`node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js`.
|
||||
3. For shared-wiki **writes** (`knowledge_ingest`, `knowledge_promote`) →
|
||||
**also** `git -C ~/projects/projects-wiki pull --ff-only`. Unconditional.
|
||||
The MCP server uses sha-based optimistic locking on the wiki repo;
|
||||
without an up-to-date local SHA the commit is rejected with a 422.
|
||||
4. For tasks-mutations (`tasks_create`/`update`/`close`) → sync via
|
||||
`dist/sync.js` is enough; there's no local clone of the target tasks repo.
|
||||
5. If sync returns **401 / 403** → STOP. Token is dead. Send the user to
|
||||
`~/.config/projects-mcp/auth.toml` to rotate `gitea_token`. Don't
|
||||
pretend success, don't retry silently.
|
||||
|
||||
**Don't sync unconditionally** on every call — overhead + 401-risk for
|
||||
casual reads. The 10-minute window is the chosen threshold.
|
||||
|
||||
**Don't apply Step 0 to `meta_status` itself** — it's the probe.
|
||||
|
||||
## Two operation classes
|
||||
|
||||
### Read (no confirmation)
|
||||
|
||||
`tasks_aggregate`, `tasks_search`, `tasks_get`, `knowledge_search`,
|
||||
`knowledge_get`, `knowledge_suggest_promote`, `meta_status` — all
|
||||
side-effect-free. Call directly, cite the result.
|
||||
|
||||
### Mutate (always two-step)
|
||||
|
||||
`tasks_create`, `tasks_update`, `tasks_close`, `knowledge_ingest`,
|
||||
`knowledge_promote` — write to Gitea. Procedure:
|
||||
|
||||
1. Call **without** `confirm: true` → returns dry-run preview (proposed file
|
||||
diff + commit message).
|
||||
2. Show the preview to the user. Wait for explicit "ok" / "go" / "поехали".
|
||||
3. Re-call with `confirm: true` → committed.
|
||||
|
||||
**Never inline `confirm: true` on the first call.** A trigger phrase is
|
||||
permission to plan, not to commit.
|
||||
|
||||
## Tool quick reference
|
||||
|
||||
### Read
|
||||
|
||||
| Tool | Required args | Purpose |
|
||||
|---|---|---|
|
||||
| `mcp__projects-meta__tasks_aggregate` | — | All active tasks across cached projects |
|
||||
| `mcp__projects-meta__tasks_search` | `query` | Substring search across slug + next_action |
|
||||
| `mcp__projects-meta__tasks_get` | `project` | Raw STATUS.md of one project (cache snapshot) |
|
||||
| `mcp__projects-meta__knowledge_search` | `query`; opt `domain`, `limit` | Shared-wiki search; default domain auto-detected from cwd |
|
||||
| `mcp__projects-meta__knowledge_get` | `slug` | Full text of one wiki page |
|
||||
| `mcp__projects-meta__knowledge_suggest_promote` | — | Local `.wiki/concepts/` candidates for shared promotion |
|
||||
| `mcp__projects-meta__meta_status` | — | Sync diagnostics (cache age, project / page / error counts) |
|
||||
|
||||
### Mutate (need `write:repository` Gitea scope)
|
||||
|
||||
| Tool | Required args | Effect |
|
||||
|---|---|---|
|
||||
| `mcp__projects-meta__tasks_create` | `target_project`, `slug`, `description`, `next_action` | Append block to target's `.tasks/STATUS.md` via Gitea commit |
|
||||
| `mcp__projects-meta__tasks_update` | `target_project`, `slug` + ≥1 mutable field | Sha-based optimistic lock; 422 on conflict |
|
||||
| `mcp__projects-meta__tasks_close` | `target_project`, `slug` (+ opt `note`) | Marks task 🟢 done with identity-footer |
|
||||
| `mcp__projects-meta__knowledge_ingest` | `target_project`, `type`, `slug`, `body` | Three commits: `<type>/<slug>.md` + `index.md` + `log.md` |
|
||||
| `mcp__projects-meta__knowledge_promote` | `target_project`, `slug`, `body` | Move `raw/<slug>.md` → `summaries/<slug>.md` |
|
||||
|
||||
`type` ∈ `entities` / `concepts` / `packages` / `summaries` / `raw`.
|
||||
`target_project` = Gitea repo name, or `_meta` (meta-tasks / meta-wiki repos
|
||||
from `auth.toml`).
|
||||
|
||||
## Common mistakes
|
||||
|
||||
- **Reading current project's tasks via `tasks_get`.** Read `.tasks/STATUS.md`
|
||||
on disk; the MCP cache is for *other* projects.
|
||||
- **Inlining `confirm: true` on first call.** Always preview first; show user;
|
||||
only then `confirm: true`.
|
||||
- **Confusing the local `.wiki/` with the shared `projects-wiki`.** They are
|
||||
two different stores. `using-wiki` operates on the local one;
|
||||
`using-projects-meta` queries / ingests the shared one.
|
||||
- **Acting on stale `tasks_aggregate`.** If `meta_status.age_seconds` > 3600,
|
||||
either run `node dist/sync.js` (in `~/projects/.common/lib/projects-meta-mcp`) or warn the
|
||||
user about staleness.
|
||||
- **Vague `knowledge_search` queries.** "auth" returns noise. Multi-word,
|
||||
specific queries return targeted snippets.
|
||||
- **Wrong `type` on `knowledge_ingest`.** Mis-typed pages land in the wrong
|
||||
section and break `index.md`. Pick from the five canonical types.
|
||||
|
||||
## When NOT to use
|
||||
|
||||
- The current project's own tasks — read `.tasks/STATUS.md`.
|
||||
- The current project's own wiki — read `.wiki/`.
|
||||
- Library / framework documentation — that's [`using-context7`](../using-context7/).
|
||||
- Repo-internal code search — that's `Glob` / `Grep`.
|
||||
- One-off git history questions — `git log`.
|
||||
|
||||
## Install
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
bash scripts/install.sh using-projects-meta
|
||||
```
|
||||
|
||||
Works on Windows under git-bash, Linux, macOS.
|
||||
|
||||
## See also
|
||||
|
||||
- [`setup-projects-meta`](../setup-projects-meta/) — companion, owns server
|
||||
install + MCP registration.
|
||||
- [`using-context7`](../using-context7/) — sister skill for library docs (same
|
||||
using-X structure).
|
||||
- [`using-tasks`](../using-tasks/), [`using-wiki`](../using-wiki/) —
|
||||
per-project policies for in-repo `.tasks/` and `.wiki/`. Orthogonal to this
|
||||
skill; together they cover both per-project and cross-project state.
|
||||
@@ -1,238 +0,0 @@
|
||||
---
|
||||
name: using-projects-meta
|
||||
author: ours
|
||||
version: 1.3.0
|
||||
description: Use when working across multiple projects on one or many machines — cross-project task aggregation (`mcp__projects-meta__tasks_*`), shared Gitea-backed wiki query / ingest (`mcp__projects-meta__knowledge_*`), or sync diagnostics (`mcp__projects-meta__meta_status`). Triggers on phrases like "across all projects", "what's on the boards", "check shared wiki", "search projects-wiki", "ingest into shared wiki", "что у меня на досках", "по всем проектам", "общая вики", "cross-project status", or any time the user wants to see / mutate state in another repo than the current cwd. v1.1.0 mandates a Step 0 freshness gate (probe `meta_status`, sync if stale, pull `projects-wiki` before shared-wiki writes) — see SKILL body. Mutation tools require two-step preview → confirm. Skip for the **current** project's tasks/wiki — those live on disk in `.tasks/` / `.wiki/`.
|
||||
---
|
||||
|
||||
# Using the projects-meta MCP server
|
||||
|
||||
## Overview
|
||||
|
||||
`projects-meta-mcp` is a local stdio MCP server. Two responsibilities:
|
||||
|
||||
1. **Cross-project task aggregation** — parses `.tasks/STATUS.md` from every repo on the user's Gitea, caches them in `~/.cache/projects-mcp/tasks.json`. Read tools (`tasks_aggregate`, `tasks_search`, `tasks_get`) hit the cache. Mutations (`tasks_create`, `tasks_update`, `tasks_close`) commit back to Gitea with sha-based optimistic lock.
|
||||
2. **Shared knowledge wiki** — single Gitea repo (`projects-wiki`) cloned at `~/projects/projects-wiki/` with content at `~/projects/projects-wiki/.wiki/`, structured as packages / concepts / entities / summaries / raw. `knowledge_search` + `knowledge_get` for queries, `knowledge_ingest` + `knowledge_promote` for writes.
|
||||
|
||||
Source of truth: Gitea (`https://git.kzntsv.site`, owner `OpeItcLoc03`). Cache and clone are local convenience.
|
||||
|
||||
Full reference: `mcp__projects-meta__knowledge_get slug=packages/projects-meta-mcp`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This skill assumes `mcp__projects-meta__*` tools are available. If they aren't (tools missing from the session, or calls fail with a connection error), the server isn't running for this session. Trigger the **`setup-projects-meta`** skill to clone, build, write `auth.toml`, and register `mcpServers.projects-meta` in `~/.claude.json`. It's a one-time procedure with confirmation gates.
|
||||
|
||||
## Local-first rule
|
||||
|
||||
**For the current project — read disk directly.** `.tasks/STATUS.md` and `.wiki/` files in cwd are always fresher than the MCP cache. The cache:
|
||||
|
||||
- May be stale (default sync runs only when triggered).
|
||||
- Hides 🟢 done by default.
|
||||
- May not contain locally-developed projects that aren't pushed to Gitea yet.
|
||||
|
||||
Use MCP only for **other** projects, **other** machines, or **shared** wiki content.
|
||||
|
||||
| Question | Where to read |
|
||||
|---|---|
|
||||
| "What's the status of *this* project?" | local `.tasks/STATUS.md` |
|
||||
| "What's on all my boards?" | `mcp__projects-meta__tasks_aggregate` |
|
||||
| "Has *this* project's wiki got a page on X?" | local `.wiki/index.md` + relevant file |
|
||||
| "Has the **shared** wiki got a page on X?" | `mcp__projects-meta__knowledge_search` |
|
||||
| "Sync state across machines?" | `mcp__projects-meta__meta_status` |
|
||||
|
||||
## When to use
|
||||
|
||||
- Cross-project task overview ("what am I working on across projects", "по всем проектам", "across the board").
|
||||
- Hopping into another repo's task state without cloning it ("what's the status of project X").
|
||||
- Querying the shared wiki for cross-cutting concepts (patterns, package references, design notes that apply to several repos).
|
||||
- Ingesting a finished design / decision into the shared wiki so other machines / projects can see it.
|
||||
- Creating a task in another project's `.tasks/STATUS.md` from the current repo (cross-project handoff).
|
||||
- Sync diagnostics (when did the cache last refresh, are there errors, how many projects).
|
||||
|
||||
## When NOT to use
|
||||
|
||||
- The current project's own tasks or wiki — read disk.
|
||||
- Anything inside a single project — `.tasks/<task>.md` and `.wiki/<page>.md` are always closer.
|
||||
- One-off questions answered by `git log` or a single file.
|
||||
- Library / framework documentation — that's `using-context7`.
|
||||
- Code search — that's `Glob` / `Grep`.
|
||||
|
||||
## Step 0 — Freshness gate (run before any tool)
|
||||
|
||||
`projects-meta` is a bus between machines. Another host may have pushed minutes ago. Without this gate, reads return stale data and writes hit sha-based optimistic-lock 422s with no explanation. Mandatory pre-flight, every session, every workflow:
|
||||
|
||||
```
|
||||
1. Call mcp__projects-meta__meta_status.
|
||||
|
||||
2. Branch on cache freshness:
|
||||
• If cache_age_minutes > 10 OR errors_count > 0:
|
||||
run `node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js`
|
||||
(or `npm run sync` from ~/projects/.common/lib/projects-meta-mcp).
|
||||
• Else: cache is fresh enough — skip sync, no need to hit the network.
|
||||
|
||||
3. For shared-wiki WRITES (knowledge_ingest, knowledge_promote):
|
||||
ALWAYS additionally run `git -C ~/projects/projects-wiki pull --ff-only`
|
||||
regardless of cache age. The MCP server uses sha-based optimistic locking;
|
||||
without an up-to-date local file SHA, the commit will be rejected (422)
|
||||
and the failure mode is opaque to the user.
|
||||
|
||||
4. For tasks-mutations (tasks_create, tasks_update, tasks_close):
|
||||
sync via dist/sync.js is enough — there's no local clone of the target
|
||||
tasks repo, mutations go straight through Gitea API. Sync only refreshes
|
||||
the local view so you reason from current state.
|
||||
|
||||
5. If sync returns 401 or 403:
|
||||
STOP. The Gitea token in ~/.config/projects-mcp/auth.toml is dead or
|
||||
wrong-scoped. Tell the user explicitly:
|
||||
"Gitea sync failed with <401|403>. Rotate gitea_token in
|
||||
~/.config/projects-mcp/auth.toml (Gitea: settings/applications)
|
||||
and rerun."
|
||||
Do not pretend sync succeeded. Do not retry silently.
|
||||
```
|
||||
|
||||
**Don't sync unconditionally on every call.** Network overhead + risk of 401 even on a casual "what's on my boards". The 10-minute cache window is the right balance — catches multi-machine drift without burning Gitea round-trips for back-to-back questions.
|
||||
|
||||
**Don't apply Step 0 to `meta_status` itself** — it's the freshness probe, not a downstream read.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Read (no confirmation needed)
|
||||
|
||||
```
|
||||
0. Run Step 0 — Freshness gate (above) first.
|
||||
1. Identify what you need: cross-project tasks? shared wiki page? sync state?
|
||||
2. Pick the right read tool (table below).
|
||||
3. Cite the result with the source slug / project name.
|
||||
```
|
||||
|
||||
### Mutate (always two-step)
|
||||
|
||||
```
|
||||
0. Run Step 0 — Freshness gate (above) first.
|
||||
For shared-wiki writes (knowledge_ingest, knowledge_promote): unconditional
|
||||
`git -C ~/projects/projects-wiki pull --ff-only` is part of Step 0.
|
||||
1. Identify the mutation: tasks_create / tasks_update / tasks_close / knowledge_ingest / knowledge_promote.
|
||||
2. Call the tool WITHOUT `confirm: true` → returns a dry-run preview (the proposed file diff and the Gitea commit message).
|
||||
3. Show the preview to the user. Wait for explicit "ok" / "go" / "поехали".
|
||||
4. Re-call with `confirm: true` to commit.
|
||||
```
|
||||
|
||||
**Never inline `confirm: true` on the first call.** A trigger phrase ("create a task in project X") is permission to *plan*, not to *commit*.
|
||||
|
||||
## Tool quick reference
|
||||
|
||||
### Read tools
|
||||
|
||||
| Tool | Required args | Purpose |
|
||||
|---|---|---|
|
||||
| `mcp__projects-meta__tasks_aggregate` | — | All active tasks across all cached projects |
|
||||
| `mcp__projects-meta__tasks_search` | `query` | Substring search across slug + next_action |
|
||||
| `mcp__projects-meta__tasks_get` | `project` | Raw STATUS.md of one project (cached snapshot) |
|
||||
| `mcp__projects-meta__knowledge_search` | `query`; opt `domain`, `limit` | Shared-wiki search; auto-detects domain from cwd, pass `domain="all"` to disable |
|
||||
| `mcp__projects-meta__knowledge_get` | `slug` | Full text of one wiki page (e.g. `packages/projects-meta-mcp`) |
|
||||
| `mcp__projects-meta__knowledge_suggest_promote` | — | Local `.wiki/concepts/` candidates for shared-wiki promotion |
|
||||
| `mcp__projects-meta__meta_status` | — | Sync diagnostics: cache age, project count, error count, page count |
|
||||
|
||||
### Mutation tools (need `write:repository` Gitea scope; preview → confirm)
|
||||
|
||||
| Tool | Required args | Effect |
|
||||
|---|---|---|
|
||||
| `mcp__projects-meta__tasks_create` | `target_project`, `slug`, `description`, `next_action` (+ opt `where_stopped`, `status`, `blocker`, `branch`, `source_project`) | Append block to `<target>/.tasks/STATUS.md` via Gitea commit |
|
||||
| `mcp__projects-meta__tasks_update` | `target_project`, `slug` + ≥1 of `where_stopped` / `next_action` / `blocker` / `branch` / `description` / `status` | Sha-based optimistic lock; 422 on conflict |
|
||||
| `mcp__projects-meta__tasks_close` | `target_project`, `slug` (+ opt `note`) | Sets task to 🟢 done; appends identity-footer |
|
||||
| `mcp__projects-meta__knowledge_ingest` | `target_project`, `type`, `slug`, `body` (+ opt `frontmatter`, `source_project`) | Three commits: `<type>/<slug>.md` + `index.md` + `log.md`. `type` ∈ entities / concepts / packages / summaries / raw |
|
||||
| `mcp__projects-meta__knowledge_promote` | `target_project`, `slug`, `body` (+ opt `frontmatter`, `source_project`) | Move `raw/<slug>.md` → `summaries/<slug>.md` with auto `raw_path` link |
|
||||
|
||||
`target_project` is **qualified** `<owner>/<repo>` (e.g. `victor/books`, `OpeItcLoc03/skills`), or the literal `agenda` for the cross-project meta-board (resolves via `agenda_tasks_repo` in `auth.toml`). Bare names (`books`) are rejected with a hint to use the qualified form. Cross-cutting design: shared wiki → `concepts/projects-meta-multi-owner`.
|
||||
|
||||
## Examples
|
||||
|
||||
### Read example: cross-project status
|
||||
|
||||
User: "что у меня на досках?"
|
||||
|
||||
```
|
||||
1. mcp__projects-meta__tasks_aggregate
|
||||
→ 7 projects, 12 active tasks
|
||||
|
||||
2. Group by project, summarize 1 line per active task.
|
||||
Cite project name; if a task is stale (cache age > 1h), flag it.
|
||||
```
|
||||
|
||||
### Read example: shared wiki query
|
||||
|
||||
User: "есть ли в общей вики что-то про setup-using паттерн?"
|
||||
|
||||
```
|
||||
1. mcp__projects-meta__knowledge_search
|
||||
query: "setup-using skill pair pattern"
|
||||
domain: "all"
|
||||
→ hits include concepts/setup-using-skill-pair
|
||||
|
||||
2. mcp__projects-meta__knowledge_get
|
||||
slug: "concepts/setup-using-skill-pair"
|
||||
→ full text
|
||||
|
||||
3. Summarize, link with markdown to the slug.
|
||||
```
|
||||
|
||||
### Mutation example: create cross-project task
|
||||
|
||||
User: "заведи в проекте books задачу на миграцию `settings.json`"
|
||||
|
||||
```
|
||||
1. mcp__projects-meta__tasks_create
|
||||
target_project: "victor/books"
|
||||
slug: "settings-json-migration"
|
||||
description: "<...>"
|
||||
next_action: "<...>"
|
||||
(no `confirm`)
|
||||
→ preview: proposed STATUS.md diff + commit message
|
||||
|
||||
2. Show preview to user.
|
||||
|
||||
3. User: "ok, go"
|
||||
|
||||
4. mcp__projects-meta__tasks_create
|
||||
(same args + confirm: true)
|
||||
→ committed to Gitea
|
||||
```
|
||||
|
||||
### Mutation example: closing a cross-project task
|
||||
|
||||
User: "close `[projects-meta-skills]` in skills"
|
||||
|
||||
```
|
||||
1. mcp__projects-meta__tasks_close
|
||||
target_project: "OpeItcLoc03/skills"
|
||||
slug: "projects-meta-skills"
|
||||
note: "<one-line summary>"
|
||||
(no `confirm`)
|
||||
→ preview
|
||||
|
||||
2. User confirms.
|
||||
|
||||
3. Re-call with confirm: true.
|
||||
```
|
||||
|
||||
## Common mistakes
|
||||
|
||||
| Mistake | Fix |
|
||||
|---|---|
|
||||
| Reading current project's tasks via `tasks_get` instead of disk | Read `.tasks/STATUS.md` directly. MCP is for *other* projects. |
|
||||
| Inlining `confirm: true` on the first mutation call | Always preview first; show user; only then `confirm: true`. |
|
||||
| Using `knowledge_search` for the project's own wiki | The shared wiki is a separate Gitea repo. Local `.wiki/` is in cwd. |
|
||||
| Acting on a stale `tasks_aggregate` without checking `meta_status` | Step 0 — Freshness gate is mandatory. If `cache_age_minutes` > 10 (or errors > 0), run `node ~/projects/.common/lib/projects-meta-mcp/dist/sync.js` first. |
|
||||
| Skipping `git -C ~/projects/projects-wiki pull` before `knowledge_ingest` / `knowledge_promote` | sha-based optimistic lock will reject the commit (422) and the failure is opaque. Pull is unconditional for shared-wiki writes — fast-forward is a no-op when current. |
|
||||
| Treating sync 401/403 as "MCP is fine, the page just doesn't exist yet" | 401/403 means the Gitea token is dead. Stop, tell the user to rotate `gitea_token` in `~/.config/projects-mcp/auth.toml`. Never guess on stale data. |
|
||||
| Calling `knowledge_ingest` with the wrong `type` | `type` must be one of `entities` / `concepts` / `packages` / `summaries` / `raw`. Mis-typed pages land in the wrong section and break `index.md`. |
|
||||
| Vague `knowledge_search` queries ("auth", "config") | Specific multi-word queries return targeted snippets; vague ones return noise. |
|
||||
| Forgetting `domain="all"` when searching across families | Default `domain` is auto-detected from cwd; use `"all"` if the wiki page lives in a different family. |
|
||||
| Passing bare project name (`target_project: "books"`) to mutation tools | v2.x rejects bare names. Use qualified `<owner>/<repo>` (e.g. `victor/books`, `OpeItcLoc03/skills`). Literal `agenda` is the only exception (cross-project meta-board). |
|
||||
|
||||
## Red flags
|
||||
|
||||
- "I'll just commit it directly" → no. Mutation tools have a preview step for a reason — silent writes to another repo are a recipe for drift.
|
||||
- "The cache is fresh enough" → run Step 0. The 10-minute window is the threshold; below it skip sync, above it sync. Don't eyeball this — the bus moves fast in cross-machine sessions.
|
||||
- "I'll skip the pull, my last write was 30 seconds ago" → another machine pushed in between. Always pull before shared-wiki writes; the sha-lock check is your only safety net.
|
||||
- "I'll skip the wiki page" → if you're answering a cross-cutting question and there's no wiki page, that's a `knowledge_ingest` candidate. Surface it to the user.
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: writing-skills
|
||||
adapted-from: obra/superpowers @ 6.2.0 (MIT) — TDD-for-skills core; ideya 8 self-skill-authoring (workshop record)
|
||||
version: 0.1.1
|
||||
version: 0.2.0
|
||||
description: >
|
||||
Authoring agent skills TDD-style — RED-GREEN-REFACTOR applied to SKILL.md
|
||||
documents. Use when creating a new skill, editing an existing one, or
|
||||
@@ -89,9 +89,21 @@ content for hypothetical cases.
|
||||
### Skill structure (our catalog conventions)
|
||||
|
||||
```
|
||||
skills/<name>/SKILL.md
|
||||
skills/<name>/SKILL.md # общий каталог (catalog, версионируется, ставится install.sh)
|
||||
```
|
||||
|
||||
**Куда класть скил — определи ДО написания (три варианта, не путать):**
|
||||
|
||||
| Тип скила | Место | Когда |
|
||||
|---|---|---|
|
||||
| Общий каталог | `~/projects/skills/skills/<name>/SKILL.md` + deploy (lint/build/install) | полезен всем проектам/агентам |
|
||||
| **Проектный (зона проекта)** | `<project>/.agents/skills/<name>/SKILL.md` (pi грузит из cwd; Claude Code: `.claude/skills/`) | привязан к одному проекту/роли (пример: `.admin` ops-скилы, `books/.agents/skills/`) |
|
||||
| ~~Глобальная установка руками~~ | ~~`~/.agents/skills/`~~ | **НЕ сюда** — это установочная копия (перезапишется update-skills), не место разработки |
|
||||
|
||||
Правило: если скил про зону одного проекта (admin, books, …) — проектный путь
|
||||
(`.agents/skills/` в cwd проекта), НЕ общий каталог и НЕ `~/.agents/skills/`.
|
||||
Если скил общий — каталог `skills/` + deploy-цикл. При сомнении — спросить.
|
||||
|
||||
Frontmatter (YAML):
|
||||
|
||||
- `name` — letters, numbers, hyphens only. Verb-first, active voice:
|
||||
@@ -158,6 +170,8 @@ discipline skills.
|
||||
- [ ] Description = when to use only, no workflow summary
|
||||
- [ ] Frontmatter: name (verb-first, hyphens), description (triggers), version
|
||||
(bumped), provenance (author/adapted-from with real pin)
|
||||
- [ ] Placement decided: project skill → `<project>/.agents/skills/` (cwd), NOT
|
||||
`~/.agents/skills/`; catalog skill → `skills/<name>/` + deploy cycle
|
||||
- [ ] Lint passes (catalog: `scripts/lint-skills.py`), dist rebuilt
|
||||
(`scripts/build.sh`), installed (`scripts/install.sh <name>`)
|
||||
- [ ] README provenance table updated (catalog)
|
||||
|
||||
Reference in New Issue
Block a user