CityRP Documentation Repository

Session artifacts

Last updated

What a skill session produces, and where each kind of output must end up. Two rules, split by kind: documents go to a branch and a draft PR, decided work goes to issues. Nothing is left in the transcript.

master accepts direct pushes for a docs-only change, but documents reach it through a PR anyway: the PR is where the review happens and where the record of what the document was for lives.

Documents: branch and draft PR

Any Markdown artifact a skill writes into the repo — an ADR, a design document, research findings, a glossary change — is committed to a branch and raised as a draft PR.

One PR per session, not per artifact. A session producing two ADRs and a glossary change opens one branch and one draft PR carrying all three.

Branch naming

Every documents branch is docs/<name>/<slug>. <name> is whose work it is, in the same form as a dev/ branch: the author, or <person>-<agent> for an agent session (docs/internet-claude/<slug>). <slug> names the session's topic, whichever kinds of document it produced.

Artifact Path in repo
ADR docs/adr/NNNN-<slug>.md
Design document, or a spec kept as a document docs/design/<topic>/<stage>.md
Research findings docs/design/<topic>/research-<slug>.md
GLOSSARY.md glossary edits GLOSSARY.md

One folder per topic. Every document about the same topic lands in that topic's existing folder: successive stages of a design doc, a later redesign, the research that fed it. Coming back to a topic months later means adding a file to docs/design/<topic>/, never creating a docs/design/<topic>-v2/ sibling. The folder is the topic's whole history, in order.

That includes research: findings live beside the design they feed, as research-<slug>.md in the same topic folder, rather than in a parallel tree of their own. A topic's research and its design are read together, and splitting them by kind means neither half points at the other.

ADRs are the exception: they stay flat and numbered in docs/adr/, because the number is the ordering and /domain-modeling writes them that way. Several ADRs on one topic are still several flat files.

These branches deliberately omit the dev/ prefix. AGENTS.md requires dev/ for anything deployable and exempts "doc-topic edits, other .md files, and similar non-code changes" — which is exactly what these are. A docs/ branch tells the deploy pipeline it carries no game code.

The exception is a /build-ui feature: its UX spec is the first commit on the feature's own dev/<person>-<agent>/<feature> branch, because the code that implements it lands on the same branch and in the same PR. See skill-pipelines.md.

GLOSSARY.md is still captured inline

/domain-modeling writes glossary terms the moment they crystallise, mid-conversation, and that behaviour does not change. What changes is the destination: those edits accumulate in the working tree and go out on the session's artifact branch at the end, with the rest of the session's documents.

The draft PR

Open it as soon as the first artifact is committed, so the work is visible while still in progress:

tea pulls create --draft --base master --title "docs: <session topic>" --description "$(cat pr-body.md)"

--draft prefixes the title with WIP: , which is how Gitea marks a draft. The body says what the session was, what each artifact decides, and links any issue that prompted it. A documents-only PR carries neither a ## Changelog section nor a test plan, and says nothing about leaving them out. Mark it ready for review when the session ends by dropping the WIP: prefix.

status: moves on a person's approval, never on an agent's. A design doc, UX spec or event plan is born status: Proposed. An APPROVED verdict from /design-review or /ux-review is advice and changes nothing. Once a human reviewer approves the pull request, set status: Accepted in that same PR before it merges, so the merged document says what was agreed. Landed and Implemented follow the code, per the design index.

Delete the branch by hand once merged. Nothing on this repo prunes it for you, and the remote carries the backlog to prove it.

Exceptions

  • /improve-codebase-architecture writes its HTML report to the OS temp directory by design, so nothing lands in the repo. No branch, no PR.
  • /prototype commits throwaway code to its own throwaway branch and leaves a context pointer on the issue. That branch is a primary source, not an answer — the verdict is captured on the issue and only the validated decision reaches master — so it opens no PR.
  • /implement produces code, not documents. See below.
  • /wave produces neither: it walks the ## Tickets checklists under the issues it is passed and starts one session per child it can advance, each running the skill for that child's state — so what a wave leaves behind is whatever those skills produce. Its /deliver dispatches open their own branches and PRs under the rules below, as does its /research dispatch for the findings file it writes; its /triage, /to-tickets and /grill-to-spec dispatches edit the tracker and ask the maintainer questions.

