The prior #708 slice added assess_connected_namespace_attachment() as a pure
decision function with no call site: nothing invoked it, so a session whose
role namespaces were Connected at the host but absent from the active session
tool surface still passed every mutation gate. Detection existed on paper only.
This makes it load-bearing.
Decision layer (mcp_namespace_health.py)
- assess_connected_namespace_attachment() gains secret-free telemetry
(connected/attached/required/missing counts, discovery cache hit and age,
reconnect_required, auto_attach_attempted, auto_recovered, error_type) and
reports reconnect_required, auto_recovered and startup_ordering_race.
- Startup ordering: a namespace whose connect completed after the session tool
snapshot cannot be in that snapshot, so parallel multi-role startup is
identified as its own race with the affected namespaces listed.
- attachment_gate_from_session() is a fail-closed gate keyed by
ATTACHMENT_GATED_TASKS. An unassessed namespace does not gate, matching #543
semantics, so this never fabricates a block.
- SANCTIONED_ATTACH_RECOVERY_TOOL names gitea_request_mcp_reconnect (#678) as
the only recovery.
Server wiring (gitea_mcp_server.py)
- New tool gitea_assess_mcp_namespace_attachment classifies the condition and
records a per-namespace verdict in _LIVE_NAMESPACE_ATTACHMENT.
- gitea_submit_pr_review and gitea_merge_pr now consult
_namespace_attachment_gate() alongside the existing #543 health gate, so both
fail closed while a required namespace is unattached.
- Watchdog check-in emits status only, never namespace contents.
The typed condition mcp_connected_namespaces_missing stays distinct from config
drift (#672), transport-closed (#584) and resolver EOF (#685). Recovery never
routes through direct imports, CLI or raw API mutation, profile hopping,
session-state overrides, or process kills.
Docs
- docs/mcp-namespace-health.md documents the tool arguments, startup ordering,
the fail-closed gate, and the telemetry contract.
- skills/llm-project-workflow/SKILL.md states Connected is not attached, and
that preflight proof is live tool visibility plus gitea_whoami on the role
namespace rather than host status alone.
- docs/mcp-tool-inventory.md lists the new tool.
- docs/remote-mcp/threat-model-anchors.json and threat-model.md: 21 #956 anchors
restamped for the line shift these additions caused in gitea_mcp_server.py.
Every anchor was re-derived from its recorded expect substring; none guessed.
Tests
- tests/test_issue_708_attachment_wiring.py (19 cases): typed detection, proof
mapping, reconnect-only next action, auto-attach success and failure,
reconnect rediscovery, multi-role startup ordering, telemetry including a
no-secret-leak assertion, fail-closed gate per role, unassessed and unmapped
tasks not gating, partial attachment gating only the affected role, and no
healthy verdict without attachment proof.
Verification
- tests/test_issue_708_attachment_wiring.py + test_issue_708_mcp_namespace_attachment.py: 24 passed
- namespace/session/runtime/review sweep: 427 passed, 12 subtests
- full suite head: 31 failed, 5885 passed, 6 skipped, 1047 subtests
- full suite base 17ba1ff035: 30 failed, 5862 passed, 6 skipped, 1047 subtests
- failing identifier sets match, plus tests/test_mirror_refs.py DryRunBanner,
which fails on the unmodified base in isolation and passes here: flaky, not a
regression from this branch.
- Gate proven by execution, not inspection: registering the assessment blocks
merge_pr and review_pr, and attaching the namespaces clears the block.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
358 lines
19 KiB
Markdown
358 lines
19 KiB
Markdown
---
|
|
name: llm-project-workflow
|
|
description: >-
|
|
Router skill for safe LLM project work: identify task mode, load the matching
|
|
canonical workflow file, enforce mode isolation, and emit the correct final
|
|
report schema. Use at the start of any implementation, review, merge,
|
|
reconciliation, or issue-filing task.
|
|
---
|
|
> **Also known as:** `gitea-workflow`, `git-pr-workflows` (canonical multi-runtime names — see docs/workflow-skill-mount.md / #551).
|
|
|
|
|
|
# LLM Project Workflow Skill
|
|
|
|
This skill is a **router**. Do not perform project work from this file alone.
|
|
|
|
Before any project mutation, identify the task mode and load the matching
|
|
workflow file.
|
|
|
|
## Workflow modes
|
|
|
|
| Task mode | Workflow | Final report schema |
|
|
|-----------|----------|---------------------|
|
|
| PR review / approval / merge | [`workflows/review-merge-pr.md`](workflows/review-merge-pr.md) | [`schemas/review-merge-final-report.md`](schemas/review-merge-final-report.md) |
|
|
| Reconcile already-landed open PRs | [`workflows/reconcile-landed-pr.md`](workflows/reconcile-landed-pr.md) | [`schemas/reconcile-landed-final-report.md`](schemas/reconcile-landed-final-report.md) |
|
|
| Create or update Gitea issues | [`workflows/create-issue.md`](workflows/create-issue.md) | [`schemas/create-issue-final-report.md`](schemas/create-issue-final-report.md) |
|
|
| Work on an assigned issue / author code | [`workflows/work-issue.md`](workflows/work-issue.md) | [`schemas/work-issue-final-report.md`](schemas/work-issue-final-report.md) |
|
|
| PR-only queue cleanup (one canonical review per PR) | [`workflows/pr-queue-cleanup.md`](workflows/pr-queue-cleanup.md) | [`schemas/pr-queue-cleanup-final-report.md`](schemas/pr-queue-cleanup-final-report.md) |
|
|
|
|
## Universal rules
|
|
|
|
- Prove identity, active profile, runtime context, and **exact** capability before
|
|
mutation.
|
|
- A nearby capability does not count.
|
|
- Do not self-review or self-merge.
|
|
- **Never push a stable branch directly.** Worker sessions (author/reviewer/merger)
|
|
must never run `git push <remote> master` — or `main`/`dev`/other stable refs,
|
|
including refspecs (`HEAD:master`), `--force`, `--delete`, or `--dry-run` no-op
|
|
probes — and must never commit on the root/control checkout outside an issue
|
|
feature branch. Stable-branch updates land ONLY through sanctioned Gitea merge
|
|
tooling (`gitea_merge_pr`) or an explicitly authorized reconciler path. A
|
|
detected attempt marks the session workflow-contaminated (#671) and fails
|
|
closed on review/merge/close/completion until a reconciler audits and clears
|
|
it. `git fetch` / `git pull --ff-only` and feature-branch pushes stay allowed.
|
|
- Do not mix modes in one run.
|
|
- **BLOCKED + DIAGNOSE default rule (required):** If any required workflow step, skill, tool, capability, preflight, instruction, profile, worktree binding, or terminal/MCP operation cannot be performed or loaded (including the canonical ones listed in this skill and its loaded workflow), immediately enter `BLOCKED + DIAGNOSE`. Stop before any git or Gitea mutation. Diagnose using the standard template in [`templates/blocked-diagnose-report.md`](templates/blocked-diagnose-report.md). Attempt *only* safe non-mutating recovery. Report using the template. Do not continue, use fallbacks, or treat the missing requirement as harmless.
|
|
- If the required workflow cannot be loaded, stop and produce a recovery handoff
|
|
only (see BLOCKED + DIAGNOSE rule above).
|
|
- Final report must use the schema for the loaded workflow.
|
|
- If a task requires a different mode, stop and produce a handoff for the
|
|
correct workflow.
|
|
|
|
## Covered blocker classes (BLOCKED + DIAGNOSE must trigger for these)
|
|
|
|
- missing required skill or workflow guide (e.g. gitea-workflow, llm-project-workflow)
|
|
- broken terminal/tool runner or shell spawn failure
|
|
- MCP capability failure, reset, or deadlock (e.g. preflight state cleared)
|
|
- wrong profile or role for the requested operation
|
|
- dirty or misbound worktree (root checkout or non-branches/ path)
|
|
- root checkout mutation risk
|
|
- stable-branch push contamination (#671): a direct `git push <remote> master`
|
|
(or `main`/`dev`) equivalent, or a root/control-checkout commit not on an issue
|
|
feature branch, marks the session workflow-contaminated; review/merge/close/
|
|
completion mutations then fail closed until a reconciler audits and clears it
|
|
(`gitea_audit_stable_branch_contamination action=clear`, reconciler-only)
|
|
- mutation guard failure (e.g. branches-only guard)
|
|
- missing required MCP tool/schema or operation
|
|
- stale or inconsistent runtime state (e.g. lease vs actual, dirty state disagreement)
|
|
- unavailable project instructions or checked-in guides
|
|
- any other failure of a step the current workflow or controller prompt declares "required"
|
|
|
|
**Prohibited unless controller authorizes in writing for this instance:** temp scripts, direct API fallback, MCP internals, direct imports, in-memory state restoration, manual bypasses, or any continuation that hides the blocker. All such cases must be reported as process/tooling defects.
|
|
|
|
## Mode isolation
|
|
|
|
A run that starts in `review-merge-pr` mode may not create process issues,
|
|
implement fixes, or edit source files.
|
|
|
|
A run that starts in `reconcile-landed-pr` mode may not approve, request
|
|
changes, merge, implement fixes, or create normal issues.
|
|
|
|
A run that starts in `create-issue` mode may not review, approve, request
|
|
changes, merge, implement fixes, create branches, commit, push, or create PRs.
|
|
|
|
A run that starts in `work-issue` mode may not review, approve, request changes,
|
|
merge, close PRs, or act as reviewer.
|
|
|
|
A run that starts in `pr-queue-cleanup` mode may not claim issues, create
|
|
branches, edit implementation files, file new issues, or review a second PR
|
|
after any terminal review mutation.
|
|
|
|
If the task requires a different mode, stop and produce a handoff for the
|
|
correct workflow.
|
|
|
|
---
|
|
|
|
## Definitions
|
|
|
|
- **Merged**: Gitea PR metadata says `merged=true`.
|
|
- **Landed**: Equivalent content is present on remote `master`, but PR metadata
|
|
may not say merged.
|
|
- **Closed-not-merged**: PR state is closed and `merged=false`.
|
|
- **Reconciled**: Verified whether closed-not-merged or already-landed content
|
|
is present on the target branch; issue/label/tracker state repaired.
|
|
|
|
## Work Selection Rule for LLMs
|
|
|
|
Before starting any issue or PR work, acquire or verify a work lease. Do not
|
|
begin coding, reviewing, fixing, branching, committing, pushing, commenting, or
|
|
creating a PR until you prove the target is not already being worked.
|
|
|
|
Required checks:
|
|
|
|
1. List open PRs.
|
|
2. Search for PRs linked to the target issue.
|
|
3. Search local and remote branches for the issue number.
|
|
4. Search registered worktrees for the issue branch.
|
|
5. Check dirty worktrees.
|
|
6. Check active leases or recent handoffs.
|
|
7. Check whether the issue was already completed by a merged PR.
|
|
|
|
If another active session owns the lease, stop with "work already claimed" or
|
|
produce a handoff.
|
|
|
|
For Gitea-Tools: `gitea_lock_issue` is the fail-closed lease gate before author
|
|
mutations; `status:in-progress` and claim comments are supporting lease signals.
|
|
|
|
## Global LLM Worktree Rule
|
|
|
|
The main project checkout is a stable control checkout on `master`, `main`, or
|
|
`dev`. All LLM task work must happen inside the project's `branches/` directory.
|
|
|
|
If `cwd` is not inside `branches/`, stop before any file edit, test write,
|
|
commit, merge, rebase, or cleanup. The main checkout is orchestration-only.
|
|
|
|
## Stable Branch Push Protection (#671)
|
|
|
|
Worker sessions must **never** publish a stable branch directly. This is the
|
|
prevention hardening for the #670 incident (a bare direct-to-master commit
|
|
`2fa97c26` and a PR #654 merger `git push prgs master` attempt).
|
|
|
|
**Forbidden for author/reviewer/merger sessions:**
|
|
|
|
- `git push <remote> master` (and `main`, `dev`, `develop`, `development`),
|
|
including refspecs (`HEAD:master`, `+refs/heads/x:refs/heads/master`),
|
|
`--force`, `--delete` / `:master`, and `--dry-run`/`-n` no-op probes (a
|
|
dry-run still proves intent and contaminates the session).
|
|
- Local commits on the root/control checkout that are not carried by an issue
|
|
feature branch under `branches/`.
|
|
|
|
**Allowed (never blocked):**
|
|
|
|
- `git fetch` and `git pull --ff-only` of master into the control checkout when
|
|
authorized for sync.
|
|
- Feature-branch pushes to non-stable refs (`git push <remote> fix/issue-N-...`).
|
|
- Sanctioned merges via `gitea_merge_pr` / the Gitea API merge endpoint — the
|
|
**only** way stable branches advance.
|
|
|
|
**What happens on a detected attempt:** the session is marked
|
|
workflow-contaminated (durable `stable_branch_contamination` marker, redacted
|
|
command summary + session id + remote + ref). While contaminated, all
|
|
review / merge / close / issue-completion mutations fail closed. `comment_issue`
|
|
and `lock_issue` remain allowed so the contaminated worker can post the durable
|
|
audit comment and hand off. Contamination **cannot be self-cleared** — only a
|
|
reconciler audit (`gitea_audit_stable_branch_contamination action=clear`) may
|
|
clear it.
|
|
|
|
Tooling: call `gitea_record_stable_branch_push_attempt` to classify/record a
|
|
proposed push before running it; `gitea_audit_stable_branch_contamination` to
|
|
inspect or (reconciler-only) clear the marker.
|
|
|
|
## Runtime Recovery Protection (#630)
|
|
|
|
MCP connectivity is recovered through **sanctioned reconnect/restart only**:
|
|
host auto-reconnect, an explicit client reconnect, an IDE/client relaunch, or an
|
|
operator-owned restart. Worker sessions must never kill the daemons their own
|
|
proof depends on.
|
|
|
|
**Forbidden for author/reviewer/merger sessions:**
|
|
|
|
- `pkill -f mcp_server.py`, `pkill -f gitea_mcp_server`, broad `pkill -f mcp`.
|
|
- `killall` of a daemon, or `kill <pid>` of an MCP daemon pid.
|
|
- Any pattern broad enough to sweep unrelated namespaces (`pkill -f python`),
|
|
even when it never names MCP.
|
|
|
|
**Allowed (never blocked):** read-only inspection (`ps aux | grep mcp_server`),
|
|
and process management unrelated to the daemons — a `kill` of some other pid is
|
|
reported as *ambiguous*, not as contamination.
|
|
|
|
**What happens on a detected attempt:** the session is marked
|
|
workflow-contaminated (durable marker, redacted command summary + session id +
|
|
remote + role). While contaminated, all review / merge / close / completion
|
|
mutations fail closed. `comment_issue` and `lock_issue` remain allowed so the
|
|
contaminated worker can post the durable audit comment and hand off.
|
|
Contamination **cannot be self-cleared** — only a reconciler audit may clear it,
|
|
and it does not expire with the session-state TTL. The final report must surface
|
|
the contaminated recovery and must not claim a clean session.
|
|
|
|
Operator-authorized host maintenance stays permitted, but the authorization is
|
|
read from the operator's environment, never from a tool argument: a session must
|
|
not be able to authorize itself.
|
|
|
|
Tooling: call `gitea_record_daemon_process_kill_attempt` to classify/record a
|
|
proposed command before running it; `gitea_audit_runtime_recovery_contamination`
|
|
to inspect or (reconciler-only) clear the marker. Full contrast in
|
|
`docs/mcp-namespace-eof-recovery.md`.
|
|
|
|
## Connected is not attached (#708)
|
|
|
|
A host/CLI MCP inventory showing **Connected** is not proof the tools are usable.
|
|
The active session can expose **none** of a Connected server's tool namespaces —
|
|
a *session attachment* failure, distinct from config drift (#672),
|
|
transport-closed (#584), and resolver EOF (#685).
|
|
|
|
Required preflight proof is **live tool visibility plus `gitea_whoami` on the role
|
|
namespace**, never host Connected status alone. Call
|
|
`gitea_assess_mcp_namespace_attachment` with the Connected set and the namespaces
|
|
actually attached to the session; it returns the typed condition
|
|
**mcp_connected_namespaces_missing** with per-namespace connected-vs-attached proof,
|
|
and records the verdict so review and merge fail closed while a required namespace
|
|
is unattached.
|
|
|
|
Only sanctioned recovery: the client attach/reconnect path
|
|
(`gitea_request_mcp_reconnect`), then full preflight
|
|
(`gitea_whoami` → `gitea_resolve_task_capability` → task). Never recover by direct
|
|
module import, CLI/raw API mutation, profile hopping, session-state overrides,
|
|
process kills, or `.env`/mtime edits. A final report must not claim a healthy
|
|
session without attachment proof.
|
|
|
|
## Shell Spawn Hard-Stop Rule
|
|
|
|
`exit_code: -1` with empty stdout/stderr means the shell failed to spawn — not a
|
|
command failure. After two consecutive spawn failures, hard-stop shell use for
|
|
the session and emit a recovery report (#258).
|
|
|
|
## Isolated worktree naming
|
|
|
|
Implementation: `(fix|feat|docs|chore)/issue-<number>-<short-description>`
|
|
|
|
Review: `review/pr-<number>-<short-description>`
|
|
|
|
## Subagent Tool-Budget Guardrails
|
|
|
|
General-purpose subagents on **single-step MCP tasks** (for example
|
|
`gitea_commit_files`) must not expand into 100+ tool-call retry spirals with
|
|
WebFetch/Playwright/manual-encoding fallbacks (issue #259).
|
|
|
|
Default budgets (stop when exceeded):
|
|
|
|
- **Single-step MCP mutation** (`commit_files`, `create_pr`, `lock_issue`):
|
|
15 tool calls, 5 minutes wall time.
|
|
- **Review / merge queue inspection**: 40 tool calls, 15 minutes.
|
|
- **Non-mutating exploration**: 60 tool calls, 20 minutes.
|
|
|
|
Rules:
|
|
|
|
1. When the main session has `gitea.repo.commit`, call `gitea_commit_files`
|
|
directly — do not delegate commit to a subagent (#260).
|
|
2. After shell spawn failure (#258), attempt the native MCP tool once before
|
|
any fallback; shell unavailability never authorizes WebFetch/Playwright/
|
|
manual base64.
|
|
3. Never resume a failed subagent into a larger retry loop or spawn a second
|
|
subagent for the same deterministic step — stop and report.
|
|
4. When `gitea_commit_files` is available, forbid WebFetch, Playwright,
|
|
manual encoding, and ad-hoc `_encode_*` / `_emit_*` helpers in the repo.
|
|
|
|
Worktree folder: branch with `/` replaced by `-` under `branches/`.
|
|
|
|
Helpers: `scripts/worktree-start`, `scripts/worktree-review`,
|
|
`scripts/worktree-clean`.
|
|
|
|
## Identity and profile safety
|
|
|
|
- Author and reviewer identities must be distinct.
|
|
- Never place raw tokens in LLM/MCP config.
|
|
- Use `gitea_whoami` and `gitea_resolve_task_capability` before mutating.
|
|
|
|
## Tool inventory
|
|
|
|
[`docs/mcp-tool-inventory.md`](../../docs/mcp-tool-inventory.md) is the canonical
|
|
list of registered tools, held equal to the live registry by a test. A tool that
|
|
is not listed there does not exist — do not scope work around it (#781).
|
|
|
|
Issue content is edited with `gitea_edit_issue` (title/body only, read-after-write
|
|
verified). `gitea_edit_pr` is pull-request-only and never accepts an issue number.
|
|
|
|
## Controller Handoff
|
|
|
|
Every task must end with a section titled exactly `Controller Handoff`. Compact
|
|
format canonical field set per issue #182; mode-specific schemas in
|
|
`schemas/*-final-report.md` define required fields. Use the final report schema
|
|
for the loaded workflow mode — not the legacy compact block alone.
|
|
`review_proofs.assess_controller_handoff()` validates presence.
|
|
|
|
## Canonical self-propagating handoff
|
|
|
|
Every workflow mode also carries the cross-role handoff block defined in
|
|
[`schemas/self-propagating-handoff.md`](schemas/self-propagating-handoff.md)
|
|
(#626). Each actor consumes exactly one canonical handoff, performs exactly one
|
|
authorized role, posts the result to the Gitea issue or PR thread, and emits the
|
|
next complete handoff — until the controller records final closure. The block
|
|
must be posted to Gitea, not returned in chat alone, and the next prompt is not
|
|
an optional prose section. `self_propagating_handoff.py` implements the schema;
|
|
`final_report_validator.py` enforces it as `shared.self_propagating_handoff`.
|
|
|
|
## Prompt templates
|
|
|
|
Ready-to-copy task prompts live in [`templates/`](templates/):
|
|
|
|
- [`start-issue.md`](templates/start-issue.md) — author work (loads `work-issue.md`)
|
|
- [`review-pr.md`](templates/review-pr.md) — review (loads `review-merge-pr.md`)
|
|
- [`pr-queue-cleanup.md`](templates/pr-queue-cleanup.md) — one PR per cleanup run
|
|
- [`merge-pr.md`](templates/merge-pr.md) — merge (loads `review-merge-pr.md`)
|
|
- [`recover-bad-state.md`](templates/recover-bad-state.md)
|
|
- [`reconcile-closed-not-merged-pr.md`](templates/reconcile-closed-not-merged-pr.md)
|
|
- [`worktree-cleanup.md`](templates/worktree-cleanup.md)
|
|
- [`release-tag.md`](templates/release-tag.md)
|
|
- [`canonical-state-comments.md`](templates/canonical-state-comments.md)
|
|
|
|
## Adapting to a project
|
|
|
|
| Placeholder | Example here |
|
|
|-------------|--------------|
|
|
| `<remote>` | `prgs` |
|
|
| default branch | `master` |
|
|
| profile env vars | `GITEA_MCP_CONFIG`, `GITEA_MCP_PROFILE` |
|
|
| `branches/` | `branches/` |
|
|
| helpers | `scripts/worktree-start` / `-review` / `-clean` |
|
|
|
|
## Versioning And Tagging
|
|
|
|
Releases follow SemVer from remote `master` only, after full test suite passes.
|
|
See [`templates/release-tag.md`](templates/release-tag.md) and
|
|
`scripts/release-tag`.
|
|
|
|
## Proof: missing required workflow steps stop before mutation
|
|
|
|
- The llm-project-workflow router (this file) and every loaded workflow (work-issue.md, review-merge-pr.md, create-issue.md, etc.) now declare at the top: if required step/skill/tool/capability/instruction/profile/worktree binding/preflight fails, STOP, state BLOCKED, use blocked-diagnose-report.md template, only non-mutating recovery.
|
|
- Controller prompts (start-issue.md, review-pr.md, merge-pr.md, recover-bad-state.md, etc.) and the runbooks (docs/llm-workflow-runbooks.md) explicitly require the same and prohibit unsafe fallbacks.
|
|
- MCP guards (branches-only mutation guard #274, worktree binding #510, preflight purity, role checks, lease gates, gitea_lock_issue, etc.) plus the "prove before mutation" rules ensure that a BLOCKED state prevents git/Gitea mutations.
|
|
- When a skill/guide/tool is missing (e.g. gitea-workflow not mounted for a runtime), the load step in the router/prompt fails the "required" check → BLOCKED + report before any gitea_* call or git command that mutates.
|
|
- Terminal/shell failures, capability deadlocks, wrong profile, dirty/misbound worktree, root risk, guard failures, missing schema, stale state, unavailable instructions all map to the covered blocker classes and trigger the same stop + report.
|
|
- No code path in the canonical workflows allows continuation past a declared required step without the BLOCKED report.
|
|
- See also: Global LLM Worktree Rule, Shell Spawn Hard-Stop Rule, Identity and profile safety, Subagent Tool-Budget Guardrails, and the explicit prohibition list in Universal rules.
|
|
|
|
Tests / proof docs updated in this change + runbooks. Full relevant test runs (see PR handoff) pass; `git diff --check` clean. Missing-step cases are now documented to fail closed before mutation.
|
|
|
|
## Bootstrap Review Path (#557)
|
|
|
|
Self-hosted MCP workflow fixes can deadlock live review daemons. Do not bypass
|
|
gates with raw API, direct imports, or root checkout edits.
|
|
|
|
If and only if a controller posts a durable `BOOTSTRAP REVIEW AUTHORIZATION
|
|
(#557)` record, follow:
|
|
|
|
`docs/bootstrap-review-path.md`
|
|
|
|
Otherwise stop with BLOCKED + DIAGNOSE. Bootstrap never weakens normal PR gates.
|