Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/docs-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ jobs:
set -euo pipefail
site_dir="_site"
rm -rf "${site_dir}"
rsync -a --delete docs/ "${site_dir}/"
# docs/agents/ is repo-internal contributor process guidance
# (tracker, ticket, and PR norms), not site content.
rsync -a --delete --exclude 'agents/' docs/ "${site_dir}/"
touch "${site_dir}/.nojekyll"
echo "lash.run" > "${site_dir}/CNAME"

Expand Down
40 changes: 40 additions & 0 deletions docs/agents/domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Domain docs

How the engineering skills should consume this repo's domain documentation when exploring the codebase. This repo is **single-context**: one root `CONTEXT.md` glossary + one `docs/adr/`.

## Before exploring, read these

- **`CONTEXT.md`** at the repo root: the ubiquitous-language glossary (Host Application, Execution Mode, Runtime Scenario, Trigger Occurrence, Pending Turn Input, Queued Work, and the rest).
- **`docs/adr/`**: read the ADRs that touch the area you're about to work in.

For a narrative map of the runtime rather than its vocabulary, the architecture chapters on the published docs site (`docs/architecture/`) orient faster than reading crates cold.

If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill creates them lazily when terms or decisions actually get resolved.

## File structure

Single-context (this repo):

```
/
├── CONTEXT.md
├── docs/adr/
│ ├── 0026-model-capability-is-host-supplied-data.md
│ ├── 0045-services-are-stateless-substrates-own-continuation.md
│ └── …
└── crates/ · examples/ · runbooks/
```

## Use the glossary's vocabulary

