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
6 changes: 6 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Framework-owned session-protocol files, synced verbatim from ~/code/ai by its
# INSTALL.md; formatting them would diverge from the source of truth.
.session.md
AGENTS.md
CLAUDE.md

.DS_Store
.moon/
CHANGELOG.md
Expand Down
233 changes: 233 additions & 0 deletions .session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# Session Protocol

This is the startup map for the repo's agent session system. Keep it in
context; load lifecycle skills and their references only when the user asks for
that lifecycle action.

## Command Routing

Sessions are opt-in. Default to no session and do not create journal state
unless the user explicitly asks.

| User intent | Action |
|-------------|--------|
| "session setup", "setup sessions", "onboard me" | Invoke `session-setup`. |
| "new session", "start a session" | Invoke `session-new`. |
| "continue session N" | Invoke `session-continue` with `N`. |
| "session close", "wrap up", "end the session" | Invoke `session-close`. |
| "journal sync", "sync journals", "refresh journals" | Invoke `journal-sync`. |
| Anything else | Help normally; do not journal. |

If the request sounds session-related but the mode is ambiguous, ask before
priming anything.

Do not run `journal-sync` automatically during startup or a lifecycle command.

## Current Session Binding

A task may have one current journal session. This binding belongs to the task,
not to a checkout or shared journal file, and must never be inferred from other
`in-progress` rows in `INDEX.md`.

- A successful `session-new` binds the newly created session to the current
task. An explicit new-session request is authoritative even when other
sessions are open. Do not warn about, ask permission because of, reuse,
continue, close, or otherwise surface those sessions. If the task was already
bound, replace only the task-local binding and leave the old session intact.
- A successful `session-continue <ID>` binds `<ID>` to the current task,
replacing any prior binding without modifying the previously bound session.
- `session-close` without an ID closes the current task's bound session. Other
open sessions do not create ambiguity.
- `session-close <ID>` closes that explicitly named session. If `<ID>` differs
from the current binding, preserve the current binding.
- If `session-close` has no ID and the task has no binding, ask for an explicit
ID. Never select a target by counting `in-progress` rows or incomplete
folders, even when exactly one exists.
- Preserve the binding across turns, context summarization, handoff, and a close
workflow paused for PR review. Clear it only after successfully closing the
bound session, or replace it after a successful new/continue action.
- If an explicit or bound target is missing or already closed, report that
exact target and do not fall back to another session.

`INDEX.md` is a history, allocation, and validation surface. It is not a task
ownership or lifecycle-target selector.

## Journal Root

The journal root is the default source for startup context and lifecycle state.
Once sessions are set up, the writable personal journal root is the exact
`journal/<github-login>` worktree for the authenticated user.

Resolve it in this order:

1. If the current branch is `journal/<github-login>`, use the current checkout.
2. If `gh api user --jq .login` succeeds and `wt list --format=json` shows a
worktree for `journal/<github-login>`, use that worktree.
3. For ordinary startup reads only, if the current branch is not any
`journal/*` branch and the current checkout has a local `.journal/`, use it
as a legacy/local fallback. Lifecycle skills never write through this
fallback.
4. If no journal root exists, continue without journal context unless the user
asked for setup or a session lifecycle command.

Other `journal/<login>` worktrees are peer journal roots. They are read-only
references for ordinary work and must never be selected as the personal journal
root, including when the current checkout happens to be a peer worktree.

`session-setup` is the only lifecycle skill that creates or opens the personal
journal branch. `session-new`, `session-continue`, and `session-close` must stop
and tell the developer to run `session-setup` if no personal journal worktree
exists.

## Startup Context

Before substantive repo work:

1. Read this file.
2. Resolve the journal root, if any.
3. If `<journal-root>/.journal/SKILLS.md` exists, load every required skill it
lists.
4. If `<journal-root>/.journal/TECH_NOTES.md` exists, read it.
5. Read recent session summaries only when they are likely to matter: session
lifecycle commands, substantial code/config/docs changes, architecture work,
multi-step research, or tasks that mention prior work. Use the latest three
closed sessions with `SUMMARY.md`; skip sessions without summaries.

Use `<journal-root>/.journal/INDEX.md` as the full session map. Search it for
task-relevant history and read only the matching `SUMMARY.md` files.

When cross-engineer history is relevant, peer journals may be consulted as
read-only references. Limit those reads to peer `INDEX.md`, `TECH_NOTES.md`, and
closed-session `SUMMARY.md` files, and identify the journal owner and session ID
when using that context. Do not load peer `SKILLS.md` as project requirements or
read peer active-session `NOTES.md` by default. Startup context comes from the
resolved journal root: prefer the personal root when it exists, otherwise use
only the documented non-journal legacy fallback. Treat peer context as
last-synchronized local state and never auto-sync it.

Do not read session `NOTES.md` files during ordinary startup. Read a session's
`NOTES.md` only when continuing that session, when a lifecycle skill instructs
you to, or when the user explicitly asks for it.

## Skills

Always load and follow:

- `git`
- `worktrunk`

They should be installed repo-locally under `.agents/skills`. If either is
missing, tell the user the protocol install is incomplete before doing version
control work.

Before touching a technology or workflow, survey available skills and load the
task-relevant ones. Project-required skills belong in
`<journal-root>/.journal/SKILLS.md`; keep that file as a short list, not a
manual.

## Branch And Journal Invariants

