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
24 changes: 24 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env sh
msg_file="$1"
subject=$(head -n 1 "$msg_file")

case "$subject" in
Merge\ * | Revert\ * | fixup!* | squash!*) exit 0 ;;
esac

if ! printf '%s' "$subject" | grep -qE '^(([A-Z][A-Z0-9]*-[0-9]+|#[0-9]+) )?(feat|fix|refactor|docs|test|chore|style|perf)(\([^)]+\))?: .+'; then
echo "commit-msg: bad subject '$subject'" >&2
echo "commit-msg: expected 'TICKET type(SLUG): description' — see the Git memory section in CLAUDE.md" >&2
exit 1
fi

type=$(printf '%s' "$subject" | sed -E 's/^(([A-Z][A-Z0-9]*-[0-9]+|#[0-9]+) )?([a-z]+).*/\3/')
case "$type" in
feat | fix | refactor)
if ! sed -n '2,$p' "$msg_file" | grep -v '^#' | grep -q '[^[:space:]]'; then
echo "commit-msg: warning — $type commit without a body; if it carries a decision, amend with the why (Git memory, CLAUDE.md)" >&2
fi
;;
esac

exit 0
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ claude --plugin-dir .
## Conventions

- **Content language**: English by default. Skills adapt to the ticket/input language, or follow the project-level definition in CLAUDE.md / AGENTS.md.
- **Commits**: `TICKET type(SLUG): description`; a commit that carries a decision gets a prose body plus decision trailers (`Constraint:`, `Rejected:`, `Directive:`, `Related:`) — the git memory, see `skills/gca/reference/commit-convention.md`. Before modifying a file, read its history per `skills/gca/reference/history-reading.md`. Subject shape is enforced by `.husky/commit-msg`.
- **Files and directories**: kebab-case
- **Versioning**: semver in `plugin.json` (source of truth for the version)
- **marketplace.json**: required top-level fields — `name`, `owner` (object with `name`), `plugins[]`
Expand Down
31 changes: 29 additions & 2 deletions skills/bootstrap/agents/claude-md-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ You are the CLAUDE.md generator: create or extend the project's file.
- Non-obvious decisions and gotchas (extract from DOC_CONTENT — workarounds, limitations; from `DOMAIN_FINDINGS.CODE_WORKAROUNDS` — code-level workarounds)
- Workflows and processes (extract from DOC_CONTENT — CI/CD, deploy, release flow)
4. If DOMAIN_FINDINGS and DOC_CONTENT overlap, prefer DOC_CONTENT (don't duplicate).
5. Add an "Artifact root (`.yoke/`)" section (see below for required content).
5. Add the "Artifact root (`.yoke/`)" and "Git memory (commit convention)" sections (see below for required content).
6. Write CLAUDE.md to the project root

### 4. Enrich mode (CLAUDE_MD_EXISTS = true)
Expand All @@ -69,6 +69,7 @@ You are the CLAUDE.md generator: create or extend the project's file.
- Non-obvious — are gotchas/workarounds captured?
- Conventions — are project-specific conventions described?
- Artifact root — is the `.yoke/` layout documented? (add if absent)
- Git memory — is the commit convention documented? (add if absent)
3. For each missing section — add it via Edit, using DOC_CONTENT as the source:
- Project description — from README.md and other documentation files
- Non-obvious decisions and gotchas — from CONTRIBUTING.md, docs/
Expand Down Expand Up @@ -104,6 +105,32 @@ Yoke stores all AI-generated artifacts under `.yoke/` at the project root:

If the section already exists with equivalent content, do not duplicate it.

## Git memory (commit convention) section — required content

Every generated or enriched CLAUDE.md must also contain this section (place it
next to the Artifact root section). When the project already documents a
commit format, merge — keep the project's ticket/type specifics and add the
memory rules; do not leave two competing commit sections.

```markdown
## Git memory (commit convention)

Commit messages are this project's long-term decision memory, written for
coding agents as much as for people.

- Subject: `TICKET type(SLUG): description` — ticket first when one exists.
- A commit that carries a decision (chosen approach, rejected alternative,
non-obvious constraint) gets a prose body explaining **why**, then optional
decision trailers: `Constraint:`, `Rejected:`, `Directive:`, `Related:`.
Mechanical commits stay one-liners.
- Never add identity trailers (`Co-Authored-By`, `Signed-off-by`).
- Before modifying a file, read its memory: `git log -n 5 -- <path>`.
Respect `Constraint:`, heed `Directive:`, do not re-propose `Rejected:`
approaches without new evidence.
- A `commit-msg` hook validates the subject shape and warns when a
feat/fix/refactor commit lacks a body.
```

## Rules

- Preserve user content — sections added manually stay on update
Expand All @@ -114,7 +141,7 @@ If the section already exists with equivalent content, do not duplicate it.
- On create, use Write
- Don't verify commands — the verifier does that
- Reach Grade A against quality-criteria (90+ points)
- The "Artifact root (`.yoke/`)" section is mandatory in every output (create and enrich)
- The "Artifact root (`.yoke/`)" and "Git memory (commit convention)" sections are mandatory in every output (create and enrich)

## Response format

Expand Down
9 changes: 9 additions & 0 deletions skills/bootstrap/reference/bootstrap-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ Dispatch 3 agents **in parallel** via the Agent tool:

Result → RECOMMENDATIONS (list of automation recommendations).

4. **Git memory hook** (orchestrator, no agent) — after the agents return,
scaffold the `commit-msg` hook per `reference/hooks-patterns.md` § Git
memory commit-msg hook: into `.husky/commit-msg` when the project uses
husky, else `.git/hooks/commit-msg` (`chmod +x`). A `commit-msg` hook
already exists → do not touch it; record the fact in VERIFY_NOTES.

Wait for all 3. Mark in TodoWrite: `[x] Generate`. Transition → Phase 4.

---
Expand Down Expand Up @@ -406,6 +412,9 @@ git add CLAUDE.md .yoke/
git commit -m "chore: bootstrap yoke flow context and .yoke/ scaffold"
```

When Phase 3 wrote `.husky/commit-msg`, stage it too — `.git/hooks/` stays
local by nature and is never staged.

**Local-only** — `.yoke/` is gitignored; commit only the repo-level artifacts.
`.yoke/flow.md` and `.yoke/yoke-context.md` stay on disk, untracked:

Expand Down
43 changes: 43 additions & 0 deletions skills/bootstrap/reference/hooks-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,49 @@ Claude Code supports hooks via `hooks.json` in a plugin or `settings.json` local
}
```

## Git memory commit-msg hook

Enforces the commit convention from the "Git memory" section bootstrap adds to
CLAUDE.md: a malformed subject **blocks** the commit; a feat/fix/refactor
commit without a body gets a **warning** on stderr — an agent sees it and
amends, a human may ignore it. The hook never judges content, only shape and
presence.

Location: `.husky/commit-msg` when the project uses husky (tracked, reaches
the whole team), otherwise `.git/hooks/commit-msg` with `chmod +x` (local
only — note that in the summary). Never overwrite an existing `commit-msg`
hook — leave it and report instead.

```sh
#!/usr/bin/env sh
msg_file="$1"
subject=$(head -n 1 "$msg_file")

case "$subject" in
Merge\ * | Revert\ * | fixup!* | squash!*) exit 0 ;;
esac

if ! printf '%s' "$subject" | grep -qE '^(([A-Z][A-Z0-9]*-[0-9]+|#[0-9]+) )?(feat|fix|refactor|docs|test|chore|style|perf)(\([^)]+\))?: .+'; then
echo "commit-msg: bad subject '$subject'" >&2
echo "commit-msg: expected 'TICKET type(SLUG): description' — see the Git memory section in CLAUDE.md" >&2
exit 1
fi

type=$(printf '%s' "$subject" | sed -E 's/^(([A-Z][A-Z0-9]*-[0-9]+|#[0-9]+) )?([a-z]+).*/\3/')
case "$type" in
feat | fix | refactor)
if ! sed -n '2,$p' "$msg_file" | grep -v '^#' | grep -q '[^[:space:]]'; then
echo "commit-msg: warning — $type commit without a body; if it carries a decision, amend with the why (Git memory, CLAUDE.md)" >&2
fi
;;
esac

exit 0
```

No extra CLAUDE.md note is needed — the Git memory section already mentions
the hook.

## Git hooks by stack

### Node.js — Prettier + ESLint
Expand Down
6 changes: 6 additions & 0 deletions skills/do/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ These hold for every mode run:
- **Commits by convention.** Format, ticket ID, and git initiative — from
`${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/commit-convention.md` (see its
"Git initiative and defaults"). Do not ask "commit?" mid-run.
- **Read the git memory first.** Before modifying a file, read its recent
commit history per
`${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/history-reading.md`. A
`Constraint:` or `Directive:` that conflicts with the plan is surfaced,
not silently overridden; a `Rejected:` approach is not re-proposed
without new evidence.
- **Review after each task.** Spec compliance → code quality. Mandatory.
- **Context isolation.** A sub-agent receives only its own task text, not the
whole plan.
Expand Down
4 changes: 4 additions & 0 deletions skills/draft/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ These hold for every run:
`.yoke/ai/<slug>/`.
- **Commits by convention** per
`${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/commit-convention.md`.
- **Read the git memory first** per
`${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/history-reading.md` — a Marker
must not re-propose what a `Rejected:` trailer already dismissed, and
Markup respects active `Constraint:` entries.
- Language: match the ticket/input language, or follow the project-level
definition in CLAUDE.md / AGENTS.md.

Expand Down
72 changes: 70 additions & 2 deletions skills/gca/reference/commit-convention.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Commit Convention

Commit format for yoke skills and standalone invocations.
Commit format for yoke skills and standalone invocations. This is the write
side of the **git memory** (ADR-0012): commit messages carry the project's
decision history, written for coding agents as the primary readers. The read
side — how to recover that history — lives in `history-reading.md`.

---

Expand All @@ -11,7 +14,7 @@ Plugin-wide contract for when the agent may run git and how commits are shaped.
- **Authorization.** A skill invocation (`/do`, `/merge`, `/gca`, `/gp`, `/pr`) authorizes the git operations that skill performs — commit, push, PR — with no mid-run "commit?" questions.
- **Initiative.** Outside skill runs the agent never commits or pushes on its own initiative — only on an explicit user command.
- **Language.** Commit messages default to English; a project may override via `.yoke/flow.md` or its `CLAUDE.md`.
- **Trailers.** Never add trailer lines (`Co-Authored-By`, `Signed-off-by`, etc.).
- **Trailers.** Never add identity trailers (`Co-Authored-By`, `Signed-off-by`, and the like). Decision trailers from the Body section are different — they carry memory, use them.
- **Identity.** Never fabricate committer identity (no `git -c user.email=...`); when identity is missing, ask the user.

---
Expand Down Expand Up @@ -44,6 +47,69 @@ R2-220 fix(R2-220-fix-doubled-stats): restrict analytics

---

## Body — the git memory

The subject says what; the body says **why**. The body is the Decision
Shadow — the context that lived in the author's head when the code was
written and dies with the session unless it lands here. A future agent
recovers it with `git log -- <path>` (see `history-reading.md`).

### When a body is required

By content, not by type. Write a body when the commit carries a decision — a
chosen approach, a rejected alternative, a non-obvious constraint. The test:
**will a reader six months out wonder "why"?** Mechanical commits (dependency
bumps, formatting, generated files) stay one-liners.

### Shape

1. **Prose** — one or two short paragraphs of reasoning: why this approach,
what constraint drove it, what almost worked. Plain sentences, no headings,
no diff narration.
2. **Decision trailers** — after the prose, from the fixed vocabulary:

| Trailer | Carries |
| ------------- | --------------------------------------------------------------- |
| `Constraint:` | An active rule the code must keep respecting |
| `Rejected:` | An approach considered or tried and dismissed — with the reason |
| `Directive:` | A warning to whoever touches this code next |
| `Related:` | A pointer: ticket, commit hash, or `.yoke/` artifact path |

Every trailer is optional — write one only when there is real content; repeat
a key for multiple entries. Only these four keys: a fixed vocabulary is what
keeps the history greppable (`git log --grep="^Rejected:"`).

### Example

```
#86 feat(86-black-jack-page): add SSE endpoint

Polling was dropped: the kiosk network kills idle HTTP/1.1
connections after 30s, SSE with retry survives it. Score state
lives server-side because the client is untrusted.

Rejected: WebSocket — no proxy support on kiosks
Constraint: client is untrusted, never move score calc there
Related: #84
```

### Body anti-patterns

```
# WRONG — diff narration, not a decision:
Added handleRetry() that retries the request and updated the tests.

# WRONG — ritual trailers with no content:
Constraint: none
Confidence: high # not in the vocabulary; self-assessment is noise

# RIGHT — the decision and its reason:
Retry lives in the client because the gateway strips Retry-After;
see the rejected server-side attempt in a1b2c3d.
```

---

## Ticket ID

Priority cascade:
Expand Down Expand Up @@ -158,6 +224,8 @@ Commits for yoke flow artifacts (format `TICKET docs(SLUG): description`):

- One commit — one logical change.
- Ticket ID first in the message (if present).
- A commit that carries a decision gets a prose body and, when there is
content, decision trailers — see "Body — the git memory".
- Avoid `wip`, `temp`, `misc`.
- Staging/exclusion policy lives in `staging-strategy.md`: gca excludes only untracked secrets, keys, and >1MB binaries, and always commits tracked files, git-crypt included. Never exclude a file by authorship.
- Git initiative, message language, trailers, and committer identity follow "Git initiative and defaults".
Expand Down
72 changes: 72 additions & 0 deletions skills/gca/reference/history-reading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# History Reading

The read side of the **git memory** (ADR-0012): how to recover the decision
history that `commit-convention.md` writes into commit messages. Run this
before touching or judging code — the history of a file carries constraints,
rejected approaches, and warnings that never made it into the code itself.

---

## Before modifying a file

```bash
git log -n 20 --format="%h %s" -- <path> # quick pass over the intents
git log -n 5 -- <path> # full messages: bodies + trailers
```

Act on what comes back:

- `Constraint:` — an active rule. Respect it; when the plan conflicts with
it, surface the conflict instead of silently overriding.
- `Directive:` — a warning from a previous author. Heed it.
- `Rejected:` — a dead end already explored. Do not re-propose it without
new evidence; when you do, cite what changed.
- Prose bodies — the why behind the current shape. Read them before
concluding the code is wrong or accidental.

## Before proposing an approach

Check whether it was already tried and dismissed:

```bash
git log --grep="^Rejected:" --format="%h %s%n%b" -- <path>
```

`--grep` is line-oriented: `^Rejected:` anchors to a trailer line. Drop the
`-- <path>` to search the whole history.

## Understanding one line

```bash
git blame -L <start>,<end> <file> # who last shaped these lines
git show <hash> # the full message — body and trailers
```

`git blame` gives the hash; `git show` gives the memory. Two steps from a
puzzling line to the decision behind it.

## Searching wider

```bash
git log --grep="<keyword>" # search all messages
git log --follow -- <path> # history across renames
```

## Trailer-aware output

```bash
git log --format="%h %s%n%(trailers:key=Constraint,key=Directive,valueonly=false)" -- <path>
```

`%(trailers:key=...)` prints only the named trailers — useful for harvesting
every active `Constraint:` over a directory in one pass.

---

## Depth

Ten subjects and the last five full messages per touched file is the default;
dig deeper (`--follow`, `--grep` over all history) only when a body references
older context or the file is central to the change. Older projects have
pre-memory history — a one-line commit with no body is normal there and
carries no signal beyond its subject.
2 changes: 2 additions & 0 deletions skills/grill-docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ If a question can be answered by exploring the codebase, explore the codebase in

## Domain awareness

During codebase exploration, also read the git memory: the affected files' commit history per `${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/history-reading.md` — a past `Rejected:` or `Constraint:` trailer is grill material, cite it when challenging the plan.

During codebase exploration, also look for existing documentation:

### File structure
Expand Down
1 change: 1 addition & 0 deletions skills/grill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@ Stop when a full pass down the tree surfaces no new open decisions, or when the
- Always include the recommended answer, listed first.
- Resolve dependencies in order — don't jump branches.
- Prefer exploring the codebase over asking when the codebase holds the answer.
- Exploring the codebase includes its git memory: read the affected files' commit history per `${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/history-reading.md` — a past `Rejected:` or `Constraint:` trailer is grill material, cite it when challenging the plan.
- For deeper grilling that also captures terminology and decisions into `.yoke/context.md` and ADRs, use `/yoke:grill-docs`.
- Language: match the user's language, or follow the project-level definition in CLAUDE.md / AGENTS.md.
2 changes: 1 addition & 1 deletion skills/issues/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Work from whatever is already in the conversation context. If the user passes an

### 2. Explore the codebase (skip if already explored this session)

If the codebase has not been explored this session, explore it now to understand its current state. Follow the domain-doc consumer rules in `${CLAUDE_PLUGIN_ROOT}/skills/grill-docs/reference/domain-docs.md`: read `.yoke/context.md` and relevant `.yoke/adr/`, give issue titles and descriptions the glossary's vocabulary, and flag any ADR a slice contradicts.
If the codebase has not been explored this session, explore it now to understand its current state. Follow the domain-doc consumer rules in `${CLAUDE_PLUGIN_ROOT}/skills/grill-docs/reference/domain-docs.md`: read `.yoke/context.md` and relevant `.yoke/adr/`, give issue titles and descriptions the glossary's vocabulary, and flag any ADR a slice contradicts. Also read the git memory of the areas the slices touch per `${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/history-reading.md` — a slice that re-proposes a `Rejected:` approach or crosses a `Constraint:` must say so.

### 3. Draft vertical slices

Expand Down
2 changes: 1 addition & 1 deletion skills/prd/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Turn the current conversation and codebase understanding into a PRD. Do NOT inte

## Process

1. **Explore the repo** to understand the codebase, if not already done. Follow the domain-doc consumer rules in `${CLAUDE_PLUGIN_ROOT}/skills/grill-docs/reference/domain-docs.md`: read `.yoke/context.md` and the relevant `.yoke/adr/`, use the glossary's vocabulary throughout the PRD, and flag any ADR the PRD contradicts.
1. **Explore the repo** to understand the codebase, if not already done. Follow the domain-doc consumer rules in `${CLAUDE_PLUGIN_ROOT}/skills/grill-docs/reference/domain-docs.md`: read `.yoke/context.md` and the relevant `.yoke/adr/`, use the glossary's vocabulary throughout the PRD, and flag any ADR the PRD contradicts. Also read the git memory of the modules the PRD touches per `${CLAUDE_PLUGIN_ROOT}/skills/gca/reference/history-reading.md` — active `Constraint:` entries and past `Rejected:` approaches belong in Implementation Decisions.

2. **Sketch the major modules** to build or modify. Look for deep modules to extract that can be tested in isolation. A deep module encapsulates substantial functionality behind a simple, testable interface that rarely changes. Decide the module breakdown and which modules are test-worthy yourself, and record both in the PRD's Implementation Decisions and Testing Decisions sections — do not stop to interview the user.

Expand Down
Loading