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:
@@ -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