When your output names a domain concept (an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`, and honor its `_Avoid_` lines. Don't drift to synonyms the glossary explicitly avoids (for example, don't call a Host Application a "reference host", and don't call the Deterministic Simulation Harness an "e2e fuzz test").

If the concept you need isn't in the glossary yet, that's a signal: either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).

## Flag ADR conflicts

If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:

> _Contradicts ADR-0045 (services are stateless, substrates own continuation), but worth reopening because…_

Duplicate ADR numbers exist for 0034, 0036, 0037, and 0038. Cite those by full filename so the reference is unambiguous ([way-of-working.md](way-of-working.md) has the numbering rules).
43 changes: 43 additions & 0 deletions docs/agents/issue-tracker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Issue tracker: Linear

Issues and tickets for this repo live in **Linear**, in the **`lash` project** on team **`figments`**. Use the **Linear MCP tools** (`mcp__linear__*`) for all operations.

Issues are keyed `FIG-<n>`. Refer to issues by their **title/name**, never by a bare key; the key rides inside the name as a link.

This repo has no offline Linear helper script. An agent running without the Linear MCP (for example a sandboxed `codex exec` run) cannot read the tracker, so the ticket context it needs must be inlined into its prompt.

## Conventions

- **Create an issue**: `mcp__linear__save_issue` with `team: "figments"`, `project: "lash"` (+ `title`, `description` as Markdown, `parentId`, `labels`, `blockedBy`, `priority` as needed). No `id` = create.
- **Read an issue**: `mcp__linear__get_issue` (`id: "FIG-<n>"`, `includeRelations: true` for blockers). For comments: `mcp__linear__list_comments`.
- **List issues**: `mcp__linear__list_issues` (filter by `team`, `project`, `state`, `label`, `parentId`, `assignee`, `query`).
- **Comment**: `mcp__linear__save_comment` (`issueId`, `body`).
- **Apply / change labels**: `mcp__linear__save_issue` with `id` + `labels` (replaces the full label set; include existing labels you want to keep). Create new label strings first with `mcp__linear__create_issue_label` if they don't exist.
- **Set state / close**: `mcp__linear__save_issue` with `id` + `state` (`"Done"`, `"Canceled"`, etc.; Linear workflow states, not GitHub open/closed).
- **Assign**: `mcp__linear__save_issue` with `id` + `assignee` (user id, name, email, or `"me"`).
- **Relations**: `blockedBy` / `blocks` on `save_issue` are append-only; `removeBlockedBy` / `removeBlocks` to clear.

Do not file housekeeping/teardown/process chores here; Linear is code-facing only.

## Pull requests as a request surface

**PRs as a request surface: no.** Code review happens on GitHub PRs; Linear holds the work items. Triage does not scan PRs.

## When a skill says "publish to the issue tracker"

Create a Linear issue in team `figments`, project `lash`.

## When a skill says "fetch the relevant ticket"

`mcp__linear__get_issue { id: "FIG-<n>", includeRelations: true }`.

## Parent and child operations

A multi-ticket effort is one **parent** Linear issue with its slices and decisions as **sub-issues** ([way-of-working.md](way-of-working.md), [ticket-style.md](ticket-style.md)).

- **Parent**: `save_issue { team: "figments", project: "lash", title: <effort name> }`, body carrying the destination plus the one-line-per-child index.
- **Child ticket**: a Linear issue with `parentId: "<parent key>"`. Once claimed, assign it to the driving dev.
- **Blocking**: Linear native relations, e.g. `save_issue { id: <child>, blockedBy: ["<blocker key>"] }`. A ticket is unblocked when every blocker is in a terminal state (`Done`/`Canceled`). Read blockers via `get_issue { includeRelations: true }`.
- **Frontier query**: `list_issues { parentId: "<parent key>", state: <non-terminal> }` → drop any child with an open blocker or an assignee; first in index order wins.
- **Claim**: `save_issue { id: <n>, assignee: "me" }` (the session's first write).
- **Resolve a decision**: `save_comment { issueId: <n>, body: "<the decision>" }`, then echo the conclusion into the ticket body, then `save_issue { id: <n>, state: "Done" }`, then append a one-line gist plus link to the parent's index.
52 changes: 52 additions & 0 deletions docs/agents/pr-style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Writing pull requests

[ticket-style.md](ticket-style.md) keeps tickets high-level; the detail it pushes out lands here. A ticket says *what* and *why*. The **PR says how, and proves it works**. This is where file:line reasoning, the shape of the change, and the validation evidence belong.

PRs are a code-review surface, not a request surface; work items live in Linear ([way-of-working.md](way-of-working.md)). A PR exists to get a change reviewed and merged, so write it for the reviewer.

**Why the *why* lives in the ticket, not here: a deliberate departure.** Git-native convention (Google CL descriptions, Conventional Commits) anchors the *why* in the commit message. We anchor it in the ticket instead, because a ticket must be plannable, by a human or an AFK agent, before any code or diff exists. One consequence to respect: the squash commit body is durable history in this repo and feeds the release notes (below), while review scaffolding in the PR body is not. Anything that must outlive this diff belongs in the ticket, an ADR, the code, or the commit body, not only in review comments.

## What a PR carries

```markdown
Title: imperative, one sentence, readable without opening the ticket.

## Why
Link the FIG ticket, and restate the goal in one or two sentences so the
reviewer needn't open it. The ticket has the full context; this orients.

## What changed
The shape of the change and why this shape: the reasoning a reviewer needs
to judge it. Name the key files/seams, the trade-offs taken, the alternatives
rejected. This is the detail the ticket deliberately left out. If a rejected
alternative would still matter independent of this diff (a persistence,
protocol, or cross-cutting-pattern choice), promote it to an ADR and link it;
only diff-local trade-offs stay here.

## Validation
How you know it works, matched to the Definition of Done: repeatable steps a
reviewer could rerun, i.e. which runbook passed, which `just` recipe or
confidence-gate lane you ran, what you checked live. Not "tested locally".
"Merged with green CI" is necessary, not sufficient, for behavior changes.

## Risk / not included (optional)
Blast radius, anything deliberately out of scope, follow-ups filed as tickets.
```

## Rules

- **The first line is load-bearing.** It becomes the squash commit title in `git log`, so write it imperative, one sentence, standalone: `Add cursor pagination to the process registry listing`, never `Fixes`, `WIP`, or `Phase 1`.
- **User-facing changes carry release notes.** The squash commit body must include a `Release-Notes:` section describing the change for Lash's users (breaking changes say `Breaking:` first). The release workflow collects those sections and refuses to publish a range that has none. See `docs/PUBLISHING.md`.
- **Lead with the summary.** A reviewer reads the first paragraph and knows what the PR does and why. Depth follows; it doesn't open the PR.
- **Link the ticket, don't restate it.** One or two orienting sentences, then the link. The PR carries the *how*; the ticket carries the *what/why*. Don't copy the ticket body in.
- **Prove it, per the Definition of Done.** Behavior changes are live-validated (the relevant runbook passes; turn-execution changes run both durable geometries); mechanical changes state the verification you ran ([way-of-working.md](way-of-working.md)). Say what you actually did, including what you skipped.
- **Match the diff.** The PR body describes what the diff does. No aspirational claims for code that isn't there, no stale description after a force-push.
- **Follow-ups are tickets, not TODOs.** Work discovered but out of scope gets a FIG issue and a link, not a buried comment.
- **The ticket's prose bar applies here too:** lede first, cut filler, concrete over abstract ([ticket-style.md](ticket-style.md)). A reviewer skims, and a rambling PR body costs review time.

## Mechanics (defined elsewhere)

- **Commits.** No AI-assistant attribution or co-author trailers; see the root agent rules.
- **Branching and releases.** Short-lived branches off fresh `origin/main`, merged by PR; releases are dispatched manually by a maintainer from a green `main` (`CONTRIBUTING.md`, `docs/PUBLISHING.md`). Never tag or publish by hand.
- **Stacked PRs.** For dependent chains, use the native stack flow (global agent rules).
- **Generated and gated artifacts.** Run `just push-gate` before pushing. Docs changes must keep `python3 scripts/lint_docs.py` green, and embedded Rust snippets are regenerated with `python3 scripts/lint_docs.py --fix-snippets` rather than hand-edited.
Loading
Loading