Code: /implement

/implement follows the repo's own branching rules rather than the document rules above, because its output is deployable. So does /deliver, which runs the same work as staged subagents and carries the pull request through to green CI. /deliver-epic runs /deliver's stages once per ticket of an epic on one branch, then reviews the whole epic, and follows the same rules with one PR for the lot: a Closes #<n>. line per ticket, a Closes #<epic>. line once every ticket is in it (Part of #<epic>. while a stopped run leaves some out), one public= entry for the epic, and a merge commit rather than a squash, so each ticket's test-first history survives. Once CI is green, it lists the open items for a person to decide, actions the decisions, reviews the branch again and rewrites the description, and only marks the PR ready when nothing stands and CI is green again.

  • Branch dev/<person>-<agent>/<slug> cut from master, naming the person the session works for (dev/internet-claude/<slug>). The dev/ prefix is what makes it deployable (AGENTS.md); a server still has to be pointed at it with !branch. In cityrp2-contrib, drop the dev/: the contrib workflow adds it when it raises the review PR.

  • Conventional Commits on each commit (fix:, feat:, chore:).

  • A draft PR opened once the first commit lands, marked ready when the work is done.

  • public= goes on the last line of the merge commit message, never in the PR title or on a leaf commit. PRs land as merge commits, so every leaf reaches master and is parsed by the changelog webhook. See commit-message-conventions.md.

  • Before marking the PR ready, run both diff-scoped gates from AGENTS.md. This repo has no typechecker, so where the skill says "run typechecking", it means these:

    python3 .gitea/scripts/lint-diff.py --base origin/master gamemodes/cityrp_2_by_limelightgaming
    python3 .gitea/scripts/standards-diff.py --base origin/master
    

    Where it says "the full test suite", it means the GLuaTest unit and integration runs (.gitea/scripts/gluatest.sh unit and integration), described in CONTRIBUTING.md § Testing, which CI also runs on every pull request.

What the PR description carries

Up to six things. The first, fourth and sixth are conditional: where one does not apply, leave the whole section out, heading included, and do not mention its absence anywhere else in the body either. A line such as "Docs only, with no in-game test plan and no changelog entry" is the dropped section coming back as prose; the description of what changed already says what kind of change it is.

  1. A ## Changelog section at the head, only for a player-facing change: the public= line in a fenced block for the merger to copy, drafted with the changelog skill. The skill is still what decides whether the change is player-facing; an internal change gets no section.
  2. Closes #<n>. for each issue the PR implements, one per line, at the head of the body, or directly below the changelog section where there is one.
  3. What changed and why, at the level of the decisions taken rather than a restatement of the diff.
  4. A test plan for whoever picks the branch up in game, only where something in game can observe the change. See below.
  5. What moved in the test suites: each suite added or changed, what it now covers, and any case marked knownBad with the issue it pins. Where part of the change has no coverage, say which part and why, so the gap is a reviewed decision rather than an omission.
  6. A Feedback close message as the very last thing, below gate notes and any tool attribution footer, only when the PR closes a feedback-synced issue: an unfenced public= message for the player who posted it, drafted with the changelog skill. Everywhere else in the body, public= sits inside code: the changelog fence, or inline code when the body talks about it. See Feedback close message.

Report a diff gate only when it trips, naming the rule and why the PR is being raised regardless. Both gates passing is the baseline and says nothing about the change.

The in-game test plan

Any change something in game can observe carries one: a command, a piece of UI, an entity, a job, an item, a hook, a save path, a migration. A refactor meant to change nothing still carries one, naming the behaviour that has to survive it.

Automated tests do not replace it. They cover what the harness can reach, and bots run no Lua, never authenticate, and are waved through access checks (see CONTRIBUTING.md § Testing). The client suites reach client-realm code, but only as one signed-in player on gm_construct against seeded test data, so most client-side paths and permission boundaries are still only ever proven by a person in game.

