Issue Workflow
Issues are work entries. The workflow is mostly human-driven at creation: create an issue, label it for risk and routing, optionally embed /commands to signal intent, and let PM (or yourself, in manual mode) handle the rest. Once an agent picks up the issue, the issue itself becomes the single thread of record — all agent ledgers live here.
Creating an issue
- Title and body — describe the what and the why. Mark unknowns as
???. - Apply labels:
agent/new— the onlyagent/*label you apply at creation. Issues without this label will not be triaged by the PM.risk/*— flags the blast radius, influencing how the PM should brief the working agents.complexity/*— influences how the PM should brief the working agents.priority/*— helps agents choose next best issue to work on.- Domain labels as appropriate (
area/*,kind/bug, etc.)
- Embed
/commandsfor intent you already know:/needs-spec— you know this warrants a spec proposal as the first cycle- others per the command vocabulary
- Submit. Walk away.
What PM does on agent/new
- Reads the body and executes any
/commands. - Curates an initial brief reflecting the requested phase (e.g.
/needs-spec→ brief asks the Developer to produce a spec proposal first; otherwise the brief frames the work as direct implementation) - Strips
agent/new; the issue is now visible to you for dispatching the Developer - Records the activity in its per-issue ledger
If you don't apply agent/new, PM ignores the issue. That's the opt-in: not every issue needs PM attention.
Manual mode
PM offline, or you don't want PM in the loop:
- Apply the labels yourself in the UI
- Skip
/commandsentirely and transition labels directly - Write a brief by hand using
<!-- pm:brief -->if you want one, or skip it
The agent/new signal is inert when PM is off — it sits as a label until processed. Nothing breaks.
Issue vs. PR
- Issue: the what, the why, and the record of work — problem statement, acceptance criteria, risk notes, all agent ledgers, the conversation
- PR: the how — implementation, evidence, validation output. Assume PR comments carry nothing of import
Link both ways: PR body MUST reference the issue (Refs #N); issue gets the cross-link automatically from Forgejo. See PR conventions.
Closing
PR merges do not auto-close the issue — the PR body uses Refs #N, not Closes #N. The issue stays open through dev → qa → PM finalization; fjx pm close <N> is the canonical close path. This avoids the "closed but not yet finalized" window where a closed issue would re-surface on every pm next.
Ledger Model
Each agent (PM, Developer, QA, Builder) maintains one canonical ledger comment per issue, which it edits in place. Ledger records what the assigned agent has done or still needs to do. The ledger is a small, issue-local task database implemented as one editable comment. It gives agents a durable memory surface without adding infrastructure.
All ledgers live on the issue. The PM ledger has a distinct role (assistant's record of /commands processed, briefs curated); see PM role. Working-agent ledgers track the assigned agent's tasks.
- Issue comments: incoming requests
- Ledger: normalized task queue for agent, progress tracker for reviewers
- Commits: execution
Constraints:
- Uses Forgejo REST API (via
fjx) - No external database
- Human-readable
- Idempotent across runs
- Safe to resume after interruption
- Durable in issue history
Non-Goals:
- Perfect natural-language comment parsing
- Full conversation understanding
- Complex task dependency tracking
- Replacing Forgejo labels
- Replacing human review
Multi-Agent Support: Each agent manages its own work ledger. Agents MUST NOT modify other agents' ledgers. Assume only one active agent for each role (Developer, QA, Builder).
Interaction With Labels: The ledger tracks per-issue agent work. Labels drive the broader workflow (see primitives overview).
Agent Cycle
Each agent invocation is a one-shot cycle. The executor schedules invocations; the agent itself does not loop in-process. One cycle:
- Claim the issue: transition
agent/<role>→agent/working(fjx <role> claim <id>) - Fetch issue comments
- Locate ledger (on the issue) by marker
- Parse existing tasks
- Extract new directives/questions/suggestions
- Append new tasks
- Execute open tasks
- Update task statuses
- Update checkpoint
- Edit ledger comment
- Hand off: transition
agent/working→agent/review(fjx issue label <id> --add agent/review --remove agent/working)
Forgejo Integration
Agents MUST go through fjx. Do not hand-roll https/curl against the Forgejo REST API — the CLI handles auth, repo identity, caching, and label resolution. If a needed operation is missing from the CLI, that is a gap to file as an issue, not a license to bypass the CLI.
Run fjx --help for the current command surface. Commands used in the ledger flow:
fjx comment list <N> [--since <ISO>] # fetch issue comments (optionally since checkpoint)
fjx comment create --issue <N> --body-file ./tmp/body.md
fjx comment edit <comment-id> --body-file ./tmp/body.md
fjx dev ledger --body-file ./tmp/ledger.md # upsert agent-dev:ledger on the current issue
fjx qa ledger --body-file ./tmp/ledger.md # upsert agent-qa:ledger on the current issue
fjx dev next [<N>] # start the next dev cycle: pick or load <N>, set up worktree, claim (label + assignee + ledger), emit pm:brief:dev
fjx qa claim <N> # assign agent-qa; create QA ledger if absent
fjx dev done <N> # → agent/review on issue; reassign issue to agent-pm; assign linked PR to $FJX_OWNER
fjx qa checks <N> # list Forgejo Actions runs for PR <N>'s head SHA
Known CLI gaps (surface the gap in your ledger rather than bypass):
- No
fjx build ledger— the builder currently upserts its ledger via the genericcomment create/comment editprimitives combined with a marker lookup. Mirroringdev ledger/qa ledgerfor the builder would round out the set.
Implementation details live in src/; the Forgejo REST surface is documented at https://git.tfks.net/swagger.v1.json for CLI authors, not for agents.
Ledger Lifecycle
The ledger comment MUST include a unique marker:
<!-- agent-<role>:ledger -->
Every agent cycle MUST leave a ledger entry and a PR — even outcomes like "won't fix" or "not reproducible" warrant a ./wiki/findings/<N>-<slug>.md recording the justification. The work is invisible without evidence; "I looked and decided no" is a finding.
Fetch existing comments with fjx comment list <N> (optionally --since <checkpoint>). Upsert the ledger comment with the marker-aware helper: fjx <role> ledger --body-file ./tmp/ledger.md for dev and qa; the builder uses fjx comment create / comment edit with its marker until its dedicated subcommand ships (see "Known CLI gaps" above).
Update Cursor:
An ISO-formatted timestamp checkpoint records when the agent's work was last reflected in the ledger. It signals "I have addressed all comments made to this point". Once the agent's ledger comment has been identified, save its checkpoint timestamp to local cache. This timestamp can be passed as --since <ISO> to fjx comment list to fetch only new comments.
NOTE: Before submitting an updated ledger, look for any new comments that arrived since the last checkpoint. Avoid missing work for comment 2 in this scenario:
- Issue opened
- ledger created
- comment 1
- implementing work from comment 1
- comment 2
- ledger checkpointed comment 1
- comment 3
- implementing work from comment 3
To avoid this, read the current ledger checkpoint time and use that to find all comments since then. Ensure that all tasks from those comments are included in the ledger when checkpointing. There is still a possible race condition, but the window is small enough to be acceptable at this point.
Ledger Structure
<!-- agent-<role>:ledger -->
## Ledger for: agent-<role>
Status: {working,blocked,review,complete} @ <timestamp>
---
### Tasks
- [ ] D001 — DESCRIPTION
- Source: <issue comment reference>
- Status: pending | in-progress | blocked
- [x] D002 — DESCRIPTION
- ✔ commit <sha> or explanation
### Notes
- Freeform observations
Task Management Rules
Task Creation
Agent MUST:
- Extract actionable items from issue comments after
checkpoint - Treat lines that begin with
/<command>as inert text —/<command>syntax handled by the fjx tooling. Agents MUST NOT create tasks from them, even if the surrounding prose is directive-like. - Classify the remaining comment content:
directive→ MUST create taskquestion→ MUST create tasksuggestion→ MAY create taskapproval→ no task requirednoise→ ignore
- Assign unique task IDs using the format
D###
Task Completion
Agent MUST only mark a task complete when:
- work is implemented, OR
- the task is explicitly resolved with an explanation
Completed tasks MUST include evidence:
- commit SHA, OR
- explanation if no code change was needed
Example:
- [x] D002 — Add validation for missing config
- Evidence: commit abc123
Alternative resolution:
- [x] D003 — Consider switching parser library
- Resolution: Not implemented; existing parser already supports required behavior.
Task Safety
Agent MUST NOT:
- mark unclear items as complete
- silently ignore directives
- delete unresolved tasks
If a task is unclear, mark it blocked:
- [ ] D004 — Clarify expected behavior for empty input
- Status: blocked
- Needs: human clarification
Addressing Review Feedback
When a reviewer requests changes (at agent/review), the agent MUST:
- Read all issue comments since the ledger checkpoint
- Extract tasks per the Task Management Rules above
- Implement the requested changes
- Push the changes
- Post an issue comment summarizing what changed and referencing each addressed comment
- Update the ledger (new tasks marked complete, checkpoint advanced)
- Re-signal readiness:
fjx issue label <id> --add agent/review --remove agent/working
Do not stop after pushing. A silent push leaves the work in an ambiguous state — the reviewer has no signal that their feedback was addressed.
Failure Handling
Missing Ledger: If no ledger exists, the agent MUST create one.
Corrupted or Duplicate Ledgers: Comment at the top of the ledger on the condition with any diagnostic info (e.g. error messages). Label as agent/review.