- Personal journal branches are named `journal/<github-login>`.
- Journal branches are never PR'd or merged into the default branch.
- The default branch keeps `.journal/` ignored and untracked.
- Only a `journal/<github-login>` branch may force-add `.journal/` paths. Use
explicit pathspecs; do not use broad staging such as `git add -f .journal`
outside a fresh journal setup or import.
- Implementation worktrees are created from the fetched default branch, never
from a journal branch.
- Before closing implementation work, fail if any non-journal branch has tracked
journal files: `git ls-files .journal` must print nothing.
- Lifecycle journal writes happen in the journal root, not in implementation
worktrees.
- The personal journal worktree is deliberately shared by concurrent sessions.
It is the explicit exception to normal per-agent worktree isolation;
implementation worktrees remain isolated.
- Peer journal worktrees are read-only references. Only `journal-sync` may
change them, and only by safely fast-forwarding their checked-out branches;
lifecycle skills never write to them.
- Worktrunk worktrees must live under `.wt/` via
`worktree-path = "{{ repo_path }}/.wt/{{ branch | sanitize }}"`. If config
would place worktrees elsewhere, stop and tell the user.
- Integration work uses GitHub PRs and squash merges. Do not use `wt merge`,
`wt step push`, or local `git merge` as the default integration path.

## Concurrent Journal Ownership

The personal journal worktree is expected to be dirty while sessions run in
parallel. A dirty journal worktree is not itself a blocker and must not be
described as an out-of-band change.

Before evaluating journal changes, determine the lifecycle operation's write
set:

- `session-setup` owns only the root scaffold or imported paths it creates.
- `session-new` owns the new `.journal/<ID>/` folder and its `INDEX.md` update.
- `session-continue` owns `.journal/<ID>/` and owns `INDEX.md` only when it must
reopen the session.
- `session-close` owns `.journal/<ID>/`, its `INDEX.md` update, and a
`TECH_NOTES.md` update only when the closeout requires one.

Dirty paths outside the write set are expected parallel-session work. They must
not block the operation or be surfaced for clarification. Do not modify, stage,
stash, restore, reset, clean, checkpoint, or commit them. In particular, never
checkpoint another active session's `NOTES.md`.

Scope status, staging, and commits to the write set with explicit pathspecs. If
global `git status` is inspected, its out-of-scope output is non-actionable. Use
path-limited commits such as `git commit --only ... -- <write-set>` so unrelated
staged changes cannot enter the commit. New or ignored journal files may be
added with `git add -f -- <write-set>`; never use broad staging outside fresh
setup or import. In commands, replace `<write-set>` with the literal paths owned
by the operation; never pass the placeholder itself. Check each shared root file
immediately before editing it, then stage and commit the completed write set
without an unrelated delay.

Routine journal mutations do not require a clean worktree or a preemptive
`pull --rebase`. Commit and push the scoped mutation first. On a rejected push,
run `git fetch origin`, then run
`git merge-base --is-ancestor origin/journal/<login> HEAD`. If it succeeds, the
fetched journal tip is already contained locally; retry the push once. If it
fails, stop on the actual remote divergence without rebasing, autostashing, or
touching paths outside the write set. Stop only if an unexpected change overlaps
the write set, a required shared root file has pre-existing uncommitted edits,
the commit cannot be constrained to the write set, or a real synchronization
conflict remains after the retry. A dirty shared root file is a narrow blocker
for that file; dirty folders owned by other sessions are not.

## Journal Structure

```
.journal/
β”œβ”€β”€ INDEX.md
β”œβ”€β”€ SKILLS.md
β”œβ”€β”€ TECH_NOTES.md
└── <ID>/
β”œβ”€β”€ NOTES.md
└── SUMMARY.md
```

- Session IDs are zero-padded three-digit folders (`001`, `002`, ...).
- `INDEX.md` is the table of contents across sessions, including active
`in-progress` sessions.
- `SKILLS.md` lists project-required skills.
- `TECH_NOTES.md` holds compact durable technical context and is editable.
- `NOTES.md` is the append-only running log for resuming an active session.
- `SUMMARY.md` is the curated postmortem written at closeout.

Exact templates and write procedures live in the lifecycle skills, especially
`session-new` for `NOTES.md` and `session-close` for `SUMMARY.md` / `INDEX.md`.

## During A Session

- Keep the current task's bound session `NOTES.md` updated at meaningful
checkpoints.
- Outside an explicit lifecycle operation, checkpoint only the bound
`.journal/<ID>/` folder using the concurrent journal ownership rule. A
lifecycle skill uses its resolved target and documented write set without
implicitly changing the binding. Never checkpoint any other session's files.
- Timestamp note headings in the user's local time as `YYYY-MM-DD HH:MM`.
- Correct earlier notes by appending a new entry, not by rewriting history.
- Record potential durable context in the bound session's `NOTES.md`; promote it to
`TECH_NOTES.md` during `session-close` so shared-root mutations stay narrow.
- Do not create or edit a separate `.journal/` inside an implementation
worktree.
17 changes: 17 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,20 @@
- The bundled action `dist/` is committed: after changing `src/`, run
`moon run root:package` and commit the refreshed `dist/` in the same change.
- Use Conventional Commit subjects; releases are automated by release-please.

<!-- BEGIN ai-protocol -->

# Agent Instructions

This repository's operating protocol lives in `.session.md`.

Before doing substantive work, read `.session.md` in full and follow it. It
covers startup context loading, session setup, session lifecycle, skill loading,
Worktrunk branching, session journaling, file schemas, architecture, and process
expectations.

If `.session.md` is missing, stop and tell the user the session protocol is not
installed correctly.

<!-- END ai-protocol -->