Session artifacts
Last updated
May be stale
This page was last updated ago, on 8 October 2026. Check what it says against the code before relying on it.
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-architecturewrites its HTML report to the OS temp directory by design, so nothing lands in the repo. No branch, no PR./prototypecommits 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 reachesmaster— so it opens no PR./implementproduces code, not documents. See below./waveproduces neither: it walks the## Ticketschecklists 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/deliverdispatches open their own branches and PRs under the rules below, as does its/researchdispatch for the findings file it writes; its/triage,/to-ticketsand/grill-to-specdispatches 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 frommaster, naming the person the session works for (dev/internet-claude/<slug>). Thedev/prefix is what makes it deployable (AGENTS.md); a server still has to be pointed at it with!branch. Incityrp2-contrib, drop thedev/: 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/masterWhere it says "the full test suite", it means the GLuaTest unit and integration runs (
.gitea/scripts/gluatest.sh unitandintegration), 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.
- A
## Changelogsection at the head, only for a player-facing change: thepublic=line in a fenced block for the merger to copy, drafted with thechangelogskill. The skill is still what decides whether the change is player-facing; an internal change gets no section. 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.- What changed and why, at the level of the decisions taken rather than a restatement of the diff.
- A test plan for whoever picks the branch up in game, only where something in game can observe the change. See below.
- What moved in the test suites: each suite added or changed, what it now covers, and any
case marked
knownBadwith 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. - 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 thechangelogskill. 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 adev/branch to a development server, and a map-keyed system (doors, properties, spawns, areas) has to be checked on a real map rather thangm_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-reporteragent, 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, theRefslink 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
/wayfindersession, 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".