# Project Manager (`agent-pm`) The PM is the user's assistant. It is **off the critical path**: removing PM does not break the workflow, it just makes delivery less optimized. PM exists to: - curate tailored context briefs for working agents so they don't burn tokens rediscovering the project - promote durable findings (cross-project patterns, lessons learned, reference notes) into the org-level Forgejo Wiki so knowledge survives outside any one repo PM - actually `fjx pm ...` tooling, not the agent itself - is **the sole interpreter of `/` syntax** in Forgejo comments. Working agents ([dev](./dev.md), [qa](./qa.md), builder) MUST NOT parse `/commands` as directives — those are reserved for PM, with the trust map as the single choke point for vetting. Natural-language directives in comments remain in scope for the working agents per [PR conventions](../primitives/pr.md). The PM maintains **one canonical ledger comment per issue**, which it **edits in place**. - Issue comments: incoming owner directives authored by humans - Ledger: record of directives PM has processed - Labels: applied by PM in service of authorized commands; agents also apply labels directly under their own protocols - Brief comments (``): tailored context for working agents, one per role **Constraints:** - No external database - Human-readable - Idempotent across runs - Safe to resume after interruption - Durable in issue history - Trust map enforces authorization on `/commands` - removing PM degrades to manual mode without breaking the workflow **Non-Goals:** - Full natural-language comment understanding - Tracking PM's internal scheduling or cron state - Replacing Developer/QA/Builder ledgers (see [issue conventions](../primitives/issue.md)) - Acting as a controller or invoker of working agents - Gating, policing, or accountability for working-agent activity - Tracking agent state in the PM ledger (Forgejo timeline + agent ledgers are the record) ## Manual Mode PM is off the critical path. Without PM, the user can: - apply labels directly in the Forgejo UI - write briefs by hand using the `` marker (working agents read briefs by marker, regardless of author) - skip briefs entirely; working agents fall back to reading the issue body and labels - drop fully into a Claude Code shell and run the workflow by hand against AGENTS.md and the workflow docs When `pm next` resumes after downtime, it processes only comments after the checkpoint and does not unwind manual label changes or briefs. ## Canonical Homes | Artifact | Lives on | Quantity | | ------------ | -------- | -------------------------------------------------------------- | | Agent Brief | Issue | One per role (`dev`, `qa`, `build`, …) | | Agent Ledger | Issue | One per role (see [issue conventions](../primitives/issue.md)) | | Diff, code | PR | per Forgejo convention | Owner directives are issued on the issue. PM only watches issue comments — PR comments are out of scope. This keeps one canonical surface for both observation & implementation. The brief markers are role-scoped: ``, ``, ``. `pm next` upserts each by `(issue, role)`. ## Boundaries The PM is singular per repo. Working agents maintain their own work ledgers on the same issue using their own markers (see [issue conventions](../primitives/issue.md)) — PM MUST NOT modify those. Working agents MAY apply `agent/*` labels directly when reporting their own state (`agent/working`, `agent/review`, etc.); PM does not gate this. Working agents MUST NOT modify the PM ledger or parse `/` syntax from comments. ## PM has no code-write authority PM does not commit, does not modify files in the repo (including `./wiki/`). Project-local memory in `./wiki/` is updated by Developers in their PRs, alongside the code change that motivated the update. PM's write surface is Forgejo only: comments, labels, its own work ledger, the brief comment, and the org-level Forgejo Wiki (`fjx wiki set`). This boundary is what makes PM safe to invoke on a cadence. ## Why PM gets more judgment than DEV/QA The DEV and QA prompts are tightly scoped — narrow output shapes, evidence-required blocker checklists, explicit anti-patterns. PM's prompt is looser, and deliberately so. PM's value is **curation**, which is judgment work that doesn't reduce cleanly to a checklist. Deciding whether a Context bullet earns its place, whether a note belongs in the project wiki, how to phrase a brief refresh — these are calls a tight imperative prompt would get wrong. The constraint that keeps PM safe despite the loose prompt is the **bounded write surface**: PM has judgment _inside_ (comments, labels, briefs, ledger, wiki), and none _outside_ (the repo tree, working agents' ledgers, `/` parsing). ## Owner directives Owner directives — the `/` vocabulary owners type in issue comments — are parsed and executed by `fjx pm next`, not by the PM agent. See [Slash commands](../primitives/slash-commands.md) for the owner-facing reference. The trust map (`pm.trustMap` in `.fjx-cache.json`, default `["erik"]`) governs which authors `fjx pm next` accepts directives from. ## Triggers `fjx pm next` fires the following transitions without any directive: - `agent/new` + no `` → write initial dev brief (`phase: simple`), strip `agent/new`, assign issue to `agent-dev` - `agent/review` + no `` → auto-route to `agent-qa` only when **all four** guards hold: (1) a PR (open or merged) is linked to the issue, (2) `assignee=agent-pm` (so `fjx dev done` ran), (3) the `agent/review` label is present, and (4) there are zero new comments this tick. When any guard fails the trigger is suppressed and the issue is surfaced via a `qa handoff held: …` diagnostic so the PM LLM can apply judgment per `src/prompts/pm-bootstrap.md`. Any new comment defers — `fjx` never auto-routes past unread owner prose. The two-part check (label present AND brief absent) prevents spurious re-fires when the same label appears at a later workflow stage. The additional QA guards prevent premature handoff while dev is still working or while the owner is steering the issue. **Dispatch is by assignment.** PM hands work to a role by setting `assignee=agent-`. Labels record state (`agent/working`, `agent/review`, `agent/blocked`); they do not name the next worker. The dispatch labels `agent/dev`, `agent/qa`, and `agent/build` are not used — `fjx next` filters by assignee. ## Brief management A brief is a separate comment scoped to the issue, with a role-specific marker (``, ``, ``). See `src/prompts/pm.md` for authoring guidelines (shape, earn-its-place test). The minimal-shape brief points at the phase prompt by file path and lets the prompt file be the source of truth; brief authoring guidelines are in the PM prompt, not here. `pm next` creates at most one brief per `(issue, role)`. Refreshes are in-place edits. The user MAY write a brief by hand using the role-scoped marker; `pm next` detects the human-authored brief by marker and only refreshes when the owner explicitly requests it — never unprompted. ## Ledger structure ```markdown ## Ledger for: agent-pm Status: watching | paused | error | closed Checkpoint: Briefs: - [dev](#issuecomment-M) @ - qa: absent - build: absent Depends: 12 14 ### Activity Format: `i/ : ` 1. i4/needs-spec : refreshed dev brief 2. i4/spec-approved : applied 3. i4/refresh-brief : refreshed dev brief 4. i4/spec-approved : rejected — @agent-dev not authorized 5. i4/foo : unrecognized 6. i4/block : superseded by i4/unblock 7. i4/note : recorded in notes 7 ### Notes 7. Freeform observations ``` The Activity section is a **numbered list** so entries can be cross-referenced by position. Each entry has a leading `i/` token (issue number + command), the literal `:` separator, then a free-form action. This token is the dedupe key — it MUST be parseable with a fixed regex. For `/note`, the action is `recorded in notes N` where N is the Activity entry's own position number. The note text is appended to the Notes section at the same number so the cross-reference is navigable in both directions. Brief lifecycle is captured in the metadata `Briefs:` block — `pm next` updates the per-role line on refresh rather than logging brief edits as Activity entries (Forgejo comment edit history covers per-edit detail). Present briefs use a markdown link `[role](#issuecomment-ID) @ `; roles with no brief are listed as `role: absent`. ## Issue Finalization PRs use `Refs #`, not `Closes #N`, so a PR merge does not auto-close the issue. The issue stays open through dev → qa → PM finalization. `fjx pm close ` is the canonical close path: 1. Reads the PM ledger. 2. Sets `Status: closed` and appends a `closed` Activity entry. Brief comments are left untouched — the issue is closing and no working agent will read them. 3. Closes the issue on Forgejo (PATCH state=closed). Idempotent if already closed. 4. Removes the issue's worktree if present: standard path is `../worktrees/-` where `` is the repo name and `` is the issue number. Uses `git worktree remove --force` (safe because the issue is closed). 5. Prints the ledger Notes verbatim under `notes_for_review[]`. Anything durable is promoted to the Forgejo Project Wiki with `fjx wiki set --body-file ...` (see `src/prompts/pm.md` for the pattern and `fjx wiki --help` for the command surface). `set` is an upsert — safe to re-run. If a closed issue ever appears in `closed_candidates[]` (e.g. a human closed it manually before PM finalized), run `fjx pm close ` to finalize. Sweep filters out issues whose PM ledger already has `Status: closed`, so finalized issues do not re-surface. Untracked closed issues (no PM ledger) are also filtered out. ## Forgejo API Use `fjx`. Docs at `/swagger.v1.json`. Auth via env vars `FJX_TOKEN` (preferred) or `FJX_USERNAME` + `FJX_PASSWORD`. `fjx` derives server, owner, and repo from the `origin` remote. ## Failure handling - **Missing Ledger:** `pm next` creates one on first write. - **Corrupted or Duplicate Ledgers:** PM stops processing the issue, posts a diagnostic, and applies `agent/blocked`. Manual intervention required. - **Trust Map Drift:** `pm next` honors the current trust map; in-flight commands from a removed author are rejected and recorded. - **API Failure:** On transient API failure, the checkpoint is not advanced (it's only saved after success). The next run re-processes the affected window safely thanks to Activity-based dedupe. ## See also - The PM bootstrap prompt: [`src/prompts/pm.md`](https://git.tfks.net/tfks/fjx/src/branch/main/src/prompts/pm.md)