feat(using-projects-meta): v1.1.0 — mandatory Step 0 freshness gate
Cross-machine workflows broke silently when the agent reasoned on a stale
MCP cache, or hit sha-based optimistic-lock 422s on shared-wiki writes
because another host had pushed minutes earlier. Codify the missing
pre-flight as Step 0:
1. Always probe mcp__projects-meta__meta_status first.
2. If cache_age_minutes > 10 OR errors_count > 0 → run
`node ~/.local/projects-meta-mcp/dist/sync.js` before any read.
3. For shared-wiki WRITES (knowledge_ingest, knowledge_promote) →
unconditional `git -C ~/projects/projects-wiki pull --ff-only`
(sha-lock will reject the commit otherwise; failure is opaque).
4. Tasks-mutations: sync via dist/sync.js is enough (no local clone
of target tasks repo).
5. Sync 401/403 → STOP, send user to ~/.config/projects-mcp/auth.toml
to rotate gitea_token. No silent retry.
10-minute cache window chosen as the read threshold — balances
multi-machine drift detection against unnecessary Gitea round-trips.
Common mistakes + Red flags expanded to call out the new failure modes.
README mirrors the rule for human readers. Wiki concept page
projects-meta-skills.md gets a "Freshness gate (v1.1.0)" subsection.
Closes [using-projects-meta-freshness-gate].
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Task Board
|
||||
_Updated: 2026-04-29_
|
||||
_Updated: 2026-04-30_
|
||||
|
||||
<!--
|
||||
Canonical layout. One block per task. Per-task deep context lives in
|
||||
@@ -85,6 +85,15 @@ _Updated: 2026-04-29_
|
||||
**Next action:** when triggered (skill count crosses threshold), evaluate variant B (grouped by family) vs variant C (flat + manifest) from the original brainstorm in `.wiki/concepts/repo-layout.md`
|
||||
**Branch:** (not started)
|
||||
|
||||
## 🟢 [using-projects-meta-freshness-gate] — добавить Step 0 freshness check в using-projects-meta
|
||||
|
||||
**Status:** done
|
||||
**Where I stopped:** Step 0 добавлен в `using-projects-meta/SKILL.md` (cache_age>10мин или errors>0 → `dist/sync.js` для read; shared-wiki write — безусловный `git pull --ff-only`; 401/403 → auth.toml rotation); зеркалирован в README; bump 1.0.0→1.1.0; rebuild+install OK; description trimmed до 924 chars после регрессии (полное Step 0 в description ~1450 chars сломало парсинг — harness зафолбэкнулся на H1, скилл потерял все триггеры); Common mistakes + Red flags расширены; concept page + log обновлены; new feedback memory `skill_description_length_limit`
|
||||
**Next action:** (none — kept until merged)
|
||||
**Branch:** master
|
||||
|
||||
---
|
||||
|
||||
## 🟢 [projects-meta-skills] — setup-projects-meta + using-projects-meta — пара скиллов для projects-meta-mcp
|
||||
|
||||
**Status:** done
|
||||
|
||||
34
.tasks/using-projects-meta-freshness-gate.md
Normal file
34
.tasks/using-projects-meta-freshness-gate.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# using-projects-meta-freshness-gate
|
||||
|
||||
## Goal
|
||||
Add an explicit Step 0 "Freshness gate" to `using-projects-meta` so the agent does not hit a stale MCP cache or a sha-based optimistic-lock conflict in cross-machine workflows. Pre-flight: always `meta_status`; if `cache_age` > 10 min or `errors_count` > 0, run `node ~/.local/projects-meta-mcp/dist/sync.js` for reads; for shared-wiki writes (`knowledge_ingest`, `knowledge_promote`) additionally `git -C ~/projects/projects-wiki pull --ff-only`. 401/403 from sync → token expired → send user to `~/.config/projects-mcp/auth.toml`. Tasks-mutations need only the sync (no local clone of target tasks repo). Bump skill 1.0.0 → 1.1.0 (additive, non-breaking).
|
||||
|
||||
## Key files
|
||||
- `skills/using-projects-meta/SKILL.md` — add **Step 0 — Freshness gate** section before "Read (no confirmation needed)" and before "Mutate (always two-step)"; tweak Workflow ordering
|
||||
- `skills/using-projects-meta/README.md` — mirror the rule in a new "Freshness gate" subsection of "Two operation classes"
|
||||
- `.wiki/concepts/projects-meta-skills.md` — append a "Freshness gate (v1.1.0)" subsection
|
||||
- `.wiki/log.md` — one entry
|
||||
|
||||
## Decisions log
|
||||
- 2026-04-30: Threshold = 10 min cache age. Reason: balance between catching cross-machine pushes and not hitting Gitea on every casual `tasks_aggregate`. User-supplied number, codified.
|
||||
- 2026-04-30: For shared-wiki writes, pull is **unconditional** (not gated by cache age). Reason: sha-based optimistic lock in `knowledge_ingest` will reject the commit otherwise with a 422, and the failure mode is opaque ("why did it fail?"). The pull is cheap (fast-forward is a no-op when already current).
|
||||
- 2026-04-30: For tasks-mutations, sync via `dist/sync.js` is enough. Reason: there's no local clone of the target tasks repo — mutations go straight through Gitea API. The sync only refreshes the local view of all repos' STATUS.md so the agent reasons from current state when writing.
|
||||
- 2026-04-30: 401/403 from sync → send user to rotate `gitea_token` in `~/.config/projects-mcp/auth.toml`. Reason: silently failing or pretending sync succeeded leaves the agent reasoning on stale data. Loud failure is the whole point of the check.
|
||||
- 2026-04-30: Version bump 1.0.0 → 1.1.0 (additive behavior change — adds a pre-flight step, doesn't break existing usage). `using-context7` precedent: `version` in frontmatter, semver discipline.
|
||||
|
||||
## Open questions
|
||||
- [ ] Should Step 0 apply to `meta_status` itself? Decision: no — that would be circular. `meta_status` is the freshness probe, not a read that depends on freshness.
|
||||
|
||||
## Completed steps
|
||||
- [x] Open task in STATUS.md as 🔴 active
|
||||
- [x] Add Step 0 — Freshness gate section to `using-projects-meta/SKILL.md` (between Workflow header and Read sub-section); add Step 0 reference to Read and Mutate sub-flows; extend Common mistakes (3 new rows: stale aggregate, skipped pull, sync 401/403); extend Red flags (2 new bullets)
|
||||
- [x] Mirror rule in `using-projects-meta/README.md` as new "Step 0 — Freshness gate (v1.1.0, mandatory pre-flight)" section
|
||||
- [x] Bump `version: 1.0.0` → `1.1.0` in SKILL.md frontmatter
|
||||
- [x] First rebuild + reinstall — caused regression: full Step 0 inlined into description took it to ~1450 chars, exceeded harness limit, description silently dropped → listing showed `using-projects-meta: Using the projects-meta MCP server` (H1 fallback); fixed by trimming description to 924 chars with one-sentence pointer "v1.1.0 mandates a Step 0 freshness gate ... see SKILL body"
|
||||
- [x] Saved feedback memory `skill_description_length_limit.md` so future me checks length after frontmatter edits
|
||||
- [x] Wiki: appended "Freshness gate (v1.1.0)" subsection to `concepts/projects-meta-skills.md`; one entry in `log.md`
|
||||
- [x] Closed in STATUS.md (🟢 done); next: commit + push + MCP round-trip close
|
||||
|
||||
## Notes
|
||||
- User-supplied draft text for the skill (verbatim): _"Step 0 — Freshness gate. Always call mcp__projects-meta__meta_status first. If cache_age_minutes > 10 or errors.length > 0, run node ~/.local/projects-meta-mcp/dist/sync.js before any read. For shared-wiki writes (knowledge_ingest, knowledge_promote), additionally run git -C ~/projects/projects-wiki pull --ff-only — sha-based optimistic lock will reject your commit otherwise."_
|
||||
- Trigger reason from user: "projects-meta — это шина между машинами. Юзер на ноуте может не знать, что на десктопе уже был коммит в shared wiki 5 минут назад. Без этого шага write-флоу ломается случайным образом, и баг трудно воспроизвести."
|
||||
@@ -67,6 +67,20 @@ Both `setup-projects-meta` and `using-projects-meta` now write the canon path. `
|
||||
|
||||
This is a meta-lesson: **always pull shared/multi-machine resources before relying on cached anchors**. Saved as a feedback memory.
|
||||
|
||||
## Freshness gate (v1.1.0)
|
||||
|
||||
`using-projects-meta` v1.1.0 adds a mandatory pre-flight **Step 0** before any `tasks_*` / `knowledge_*` call:
|
||||
|
||||
1. Probe `mcp__projects-meta__meta_status` (cache age + errors).
|
||||
2. If `cache_age_minutes > 10` or `errors_count > 0` → `node ~/.local/projects-meta-mcp/dist/sync.js`.
|
||||
3. For shared-wiki writes (`knowledge_ingest`, `knowledge_promote`) → **unconditional** `git -C ~/projects/projects-wiki pull --ff-only` (sha-based optimistic lock will reject otherwise with 422).
|
||||
4. For tasks-mutations (`tasks_create/update/close`) → sync via `dist/sync.js` is enough; no local clone of target tasks repo.
|
||||
5. 401/403 from sync → token dead → send user to `~/.config/projects-mcp/auth.toml`. Loud failure mandatory.
|
||||
|
||||
Why: `projects-meta` is a bus between machines. Another host can push minutes ago without the agent knowing. Without Step 0, reads return stale data and writes hit opaque 422s — exactly the failure mode we hit during this very session (skipped initial pull → wiki anchors stale → had to rewrite three places after retroactive `git pull`). Codified the lesson into the skill so future sessions don't repeat it.
|
||||
|
||||
10-minute threshold is the chosen balance: catches multi-machine drift, doesn't burn Gitea round-trips for casual back-to-back questions.
|
||||
|
||||
## Round-trip note
|
||||
|
||||
The task `[projects-meta-skills]` was created on a different machine (`OpeItcLoc03@DESKTOP-NSEF0UK`) via `mcp__projects-meta__tasks_create target_project=claude-skills`. Closing it on this machine via `mcp__projects-meta__tasks_close target_project=claude-skills slug=projects-meta-skills` round-trips back to the same Gitea board — both sides see the 🟢 done state. First end-to-end multi-machine task lifecycle in the fleet.
|
||||
|
||||
@@ -30,5 +30,6 @@ Parseable: `grep "^## \[" .wiki/log.md | tail -20`.
|
||||
## [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
|
||||
|
||||
BIN
dist/using-projects-meta.skill
vendored
BIN
dist/using-projects-meta.skill
vendored
Binary file not shown.
@@ -49,6 +49,32 @@ wiki content. See the table below.
|
||||
| "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 ~/.local/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)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: using-projects-meta
|
||||
version: 1.0.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. Mutation tools (`tasks_create`, `tasks_update`, `tasks_close`, `knowledge_ingest`, `knowledge_promote`) require two-step preview → confirm. Skip for the **current** project's tasks/wiki — those live on disk in `.tasks/` / `.wiki/` and are read directly.
|
||||
version: 1.1.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
|
||||
@@ -56,11 +56,49 @@ Use MCP only for **other** projects, **other** machines, or **shared** wiki cont
|
||||
- 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 ~/.local/projects-meta-mcp/dist/sync.js`
|
||||
(or `npm run sync` from ~/.local/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.
|
||||
@@ -69,6 +107,9 @@ Use MCP only for **other** projects, **other** machines, or **shared** wiki cont
|
||||
### 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" / "поехали".
|
||||
@@ -180,7 +221,9 @@ User: "close `[projects-meta-skills]` in claude-skills"
|
||||
| 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` | If `age_seconds` > 3600 the cache may be behind reality. Either run `node dist/sync.js` or warn the user. |
|
||||
| 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 ~/.local/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` / `sources` / `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. |
|
||||
@@ -188,5 +231,6 @@ User: "close `[projects-meta-skills]` in claude-skills"
|
||||
## 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" → check `meta_status.age_seconds`. If it's >3600s and the user is about to act on the data, sync first.
|
||||
- "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.
|
||||
|
||||
Reference in New Issue
Block a user