Write it as steps a tester can follow without reading the code:

  • Where: the server and map the check needs. !branch <branch> deploys a dev/ branch to a development server, and a map-keyed system (doors, properties, spawns, areas) has to be checked on a real map rather than gm_construct.
  • Setup: the job, rank, access flag, items, entities or database state to have in place first.
  • The steps, each one an action paired with the result it must produce.
  • The negative case: who must not be able to do this, and what they get instead.
  • The realms it has to hold on, where the change touches both: a client-side check proves nothing about the server half, and vice versa.

Where and Setup are one-line context; everything the tester actually does or checks — steps, the negative case, each realm — renders as its own - [ ] entry, so the PR body becomes a checklist the tester ticks off item by item:

Where: dev server, `!branch dev/name-agent/slug`, `rp_evocity_v4b1_fl2`.
Setup: staff rank RP Assistant; a spawned `func_door` within range.

- [ ] Aim a door remote at a `func_door` and press use — door toggles.
- [ ] Repeat as a normal player with no staff access — remote does nothing, no toggle.
- [ ] Repeat with the client-side prediction disabled — server still toggles the door.

Where nothing in game can observe the change (documentation, CI configuration, tooling, or tests alone), drop the section entirely, without a sentence elsewhere saying there is none.

Specs and decided work: issues

A finished spec is never left in the conversation. It is written to the issue tracker, typed so a later session can pick it up:

What was decided Type label Picked up by
A spec, not yet broken down type/spec /to-tickets sizes it
A single implementable piece of work type/task /deliver, or /implement by hand
A body of work spanning several tasks type/epic + child issues /deliver-epic as one PR, or /deliver child by child
A goal spanning several epics type/initiative + child epics Each epic, as above
An investigation someone must do first type/research /research
A question still needing a decision type/discussion /grilling
An idea agreed in outline, not yet grilled type/stub /grill-to-spec rewrites it as a spec
A defect confirmed along the way type/bug /deliver, or /implement by hand

Set priority and effort per issue-tracker.md. A spec is not born triaged: it waits at status/awaiting-review until /to-tickets breaks it down, which converts it to a type/task or type/epic and moves it to status/triaged. A stub waits at status/awaiting-review too, and keeps that status when /grill-to-spec turns it into a spec. The other rows are decided work and go straight to status/triaged.

A spec issue that points at a document

When the spec already exists as a reviewed document on a branch (a UX spec from /ux-design, an event plan), /to-spec's template would restate it and leave two copies to drift. Publish a pointer spec issue instead. The document stays the single source of truth, and the issue exists so /to-tickets has something to turn into the epic.

Labels are the ones /to-spec applies: type/spec, a priority, no effort, status/awaiting-review. The body carries:

  • one paragraph on the player-side problem, in the document's own words
  • Spec: a link to the document pinned to its approved commit, https://gitea.limelightgaming.net/limelight-development/cityrp2/src/commit/<sha>/<path>, so later edits cannot change what the tickets were cut from
  • PR: the pull request the document is on, and the branch name
  • Implementation Decisions: what was settled about how to build it that the document does not say, including anything decided while grilling the idea
  • Testing Decisions: the seams agreed with the user, sketched the way step 2 of to-spec does

Capture the sidebar

Anything raised in passing that is real work but not this session's point — the "oh, we also need to…" — gets its own issue before the session ends, separate from the spec the session is producing.

  • Hand each sidebar to the issue-reporter agent, one spawn per sidebar. It checks the tracker first, so a sidebar already tracked gets a comment or nothing rather than a second issue, and it owns the type, the Refs link and any dependency.
  • Its brief is everything it knows: what was seen and the evidence, the issue or PR it surfaced from and whether it blocks that work, and whether the user agreed to an idea. Write it so someone reading it cold does not need this conversation.
  • Relay its report to the user: what it filed, commented or discarded, and anything it handed back.
  • In a /wayfinder session, a sidebar beyond the destination is still filed as its own issue, and the map's Out of scope section links to it. Out of scope means "not on this route", not "not worth doing".