From f82ab7a97617c9cf08caa1036aa200446a504bf2 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Wed, 5 Aug 2026 18:52:59 -0700 Subject: [PATCH] chore: install agent session protocol --- .agents/skills/git/SKILL.md | 154 +++++++++++ .agents/skills/journal-sync/SKILL.md | 126 +++++++++ .../skills/journal-sync/agents/openai.yaml | 4 + .agents/skills/session-close/SKILL.md | 92 +++++++ .../references/session-artifacts.md | 68 +++++ .agents/skills/session-continue/SKILL.md | 26 ++ .agents/skills/session-new/SKILL.md | 30 +++ .../session-new/references/notes-template.md | 25 ++ .agents/skills/session-setup/SKILL.md | 68 +++++ .agents/skills/worktrunk/SKILL.md | 254 ++++++++++++++++++ .../skills/worktrunk/references/commands.md | 245 +++++++++++++++++ .gitignore | 5 + .session.md | 233 ++++++++++++++++ AGENTS.md | 1 + CLAUDE.md | 71 +++++ 15 files changed, 1402 insertions(+) create mode 100644 .agents/skills/git/SKILL.md create mode 100644 .agents/skills/journal-sync/SKILL.md create mode 100644 .agents/skills/journal-sync/agents/openai.yaml create mode 100644 .agents/skills/session-close/SKILL.md create mode 100644 .agents/skills/session-close/references/session-artifacts.md create mode 100644 .agents/skills/session-continue/SKILL.md create mode 100644 .agents/skills/session-new/SKILL.md create mode 100644 .agents/skills/session-new/references/notes-template.md create mode 100644 .agents/skills/session-setup/SKILL.md create mode 100644 .agents/skills/worktrunk/SKILL.md create mode 100644 .agents/skills/worktrunk/references/commands.md create mode 100644 .session.md create mode 120000 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.agents/skills/git/SKILL.md b/.agents/skills/git/SKILL.md new file mode 100644 index 0000000..37811b9 --- /dev/null +++ b/.agents/skills/git/SKILL.md @@ -0,0 +1,154 @@ +--- +name: git +description: > + Use for day-to-day Git workflow when creating commits, choosing commit scopes, + preparing PR titles, or deciding how changes should be merged. Enforces a + GitHub-only squash-merge policy, Conventional Commit subjects, concise commit + bodies, and a commit-often workflow during branch development. +--- + +# Git Workflow + +Use this skill as a small operating policy for Git usage. The point is to make +different agents converge on the same workflow instead of inventing one from +training priors. + +## Default stance + +1. All integration merges happen on GitHub, never locally. +2. All GitHub merges should be squash merges. +3. PR titles must use Conventional Commit format, because the squash-merge + commit on the main branch will inherit the PR title. +4. Commit often while working on a branch. Branch history is temporary working + history, not the final shape of `main`. +5. Do not waste time trying to make every local commit perfect. Keep commits + useful, focused, and frequent, then rely on squash merge to produce the + final main-branch commit. + +## Merge policy + +- Do not locally merge feature branches into `main`, `master`, or other shared + integration branches. +- Do not treat a local merge commit as the final integration step. +- Use GitHub's squash merge button or `gh pr merge --squash`. +- The PR title must already be the final commit subject you want on the target + branch. + +## Commit subject format + +Use Conventional Commits: + +```text +type(scope): short imperative summary +``` + +or, when no scope is needed: + +```text +type: short imperative summary +``` + +Use standard types unless the repository clearly uses a different established +set: + +- `feat` +- `fix` +- `refactor` +- `docs` +- `test` +- `ci` +- `build` +- `chore` +- `perf` +- `style` +- `revert` + +Subject rules: + +- Use imperative mood: `fix cache invalidation`, not `fixed` or `fixes`. +- Keep the type and scope lowercase. +- Do not end the subject with a period. +- Make the subject specific enough to stand alone in `git log`. + +## Scope guidance + +Scopes are optional in small repos and expected in larger or mixed repos. + +Good natural scopes include: + +- a project in a monorepo +- a Go package in a larger Go codebase +- a service, subsystem, or app in a multi-system repository +- a clearly named operational area such as `ci`, `release`, or `docs` + +When in doubt, inspect recent history and reuse the existing vocabulary instead +of inventing a new scope: + +```bash +git log --format='%s' -n 100 +``` + +If the repository already has a consistent scope pattern, follow it. + +## Commit bodies + +There is no single standard body template. Keep bodies concise and use them to +cover what the diff alone does not explain clearly, usually: + +- why the change exists +- any important constraint or tradeoff +- any reviewer or operator note that should survive in history + +Default shape: + +```text +type(scope): short imperative summary + +Why this change is needed. +Any critical context or operational note. +``` + +Rules: + +- Leave a blank line between subject and body. +- Omit the body entirely when the subject and diff are already obvious. +- Prefer short paragraphs or a few bullets over long prose. +- Do not invent author identity inside the message. +- If the repository requires trailers such as `Signed-off-by`, use Git's + configured identity and built-in support such as `git commit -s` instead of + typing made-up author lines by hand. + +## PR titles + +PR titles must always use Conventional Commit format. + +Examples: + +- `fix(api): handle empty webhook payload` +- `feat(cli): add JSON output for status` +- `docs: clarify release process` + +Because the branch is squash-merged, the PR title is the final commit title on +the target branch. Treat the PR title as merge-ready, not as a temporary review +label. + +## PR bodies + +There is no universal PR body template worth enforcing rigidly. Keep the body +useful to reviewers and future readers. In most repos, a concise PR body should +cover: + +- summary of the change +- linked issue or context +- testing or validation performed +- any rollout, migration, or reviewer notes + +## Branch work cadence + +- Commit early when you establish a direction. +- Commit again when a piece of work becomes testable or reviewable. +- Commit after meaningful fixes during review. +- Do not hold a large pile of unstaged work just to chase an ideal commit graph. + +The branch history is allowed to be messy as long as it is understandable. The +main branch history is cleaned up by squash merge. diff --git a/.agents/skills/journal-sync/SKILL.md b/.agents/skills/journal-sync/SKILL.md new file mode 100644 index 0000000..2306c30 --- /dev/null +++ b/.agents/skills/journal-sync/SKILL.md @@ -0,0 +1,126 @@ +--- +name: journal-sync +description: Synchronize remote teammate journal branches into local Worktrunk worktrees for read-only context discovery. Invoke only when the user explicitly asks to sync, fetch, refresh, or discover team journals. Never run automatically during startup or another lifecycle command. +--- + +# Journal Sync + +Mirror teammates' published journals locally without authoring journal content. +Canonical branch names are `journal/`; paths such as +`.wt/journal-` are Worktrunk-sanitized worktree paths, not branch +names. + +## Guardrails + +- Exclude the current user's `journal/` branch from sync decisions, + per-worktree Git status inspection, worktree creation, and pulls. The shared + Worktrunk inventory may be used only to report whether it exists; + `session-setup` owns that personal worktree. +- Treat every teammate journal worktree as a read-only reference mirror. Never + edit journal files or stage, commit, push, rebase, stash, reset, clean, + force-update, delete, or prune a local branch or worktree during sync. Fetch + may prune stale `origin/journal/*` remote-tracking refs only. +- Leave local journal branches and worktrees that no longer exist remotely in + place. Report them as retained; cleanup is outside this skill. +- Isolate failures by branch. A dirty, ahead, diverged, or broken peer must not + prevent other peers from syncing. + +## Workflow + +1. Resolve and verify prerequisites: + + ```bash + command -v wt + gh auth status + login=$(gh api user --jq .login) + personal="journal/$login" + git remote get-url origin + wt config show --full + ``` + + Require the Worktrunk path template + `{{ repo_path }}/.wt/{{ branch | sanitize }}`. Stop the entire operation if + identity, `origin`, Worktrunk, or its path configuration cannot be resolved; + the personal branch cannot otherwise be excluded safely. + +2. Query the remote directly and retain that output as the authoritative sync + snapshot: + + ```bash + git ls-remote --heads origin 'refs/heads/journal/*' + ``` + + Strip `refs/heads/` to obtain branch names. Accept canonical + `journal/` refs only; report malformed nested or empty-owner refs + without creating worktrees for them. Exclude `$personal` before inspecting + any worktree. + +3. Fetch and prune only the journal remote-tracking namespace, then inventory + local branches and worktrees: + + ```bash + git fetch origin --prune \ + '+refs/heads/journal/*:refs/remotes/origin/journal/*' + wt list --format=json --branches + ``` + + Use only branches from the remote snapshot for sync decisions; stale local + remote-tracking refs must not masquerade as current remote branches. Compare + the inventory with the snapshot so local peer branches or worktrees whose + remote disappeared can be reported as retained without touching them. + + Use the inventory only to determine whether the exact personal worktree is + present. If it is missing, report that the user should run `session-setup`, + but continue syncing peers. Do not create, inspect the status of, or pull the + personal worktree. + +4. For each teammate branch in the snapshot, locate its exact worktree in the + Worktrunk JSON. If none exists, create or open its tracking worktree: + + ```bash + wt switch --no-cd --no-hooks --format=json -y "journal/" + ``` + + Take the path from Worktrunk's JSON rather than constructing it. If creation + fails, refresh `wt list --format=json --branches` once: another process may + have created the worktree concurrently. Continue when the exact branch now + has a worktree; otherwise record a branch-local failure. + + Hooks are disabled because repository hooks may author files or continue + asynchronously after Worktrunk returns, violating the read-only peer rule. + +5. Verify the worktree checks out the expected branch. Record its starting + `HEAD`, then inspect it without changing it: + + ```bash + git -C "" branch --show-current + git -C "" status --porcelain=v1 --untracked-files=all + git -C "" rev-list --left-right --count \ + HEAD...refs/remotes/origin/journal/ + ``` + + If the branch is wrong or status is non-empty, do not pull. Record the reason + and continue with the next teammate. Interpret the revision counts as + ` `; if the local-only count is nonzero, the branch + is ahead or diverged. Do not pull it. Record the counts and continue. + +6. Fast-forward from the exact remote branch and verify an exact mirror: + + ```bash + git -C "" -c core.hooksPath=/dev/null \ + pull --ff-only origin "journal/" + git -C "" rev-parse HEAD + git -C "" rev-parse "refs/remotes/origin/journal/" + ``` + + Success requires the two revisions to match. If the pull fails or a + concurrent change leaves the branch ahead/diverged, classify it again with + `git rev-list --left-right --count HEAD...refs/remotes/origin/journal/`, + leave it untouched, and continue. Never repair divergence in this skill. + Disable Git hooks for the pull so a peer fast-forward cannot run a local + `post-merge` hook that authors files. + +7. Return one aggregate report containing remote journals discovered, the + personal branch skipped, peer worktrees created, updated, already current, + retained because their remote disappeared, and blocked with exact reasons. + When no canonical teammate branches exist, return a successful no-op. diff --git a/.agents/skills/journal-sync/agents/openai.yaml b/.agents/skills/journal-sync/agents/openai.yaml new file mode 100644 index 0000000..6fb38b8 --- /dev/null +++ b/.agents/skills/journal-sync/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Journal Sync" + short_description: "Safely refresh teammate journal worktrees" + default_prompt: "Use $journal-sync to discover and safely refresh teammate journal worktrees." diff --git a/.agents/skills/session-close/SKILL.md b/.agents/skills/session-close/SKILL.md new file mode 100644 index 0000000..37a7241 --- /dev/null +++ b/.agents/skills/session-close/SKILL.md @@ -0,0 +1,92 @@ +--- +name: session-close +description: Close out a journal session. Invoke only when the user explicitly asks to close, wrap up, or end a session. Requires prior session-setup, commits work, opens PRs, waits for review, squash-merges, updates local default branches, then writes SUMMARY.md and updates INDEX.md in the developer's personal journal branch per .session.md. Accepts an optional session ID argument; without one it closes the current session bound to this task. +argument-hint: [session-id] +disable-model-invocation: true +--- + +The user is **closing a session**. Follow the session protocol defined in `.session.md` at the workspace root — do not restate it, follow it. + +The close-out has three phases: **verify the personal journal worktree**, **land the work**, then **record the session**. Do them in order. Do not write `SUMMARY.md` before the PRs are merged — the summary is a postmortem of what actually happened, not what you intend to happen. + +--- + +## Phase 0 — Verify the personal journal worktree + +Resolve the developer identity with `gh api user --jq .login`, then locate an +existing Worktrunk worktree for `journal/` with `wt list --format=json` +and use that worktree as the journal root. If no worktree exists, stop and tell +the developer to run `session-setup` before closing a session. Do not create the +journal branch here. + +Verify `.journal/INDEX.md`, `.journal/SKILLS.md`, and `.journal/TECH_NOTES.md` +exist. If the root journal files are missing, stop and tell the developer to +rerun `session-setup`. + +Resolve the session being closed before landing work: + +- If the user provided a numeric session ID, normalize it to the zero-padded + folder name (`1` -> `001`) and use that session for this close operation. An + explicit ID selects the close target without replacing the current task-local + binding; if it names a different session, preserve the existing binding + unchanged. +- If no ID was provided, use the current session binding established earlier in + this task by a successful `session-new` or `session-continue`. +- If no ID was provided and this task has no current session binding, stop and + ask the user for an explicit session ID. Do not inspect or count + `in-progress` rows or incomplete folders to choose a target: those sessions + may belong to other concurrent tasks. +- Verify that `.journal//NOTES.md` exists and that its `INDEX.md` row is + `in-progress` before continuing. (`SUMMARY.md` may still exist after a session + was reopened, so it is not the status authority.) If the resolved target is + invalid, missing, or already closed, report that exact target condition and + do not fall through to another session. + +Read the resolved target's `.journal//NOTES.md` in full before landing work; +it defines which repos and branches belong to the closeout. If a reopened +session still has a prior `SUMMARY.md`, read it as historical context while +treating the current `INDEX.md` status and `NOTES.md` as authoritative. + +Treat both the task-local binding and the resolved close target as durable task +context. Preserve them across intermediate turns, context summarization, and +the Phase 1 pause for PR review. When the user returns after review, continue +closing the already resolved target instead of resolving it again. + +Apply the concurrent journal ownership rule from `.session.md`. The default +write set is `.journal//`; dirty paths in every other session folder are +expected, non-blocking parallel work and must remain untouched. Do not run +`pull --rebase` merely to prepare the shared journal worktree. + +## Phase 1 — Land the work + +For **every repo** with uncommitted changes or an unmerged branch that belongs to the target session, walk through these steps. Leave unrelated active-session branches alone. Do not skip a target-session repo because "nothing really changed" — run `git status` and verify. + +1. **Reject journal contamination.** In every non-journal implementation branch, run `git ls-files .journal`. If it prints any tracked file, stop and remove those files from the implementation branch before continuing. Only `journal/` may force-add `.journal/` paths. +2. **Commit all changes.** In each worktree that has work, stage and commit with a clear message. If there are logically distinct changes, use multiple commits. Do not commit unrelated changes together (see the Worktrunk rule in `.session.md`: one worktree per PR). +3. **Push the branch.** `git push -u origin HEAD` if the branch has no upstream; `git push` otherwise. +4. **Open a PR.** Use `gh pr create` with a title and body that explain the change. If multiple repos have work, open one PR per repo (or per logical change, if a repo has more than one). Surface the PR URL to the user. +5. **Wait for user review.** After opening each PR, **stop and wait** for the user to review. Preserve the resolved close target and current task-local binding while paused. Do not proceed to merge on your own. The user will indicate when a PR is approved and ready to merge — possibly after requesting changes that you then address in additional commits. Never merge unreviewed. +6. **Squash-merge after approval.** Once the user approves, merge with `gh pr merge --squash --delete-branch`. **Always squash.** Never use `--merge` or `--rebase`. This applies to every PR in every repo for this session, without exception. +7. **Update the local default branch.** After merge, in the main (non-worktree) checkout of that repo, detect the default branch with `git remote show origin | sed -n 's/ HEAD branch: //p'` (do not assume `main` or `master`), then fetch and fast-forward: `git fetch origin && git checkout && git pull --ff-only`. If fast-forward fails, stop and surface the problem to the user rather than resolving via merge or reset. +8. **Remove the session worktree.** Once the branch is merged and the local default is updated, clean up the Worktrunk worktree with `wt remove` (see the `worktrunk` skill). Do not leave stale worktrees. + +Repeat for each repo. Only once **every** PR for the session is merged and **every** local default branch is fast-forwarded should you move to Phase 2. + +If the user decides to abandon rather than merge one of the PRs, close it with `gh pr close` and delete the branch; record the abandonment explicitly in `SUMMARY.md` under Outcome or Open Threads. + +## Phase 2 — Record the session + +Only after Phase 1 is complete: + +1. **Finalize the write set before writing anything.** It always contains `.journal//` and `.journal/INDEX.md`; add `.journal/TECH_NOTES.md` only if the session produced durable technical context. Require each shared file in this write set—not the entire worktree—to have no pre-existing uncommitted edits. Dirty paths outside this write set remain non-blocking. +2. **Write `SUMMARY.md`** at `/.journal//SUMMARY.md` using `references/session-artifacts.md`. It is a postmortem written for another agent reading cold — cover goal, outcome (state plainly whether it was met), key decisions with reasons, changes, open threads, and references. +3. **Update `/.journal/INDEX.md`.** Add or update the row for this session. Set `Status` to `complete` (or `abandoned` if the work was dropped). Keep the summary cell to one sentence. Rows stay ordered oldest → newest. +4. **Update `/.journal/TECH_NOTES.md` if it is in the write set.** Revise it so future agents inherit the durable context. Keep it small; do not copy the session log into it. +5. **Append a final `NOTES.md` entry** using `references/session-artifacts.md`, pointing at the merged PRs and summarizing the hand-off state. `NOTES.md` is append-only — do not rewrite prior entries. +6. **Commit and push the journal mutation.** Stage only the write set with `git add -f -- `, then commit only it with `git commit --only -m "docs(journal): close session " -- `. Push `journal/` and use the bounded rejected-push retry from `.session.md`. Never include another session's dirty or staged files. +7. **Update the task-local binding.** Only after the closeout journal mutation is committed and pushed successfully, clear the current binding if it equals ``. If an explicit ID selected a different session, preserve the existing binding unchanged. Do not clear or switch the binding when closeout fails or remains paused. +8. **Confirm to the user** what was recorded: the session ID, the PRs merged, the repos whose local default was updated, the fact that `SUMMARY.md` and `INDEX.md` were written, whether `TECH_NOTES.md` was updated, which journal branch was pushed, and whether the current task-local binding was cleared or preserved. + +--- + +Do not skip the review-and-wait step in Phase 1. The whole point of opening PRs instead of merging locally is that the user sees the changes on GitHub before they land. Merging without explicit approval is a principle violation even if the change looks obviously correct. diff --git a/.agents/skills/session-close/references/session-artifacts.md b/.agents/skills/session-close/references/session-artifacts.md new file mode 100644 index 0000000..8c747c8 --- /dev/null +++ b/.agents/skills/session-close/references/session-artifacts.md @@ -0,0 +1,68 @@ +# Session Close Artifacts + +Use this reference when writing closeout artifacts. + +## SUMMARY.md + +```markdown +--- +id: 001 +title: Initial repo setup +date: 2026-04-15 +status: complete +repos_touched: [this-repo] +related_sessions: [] +--- + +## Goal +What this session set out to do, in 1-3 sentences. + +## Outcome +What actually happened. State plainly whether the goal was met, partially met, +or abandoned. + +## Key Decisions +- Decision -> reason. One bullet each. Non-obvious calls only. + +## Changes +- `path/to/file` - what changed and why +- Cross-repo changes listed with repo prefix, e.g. `other-repo/cmd/foo/main.go` + +## Open Threads +- Anything deferred, unresolved, or intentionally left for a future session. + +## References +- Links to PRs, docs, prior sessions (`.journal/000/SUMMARY.md`), external material. +``` + +Add `## Lessons` only when the session produced non-obvious learning a future +agent would benefit from. + +## INDEX.md + +```markdown +# Session Journal + +| ID | Date | Title | Status | Summary | +|-----|------------|-------|--------|---------| +``` + +Rows stay ordered oldest to newest. New sessions start as `in-progress`; +closeout updates the matching row to `complete` or `abandoned`. Keep the summary +cell to one sentence. + +## TECH_NOTES.md + +```markdown +# Technical Notes + +- Compact durable project-specific context. +``` + +This file is editable, not append-only. Revise it only when the session changed +technical context future agents should inherit. + +## Final NOTES.md Entry + +Append a final `## YYYY-MM-DD HH:MM — Close` entry pointing at merged PRs and +summarizing the handoff state. Do not rewrite earlier notes. diff --git a/.agents/skills/session-continue/SKILL.md b/.agents/skills/session-continue/SKILL.md new file mode 100644 index 0000000..ddb2345 --- /dev/null +++ b/.agents/skills/session-continue/SKILL.md @@ -0,0 +1,26 @@ +--- +name: session-continue +description: Continue an existing journal session by ID. Invoke only when the user explicitly asks to continue a specific session. Requires prior session-setup, then takes one argument, the session ID (e.g. "1", "003"). The argument follows the skill name in the user's invocation. +argument-hint: +disable-model-invocation: true +--- + +The user is **continuing an existing session** in this workspace. The session ID is passed as the argument to this skill — treat any numeric argument the user provided as the session ID, resolving it against the zero-padded folder names under the journal root's `.journal/` (e.g. `1` → `001`, `12` → `012`). + +Follow the session protocol defined in `.session.md` at the workspace root — do not restate it, follow it. + +Specifically: + +1. **Verify session setup (mandatory, first):** Resolve the developer identity with `gh api user --jq .login`, then locate an existing Worktrunk worktree for `journal/` with `wt list --format=json`. If no worktree exists, stop and tell the developer to run `session-setup` before continuing a session. Do not create the journal branch here. +2. **Resolve the target session:** Resolve the user-provided session ID to the matching `/.journal//` folder. If the folder does not exist, stop and ask the user to clarify before doing anything else. The explicit ID is authoritative; other open sessions are expected and must not be surfaced as warnings or ambiguity. The default write set is only `.journal//`. +3. **Prepare the journal root:** Verify `.journal/INDEX.md`, `.journal/SKILLS.md`, and `.journal/TECH_NOTES.md` exist. If they are missing, stop and tell the developer to rerun `session-setup`. Apply the concurrent journal ownership rule from `.session.md`; dirty paths outside the target write set are expected and non-blocking. +4. **Startup:** Read `/.journal/SKILLS.md` if present and load every required skill listed there. Read `/.journal/TECH_NOTES.md` if present. Then read the `SUMMARY.md` of the last three closed sessions in `/.journal/` (skip sessions without a `SUMMARY.md`; read fewer if fewer exist). Do **not** read their `NOTES.md` files at this step. This startup read is required in addition to the continuing-session reads below. +5. **Resume the target session:** + - Read `/.journal//NOTES.md` **in full**, top to bottom. This is your primary context for the session. + - Read `/.journal//SUMMARY.md` if it exists (the session may have been closed and is being reopened). +6. **Log the resume:** Determine first whether `/.journal/INDEX.md` needs an `in-progress` update. If it does, require only `INDEX.md` to have no pre-existing uncommitted edits and add it to the write set. Then append a new `## — Resume` entry to `NOTES.md` stating your understanding of the current state and what you're about to do, and add or update the `INDEX.md` row when required. If the session was previously `complete` or `abandoned`, say in the resume entry that it was reopened. +7. **Record the journal mutation:** Stage only the write set with `git add -f -- `, then commit only it with `git commit --only -m "docs(journal): resume session " -- `. Push `journal/` and use the bounded rejected-push retry from `.session.md`. Never include another session's dirty or staged files. +8. **Bind the current session:** After the resume mutation is committed and pushed successfully, bind `` as the current session for this task. Replace any prior task-local binding without modifying or warning about the previously bound session. Do not change the binding if target validation or the resume mutation fails. +9. Confirm to the user that the session is resumed, that it is now the current session for this task, and which journal branch was updated, then wait for their actual request. + +Do not proceed with any substantive work until the resume entry is logged. diff --git a/.agents/skills/session-new/SKILL.md b/.agents/skills/session-new/SKILL.md new file mode 100644 index 0000000..c47b8f4 --- /dev/null +++ b/.agents/skills/session-new/SKILL.md @@ -0,0 +1,30 @@ +--- +name: session-new +description: Start a new journal session in this workspace. Invoke only when the user explicitly asks to start a new session (e.g. "new session", "start a session"). Requires prior session-setup, then primes the next .journal// folder in the developer's personal journal worktree per .session.md. Does not accept arguments. +disable-model-invocation: true +--- + +The user is starting a **new session** in this workspace. Follow the session protocol defined in `.session.md` at the workspace root — do not restate it, follow it. + +An explicit new-session request is authoritative. Start a new session even when +other sessions are already `in-progress`; their existence is expected and must +not be surfaced as a warning, reminder, ambiguity, or request for confirmation. +Do not reuse, continue, close, or otherwise modify another session instead. + +Specifically: + +1. **Verify session setup (mandatory, first):** Resolve the developer identity with `gh api user --jq .login`, then locate an existing Worktrunk worktree for `journal/` with `wt list --format=json`. If no worktree exists, stop and tell the developer to run `session-setup` before starting a session. Do not create the journal branch here. +2. **Prepare the journal root:** Verify `.journal/INDEX.md`, `.journal/SKILLS.md`, and `.journal/TECH_NOTES.md` exist. If they are missing, stop and tell the developer to rerun `session-setup`. Apply the concurrent journal ownership rule from `.session.md`: dirty active-session folders are expected, do not block startup, and must not be checkpointed or committed by this operation. +3. **Startup:** Read `/.journal/SKILLS.md` if present and load every required skill listed there. Read `/.journal/TECH_NOTES.md` if present. Then read the `SUMMARY.md` of the last three closed sessions in `/.journal/` (skip sessions without a `SUMMARY.md`; read fewer if fewer exist). Do **not** read their `NOTES.md` files. +4. **Prime the new session:** Read `references/notes-template.md`, then: + - Find the highest existing session ID under `/.journal/` and increment by 1 (zero-padded, 3 digits). If no session folders exist, start at `001`. + - Define the write set as `.journal//` plus `.journal/INDEX.md`. Before creating the session, require only `INDEX.md`—not the entire worktree—to have no pre-existing uncommitted edits. + - Create `/.journal//` as the allocation attempt. If the candidate already exists or appears during creation because another concurrent `session-new` won the race, recompute the highest ID, update `` and the write set, repeat the scoped `INDEX.md` check, and retry the allocation once. If that second candidate is also taken, stop on the actual allocation collision. Never touch the session folder that won the race. + - Create `/.journal//NOTES.md` from the template, then append an initial `## — Kickoff` entry capturing the user's stated goal and the current state of the world. + - Do **not** create `SUMMARY.md` — that's written at session close. + - Add an `in-progress` row for the new session to `.journal/INDEX.md`. Derive a short title from the user's stated goal, use today's date, keep rows ordered oldest to newest, and keep the summary cell to one sentence. +5. **Record the journal mutation:** Stage only the write set with `git add -f -- .journal/ .journal/INDEX.md`, then commit only it with `git commit --only -m "docs(journal): start session " -- .journal/ .journal/INDEX.md`. Push `journal/` and use the bounded rejected-push retry from `.session.md`. Never include another session's dirty or staged files. +6. **Bind the current session:** After the journal mutation is committed and pushed successfully, bind `` as the current session for this task. Replace any prior task-local binding without modifying or warning about the previously bound session. Do not change the binding if priming fails. +7. Confirm to the user which session ID was created, that it is now the current session for this task, and which journal branch was updated, then wait for their actual request. + +Do not proceed with any substantive work until priming is complete. diff --git a/.agents/skills/session-new/references/notes-template.md b/.agents/skills/session-new/references/notes-template.md new file mode 100644 index 0000000..f8b3610 --- /dev/null +++ b/.agents/skills/session-new/references/notes-template.md @@ -0,0 +1,25 @@ +# NOTES.md Template + +Use this shape when `session-new` creates a session log. + +```markdown +--- +id: 001 +title: Initial repo setup +started: 2026-04-15 +--- + +## 2026-04-15 10:20 — Kickoff +Goal for the session: . +Current state of the world: . +Plan: . +``` + +Rules: + +- `NOTES.md` is append-only. +- Timestamp headings use `## YYYY-MM-DD HH:MM — short label` in the user's + local time. +- Capture what was done, what was learned, and what is next. +- If an earlier note is wrong, append a correction later. +- Do not create `SUMMARY.md`; `session-close` writes it. diff --git a/.agents/skills/session-setup/SKILL.md b/.agents/skills/session-setup/SKILL.md new file mode 100644 index 0000000..6928e7d --- /dev/null +++ b/.agents/skills/session-setup/SKILL.md @@ -0,0 +1,68 @@ +--- +name: session-setup +description: First-run developer onboarding for the session journal system in a repository that already has the framework installed. Creates or opens the developer's personal journal branch, initializes .journal, pushes it, and explains how to use sessions. Invoke when the user asks for session setup, setup sessions, onboarding, or first-time journal setup. +disable-model-invocation: true +--- + +The user is asking to set up their developer journal for this workspace. Follow +the protocol defined in `.session.md` at the workspace root — do not restate it, +follow it. + +This is the only skill that creates or initializes the developer's personal +journal branch. The personal branch is exactly `journal/`, where +`` is the current result of `gh api user --jq .login`. + +Specifically: + +1. **Verify prerequisites before changing anything:** + - `command -v wt` + - `gh auth status` + - `gh api user --jq .login` + - `git remote get-url origin` + - `git remote show origin | sed -n 's/ HEAD branch: //p'` + - `wt config show --full` must show `worktree-path = "{{ repo_path }}/.wt/{{ branch | sanitize }}"` + If any prerequisite fails, stop and tell the developer what is missing. Do + not install tools, run `gh auth login`, or edit Worktrunk config yourself. +2. **Resolve setup values:** + - GitHub login from `gh api user --jq .login` + - default branch from `git remote show origin` + - personal journal branch `journal/` + - Treat only that exact branch as the writable setup and lifecycle target. + Every `journal/` branch belongs to a peer and must not be used + as a fallback personal journal root. +3. **Fetch and inspect:** + - Run `git fetch origin --prune`. + - Run `wt list --format=json`. + - Ignore peer journal worktrees when resolving the personal journal root. + They are read-only context sources; only `journal-sync` may materialize or + fast-forward them. + - If an existing worktree for `journal/` is present, use that as the journal root. Apply the concurrent journal ownership rule from `.session.md`: setup does not own active session folders, must ignore their dirty paths, and must not checkpoint or commit them. Do not run `pull --rebase` merely to prepare the shared journal worktree. +4. **Open an existing journal branch if needed:** + - If no worktree exists but `origin/journal/` exists, open it with + `wt switch --no-cd --format=json -y journal/`. Worktrunk creates the + local tracking branch when it exists only on the fetched remote. + - Apply the same scoped setup ownership in that worktree. +5. **Create the journal branch if needed:** + - If neither a worktree nor `origin/journal/` exists, create one with `wt switch --create --base origin/ --no-cd --format=json journal/`. + - If the current checkout has an ignored local `.journal/`, copy its contents into the new journal worktree without overwriting existing files, for example with `rsync -a --ignore-existing .journal/ /.journal/`. +6. **Bootstrap and publish setup state:** + - In the journal worktree, ensure `.journal/` exists. + - Bootstrap any missing `.journal/INDEX.md`, `.journal/SKILLS.md`, and `.journal/TECH_NOTES.md` from the repo's scaffold files. + - Define the setup write set as only the scaffold or imported paths created by this setup. Dirty paths outside it are expected concurrent work and must remain untouched. + - If the setup write set changed, stage it with `git add -f -- `, commit only it with `git commit --only -m "docs(journal): initialize journal for " -- `, and push with upstream set to `origin/journal/`. On a rejected push, follow the bounded retry rule in `.session.md`. + - If nothing changed, do not create an empty commit; confirm the existing journal branch is ready. +7. **Confirm and orient the developer.** End with a concise onboarding note in your own words that covers: + - Sessions preserve agent context across days and teammates. + - The default branch stays clean; their journal lives on `journal/`. + - Use sessions for substantial implementation, multi-step research, architecture work, or anything another agent may need to resume. + - Do not use sessions for quick questions or one-off commands. + - Start with `new session`, resume with `continue session `, and close with `session close`. + - Teammate journal branches are read-only context sources, not implementation + bases or lifecycle targets; only `journal-sync` updates their local + worktrees. + - What setup just did, including the journal branch and journal worktree path. + +Do not create a new session during setup unless the user explicitly asks for one +after setup is complete. +Do not invoke `journal-sync` during setup unless the user separately and +explicitly asks for journal synchronization. diff --git a/.agents/skills/worktrunk/SKILL.md b/.agents/skills/worktrunk/SKILL.md new file mode 100644 index 0000000..9410179 --- /dev/null +++ b/.agents/skills/worktrunk/SKILL.md @@ -0,0 +1,254 @@ +--- +name: worktrunk +description: > + Manage Git worktrees with Worktrunk (`wt`) for parallel human and agent workflows. + Use when a repository uses `wt`, `.config/wt.toml`, or worktree-based branch isolation, + or when you need to create, inspect, clean up, or coordinate isolated worktrees safely. + Prefer PR-based integration with `gh pr`; do not default to Worktrunk's local merge flow. +--- + +# Worktrunk + +Use this skill as an operator guide for multi-worktree, multi-agent Git workflows. +Ground advice in the official docs and the local CLI help, not memory. + +## Verified against + +- Docs: https://worktrunk.dev +- GitHub: https://github.com/max-sixty/worktrunk +- Local CLI used for command grounding: `wt 0.37.1` + +## Use this skill when + +- A repository already uses Worktrunk or has a `.config/wt.toml`. +- You need to give each agent or user an isolated branch and worktree. +- You need to inspect existing worktrees before starting so changes do not collide. +- You need to explain or operate `wt switch`, `wt list`, `wt remove`, `wt config`, + `wt step`, or hooks. +- You need a PR-oriented workflow around worktrees. + +## Default stance + +1. Give each user or agent its own implementation branch and worktree. Do not share an + implementation working directory. The exact `journal/` worktree + governed by `.session.md` is the deliberate exception: the current user's concurrent + agents share it while owning disjoint journal write sets. Other `journal/` + worktrees are read-only peer references; only `journal-sync` may materialize or + fast-forward them. +2. Use `wt` for creation, switching, inspection, and cleanup so naming, hooks, and lifecycle + stay consistent. +3. Prefer remote PR flow for integration: + - push the branch with normal Git + - create and manage review with `gh pr` +4. Do not default to `wt merge` or `wt step push`. + - `wt merge` is a local integration pipeline that rebases, updates the local target branch, + and usually removes the worktree. + - `wt step push` fast-forwards the local target branch. It is not `git push origin`. +5. Use `wt remove` and `wt step prune` only after review or after the PR is merged or abandoned. + +## Non-negotiables for agents + +- Before creating a branch, inspect existing worktrees first. +- Never assume your current directory is isolated. Verify it. +- Do not reuse another agent's implementation worktree unless the user explicitly asks for + that handoff. Sharing a personal journal worktree is allowed only under the scoped + ownership rules in `.session.md`. +- Resolve the current GitHub login before treating any journal worktree as personal. Never + write lifecycle state in, reuse, or repurpose a peer's `journal/` worktree; + ordinary agents may only read it for context, and only `journal-sync` may update it. +- Avoid interactive picker flows in automation; prefer explicit branch names and JSON output. +- If shell integration is not installed, `wt switch` cannot change the parent shell's + directory. Run `wt config shell install` once per machine, or use `--no-cd` plus the + returned path/JSON in automation. + +## Fast inspection workflow + +Run these first when you are orienting yourself in a repo: + +```bash +wt --version +git rev-parse --show-toplevel +git branch --show-current +wt list --format=json +wt config show --full +``` + +Use `wt list --format=json` for tool-driven inspection. Do not scrape the table output unless +you have to. + +## Safe agent workflow + +### 1. Inspect current state + +Start by checking whether the branch or worktree already exists: + +```bash +wt list --format=json +wt list --format=json --branches +``` + +Look for: + +- `is_current`: where you are now +- `branch` and `path`: existing worktree ownership and location +- `kind == "branch"`: branches that exist without worktrees +- `symbols` / `main_state`: whether a branch is dirty, ahead, integrated, conflicted, or safe + to prune + +If the target branch already has a live worktree, treat that as owned until the user says +otherwise. + +For journal branches, ownership is determined by the exact current GitHub login, not by +which journal worktree happens to be current. A peer journal worktree remains a read-only +reference even when the agent starts inside it. + +### 2. Create an isolated worktree + +Use a unique branch name. Good patterns include ticket IDs or explicit agent prefixes. + +```bash +wt switch --create feature/some-task +wt switch --create --base main feature/some-task +wt switch --create --base @ fix/current-context +``` + +Notes: + +- `--create` creates a new branch. +- `--base` defaults to the repo's default branch. +- `@` means the current branch/worktree, `^` means the default branch, and `-` means the + previous worktree. +- If the branch already exists, `wt switch ` switches to its worktree or creates one + for that branch. + +For agent launchers, `wt switch -x ` can create the worktree and hand control to the +tool immediately. + +### 3. Work only inside that worktree + +Once the worktree exists: + +- make edits only there +- run tests there +- commit there +- avoid switching the branch in-place with `git switch` unless the user explicitly wants to + repurpose that worktree + +These edit rules apply to implementation worktrees and to the current user's personal +journal under `.session.md`. Never perform implementation or lifecycle work in a peer +journal worktree; peer journals are reference-only outside `journal-sync` fast-forwards. + +Optional coordination helpers: + +- `wt config state marker set ` can label a branch manually +- `wt list` surfaces markers, ahead/behind state, and optional CI/summary data + +### 4. Update your branch without using local merge flow + +If the base branch moved, refresh it first, then rebase your branch as needed. + +Use Worktrunk's rebase helper only when you mean "rebase my current branch onto the target +branch locally": + +```bash +wt step rebase +wt step rebase develop +``` + +This is fine for keeping a worktree current. It is not the same as merging back to the source +repo. + +### 5. Use normal Git push and `gh pr` for integration + +Default integration flow: + +```bash +git push -u origin HEAD +gh pr create --fill +gh pr status +gh pr checks +gh pr view --web +``` + +Use `gh pr edit`, `gh pr comment`, `gh pr ready`, `gh pr reopen`, and related `gh pr` +commands for ongoing review management. + +For existing PRs, Worktrunk can still help you isolate the branch locally: + +```bash +wt switch pr:123 +``` + +That resolves the PR branch and opens or creates the corresponding worktree. It requires `gh` +to be installed and authenticated. + +### 6. Clean up after merge or abandonment + +After the PR is merged, or if the branch is intentionally discarded: + +```bash +wt remove +wt remove feature/some-task +wt step prune --dry-run +wt step prune +``` + +Routine implementation cleanup must exclude every `journal/*` worktree. Do not reuse, +repurpose, or remove peer journal worktrees, and do not run `wt step prune` when its dry run +would remove one. The session protocol manages the current user's journal, while +`journal-sync` manages peer reference worktrees. + +Important details: + +- `wt remove` defaults to the current worktree. +- It deletes the branch only when Worktrunk determines it is merged or otherwise integrated, + unless you force or override that behavior. +- `wt step prune` bulk-removes integrated worktrees and branches. Review with `--dry-run` + first. + +## What to avoid by default + +- `wt merge` +- `wt step push` +- "just merge it back locally" advice + +Why: + +- PRs preserve review, CI, audit trail, and shared branch protection rules. +- Local merge flows are easier to misuse in multi-agent setups because they update local target + branches directly. +- `wt merge` also tends to clean up immediately, which can be the wrong default while a review + is still active. + +Only recommend `wt merge` if the user explicitly wants a local-only integration workflow and +understands that it is not the same as GitHub PR merge. + +## Command reference + +See [references/commands.md](references/commands.md) for the current command map and common +flags. + +## Maintenance checklist + +Re-verify this skill when Worktrunk releases a new minor or major version: + +1. Re-open: + - https://worktrunk.dev/switch/ + - https://worktrunk.dev/list/ + - https://worktrunk.dev/remove/ + - https://worktrunk.dev/merge/ + - https://worktrunk.dev/config/ + - https://worktrunk.dev/step/ + - https://worktrunk.dev/hook/ + - https://worktrunk.dev/claude-code/ +2. Re-run locally: + - `wt --version` + - `wt --help` + - `wt switch --help` + - `wt list --help` + - `wt remove --help` + - `wt merge --help` + - `wt config --help` + - `wt step --help` + - `wt hook --help` +3. Delete or rewrite any recommendation that can no longer be grounded in the docs or CLI help. diff --git a/.agents/skills/worktrunk/references/commands.md b/.agents/skills/worktrunk/references/commands.md new file mode 100644 index 0000000..23a826a --- /dev/null +++ b/.agents/skills/worktrunk/references/commands.md @@ -0,0 +1,245 @@ +# Worktrunk Command Map + +This is a curated operator reference for `wt 0.37.1`. Prefer local `--help` output and +official docs if anything here drifts. + +## Global flags + +These apply across `wt` commands: + +- `-C `: run as if started in another working directory +- `--config `: use a specific user config file +- `-v`, `-vv`: increase logging verbosity + +## Core lifecycle commands + +### `wt switch` + +Purpose: switch to a branch's worktree, or create a new branch/worktree when asked. + +Usage: + +```bash +wt switch [OPTIONS] [BRANCH] [-- ...] +``` + +Common flags: + +- `-c`, `--create`: create a new branch +- `-b`, `--base `: base branch for `--create`; defaults to the default branch +- `-x`, `--execute `: replace `wt` with another command after switching +- `--clobber`: remove a stale non-worktree path at the target location +- `--no-cd`: skip directory change after switching +- `--branches`: in picker mode, include branches without worktrees +- `--remotes`: in picker mode, include remote branches +- `-y`, `--yes`: skip approval prompts +- `--no-hooks`: skip hooks +- `--format text|json`: structured output for automation + +Branch shortcuts: + +- `^`: default branch +- `-`: previous worktree +- `@`: current worktree +- `pr:`: GitHub PR branch +- `mr:`: GitLab MR branch + +Notes: + +- With no branch argument, `wt switch` opens an interactive picker. +- After remotes have been fetched, an explicit `wt switch ` also works when the + branch exists only on a single remote: Worktrunk creates the local tracking branch and its + worktree. Do not pass `--create` for that case. +- `pr:` requires `gh`; `mr:` requires `glab`. +- For automation, prefer explicit branch names or `--format=json`. + +### `wt list` + +Purpose: inspect worktrees, branch state, divergence, and optional CI/summary data. + +Usage: + +```bash +wt list [OPTIONS] +wt list statusline +``` + +Common flags: + +- `--format table|json`: default is `table` +- `--branches`: include branches without worktrees +- `--remotes`: include remote branches +- `--full`: include CI status, diff analysis, and LLM summaries +- `--progressive`: render fast local data first, then remote/slow data + +Notes: + +- `wt list --format=json` is the best entrypoint for agents. +- `statusline` is a subcommand for shell/status integrations. + +### `wt remove` + +Purpose: remove a worktree and optionally delete its branch. + +Usage: + +```bash +wt remove [OPTIONS] [BRANCHES]... +``` + +Common flags: + +- `--no-delete-branch`: keep the branch after removal +- `-D`, `--force-delete`: delete an unmerged branch +- `-f`, `--force`: remove worktree even with untracked files +- `--foreground`: run removal synchronously +- `-y`, `--yes`: skip approval prompts +- `--no-hooks`: skip hooks +- `--format text|json` + +Notes: + +- Defaults to the current worktree. +- Removal runs in the background by default. +- Worktrunk can detect squash-merged or otherwise integrated branches before deleting them. + +## Local integration commands + +Understand these, but do not recommend them as the default agent workflow. + +### `wt merge` + +Purpose: merge the current branch into the target branch locally, typically with squash, +rebase, and cleanup. + +Usage: + +```bash +wt merge [OPTIONS] [TARGET] +``` + +Common flags: + +- `--no-squash` +- `--no-commit` +- `--no-rebase` +- `--no-remove` +- `--no-ff` +- `--stage all|tracked|none` +- `-y`, `--yes` +- `--no-hooks` +- `--format text|json` + +Important: + +- This updates the local target branch, usually the default branch. +- This is not a GitHub PR merge. +- Prefer `git push` plus `gh pr create` / `gh pr status` / `gh pr checks` instead. + +### `wt step` + +Purpose: run lower-level lifecycle operations and utilities. + +Usage: + +```bash +wt step +``` + +Subcommands: + +- `commit`: stage and commit with an LLM-generated message +- `squash`: squash commits since branching +- `rebase`: rebase current branch onto target +- `push`: fast-forward the local target branch to current branch +- `diff`: show all changes since branching +- `copy-ignored`: copy gitignored files to another worktree +- `eval`: evaluate a template expression +- `for-each`: run a command in every worktree +- `promote`: swap a branch into the main worktree +- `prune`: remove integrated worktrees and branches +- `relocate`: move worktrees to expected paths + +Common subcommand flags: + +- `wt step commit --stage all|tracked|none --show-prompt` +- `wt step squash --stage all|tracked|none --show-prompt` +- `wt step rebase [TARGET]` +- `wt step push [TARGET] [--no-ff]` +- `wt step prune --dry-run --min-age --foreground` +- `wt step for-each -- ` + +Important: + +- `wt step push` is local target-branch integration. It is similar to + `git push . HEAD:`, not `git push origin`. +- `wt step prune --dry-run` is the safe preview before batch cleanup. + +## Configuration and hooks + +### `wt config` + +Purpose: manage shell integration, config files, plugins, and internal state. + +Subcommands: + +- `wt config shell install`: install shell integration so `wt switch` can change directories +- `wt config shell init`: print shell integration code +- `wt config shell uninstall` +- `wt config shell show-theme` +- `wt config create`: create user config +- `wt config create --project`: create `.config/wt.toml` +- `wt config show [--full] [--format text|json]` +- `wt config update`: update deprecated config settings +- `wt config plugins`: plugin management +- `wt config state`: inspect default branch, markers, vars, logs, CI cache, and more + +Useful `state` subcommands: + +- `default-branch` +- `previous-branch` +- `logs` +- `ci-status` +- `marker` +- `vars` + +### `wt hook` + +Purpose: run or inspect lifecycle hooks and manage approvals. + +Hook subcommands: + +- `show` +- `pre-switch`, `post-switch` +- `pre-start`, `post-start` +- `pre-commit`, `post-commit` +- `pre-merge`, `post-merge` +- `pre-remove`, `post-remove` +- `approvals` + +Notes: + +- `pre-*` hooks block and can abort an operation. +- `post-*` hooks run in the background. +- Hooks can live in user config or project config. + +## PR-oriented workflow to prefer + +```bash +wt list --format=json --branches +wt switch --create feature/some-task +# edit, test, commit +git push -u origin HEAD +gh pr create --fill +gh pr status +gh pr checks +``` + +Cleanup after merge: + +```bash +wt remove feature/some-task +# or, in batches: +wt step prune --dry-run +wt step prune +``` diff --git a/.gitignore b/.gitignore index 103a33d..23ab0f6 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,11 @@ docs/build/ docs/.venv/ examples/provenance/provenance +# Agent session protocol state +.agents/ +.journal/ +.wt/ + # Python __pycache__/ *.py[cod] diff --git a/.session.md b/.session.md new file mode 100644 index 0000000..4461035 --- /dev/null +++ b/.session.md @@ -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 ` binds `` 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 ` closes that explicitly named session. If `` 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/` worktree for the authenticated user. + +Resolve it in this order: + +1. If the current branch is `journal/`, use the current checkout. +2. If `gh api user --jq .login` succeeds and `wt list --format=json` shows a + worktree for `journal/`, 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/` 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/SKILLS.md` exists, load every required skill it + lists. +4. If `/.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/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/SKILLS.md`; keep that file as a short list, not a +manual. + +## Branch And Journal Invariants + +- Personal journal branches are named `journal/`. +- Journal branches are never PR'd or merged into the default branch. +- The default branch keeps `.journal/` ignored and untracked. +- Only a `journal/` 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//` folder and its `INDEX.md` update. +- `session-continue` owns `.journal//` and owns `INDEX.md` only when it must + reopen the session. +- `session-close` owns `.journal//`, 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 ... -- ` so unrelated +staged changes cannot enter the commit. New or ignored journal files may be +added with `git add -f -- `; never use broad staging outside fresh +setup or import. In commands, replace `` 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/ 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 +└── / + ├── 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//` 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. diff --git a/AGENTS.md b/AGENTS.md new file mode 120000 index 0000000..681311e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..048b0f4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,71 @@ +## Development Workflow + +### Git Commits + +All commits must follow the [Conventional Commits](https://www.conventionalcommits.org/) standard: + +``` +[optional scope]: + +[optional body] + +[optional footer(s)] +``` + +Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `build`, `ci` + +Examples: +- `feat: add zstd compression support` +- `fix(registry): handle auth token refresh` +- `docs: update API examples` + +### Pull Requests + +All changes must go through pull requests. Direct commits to `master` are not allowed. + +## Code Standards + +### Style + +All Go code must conform to the `go-style` skill. Key points: + +- Standard library idioms and types (`io.Reader`, `context.Context`, etc.) +- Proper error handling with wrapped context +- Clear package organization +- Consistent naming conventions + +### Testing + +All Go tests must conform to the `go-testing` skill. Key points: + +- Table-driven tests where appropriate +- Behavioral tests over implementation tests +- Proper use of `t.Helper()`, `t.Parallel()` +- Clear test naming + +### CI Requirements + +All code changes must pass CI checks before merging: + +```bash +just ci +``` + +This runs: +1. `golangci-lint` - Static analysis and formatting +2. `go test` - All tests must pass +3. `go build` - Code must compile + + +# 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. +