From e5e96111f677c70df75cef0b5a7d25db960bb9b9 Mon Sep 17 00:00:00 2001 From: Guillaume Ferrari Date: Tue, 29 Sep 2026 21:02:42 +0000 Subject: [PATCH 1/2] intake: a project has an inbox for what is handed to it from outside Second version. The first asked for a SessionStart hook shipped by the bootstrap, reading a private layout through VAULT_ROOT; refused on ADR 0025 (the plugin ships no runtime) and on the rule that it never describes one estate's layout. Rewritten to the counter-proposal: groundrules ships the convention, the estate ships the transport. --- ...09-29-a-session-reads-its-project-inbox.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 intake/2026-09-29-a-session-reads-its-project-inbox.md diff --git a/intake/2026-09-29-a-session-reads-its-project-inbox.md b/intake/2026-09-29-a-session-reads-its-project-inbox.md new file mode 100644 index 0000000..87b18d5 --- /dev/null +++ b/intake/2026-09-29-a-session-reads-its-project-inbox.md @@ -0,0 +1,28 @@ +# A project has an inbox for what is handed to it from outside — a convention, no runtime + +> From `gitlab:lozit/cockpit`, 2026-09-29. First version refused by the groundrules session the same +> evening, on ADR 0025 (no runtime hook shipped by the plugin) and on the rule that the plugin never +> describes one estate's private layout. Rewritten to the counter-proposal: **groundrules ships the +> convention, the estate ships the transport.** + +## The need, generally stated + +Work reaches a project from outside — a colleague, a ticket, another repository, a person's notes — +before anyone in the project has looked at it. Today the generated `CLAUDE.md` has no named place +for that, so it lands in chat, in a commit message, or nowhere. A session that starts should find +what was handed to the project since last time, without a human carrying it. + +## What is to be obtained + +1. An optional `INBOX.md` proposed by the bootstrap: *what was handed to this project from outside, + dated, verbatim, not yet triaged*. Capture is free; deciding where a line belongs happens later, + in the session, not at capture. +2. The generated `CLAUDE.md` names it in its "Session start — read first" list, before the plan + and the decisions: read it, triage what is there, leave nothing silently. +3. Nothing executable, nothing that names a harness, a vault, or a filesystem layout. How lines get + *into* `INBOX.md` is each estate's business (a hook, a script, a person) and is not described + here. + +Acceptance: a repository bootstrapped with `INBOX.md` sees it named at session start in its +`CLAUDE.md`; a repository bootstrapped without it is unchanged; the plugin still contains no +executable and no reference to any particular estate. From 61455ac766fdda370d05611883a6ae34e91ee7b6 Mon Sep 17 00:00:00 2001 From: Guillaume Ferrari Date: Tue, 29 Sep 2026 21:05:37 +0000 Subject: [PATCH 2/2] feat(bootstrap): optional INBOX.md for what is handed to the project from outside MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Work reaches a project before anyone in it has looked at it, and the generated CLAUDE.md had no named place for that. INBOX.md is offered by bootstrap and adopt, unchecked by default: one dated line per subject, verbatim, triage marks a line rather than deleting it. Named first in the session-start list, before PLAN.md — what arrived can change what the rest of the list means. No conditional needed: the list already marks optional entries as (if present). The plugin never fills it. How lines arrive is the caller's business. The request behind this asked for a SessionStart hook reading a private store's layout through VAULT_ROOT. Refused: it would have been the first executable this plugin generates, reopening ADR 0025 eight days after the same line was applied against this repo's own convenience, and it would have put one estate's layout into a public plugin. ADR 0048 records the refusal and the split it produced — the convention here, the transport elsewhere. See CHANGELOG [Unreleased] > Added. --- CHANGELOG.md | 4 + README.md | 1 + ...convention-here-the-transport-elsewhere.md | 101 ++++++++++++++++++ docs/decisions/README.md | 1 + skills/adopt/SKILL.md | 1 + skills/bootstrap/SKILL.md | 7 +- skills/bootstrap/templates/CLAUDE.md.tpl | 11 +- skills/bootstrap/templates/INBOX.md.tpl | 31 ++++++ skills/verify-bootstrap/SKILL.md | 2 +- 9 files changed, 152 insertions(+), 7 deletions(-) create mode 100644 docs/decisions/0048-the-convention-here-the-transport-elsewhere.md create mode 100644 skills/bootstrap/templates/INBOX.md.tpl diff --git a/CHANGELOG.md b/CHANGELOG.md index ddcddba..c3b2990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ versions follow [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Added +- **An optional `INBOX.md`: what was handed to this project from outside, not yet triaged** ([ADR 0048](docs/decisions/0048-the-convention-here-the-transport-elsewhere.md)). Offered by `bootstrap` and `adopt`, unchecked by default, at the repository root: one dated line per subject, **verbatim**, and triage **marks** a line (`→ ADR 0012`, `→ PLAN.md`, `→ dropped`) rather than deleting it — the file is a record of what arrived, not a queue that empties. It is named **first** in the generated `CLAUDE.md`'s session-start list, before `PLAN.md`, because what arrived can change what the rest of the list means; no conditional was needed, the list already marks optional entries *(if present)*. **The plugin never fills it** — how lines get there is the caller's business, described nowhere here. + **The request that produced it asked for something else, and was refused.** It wanted a `SessionStart` hook shipped by the bootstrap, resolving a project slug and reading `01-projects//inbox.md` from a private store through `VAULT_ROOT`. That would have been **the first executable this plugin ever generated** (verified: no `.sh`, no hook, nothing runtime in any template), reopening [ADR 0025](docs/decisions/0025-no-runtime-hook-no-watch.md) — *"a runtime hook is machinery against groundrules' nature… pure Markdown + JSON, no runtime"* — eight days after the same line was applied against this repository's own convenience, when an index check went to CI rather than to a `PostToolUse` hook. And it would have put **one estate's private layout into a public plugin**: `VAULT_ROOT` makes the root configurable, `01-projects//inbox.md` is still a shape that exists in one person's store. That is the September lesson returning in a new form. The refusal held and the request was rewritten to the counter-proposal — **the convention here, the transport elsewhere** — which is the part of this worth reusing beyond inboxes. + ### Changed - **`AGENTS.md` investigated and deliberately deferred to M2** ([ADR 0047](docs/decisions/0047-agents-md-deferred-to-m2.md); **nothing in the plugin changes**). The open spec — OpenAI-led, donated to the Linux Foundation, 60k+ repositories — is a single predictable file of project context for AI agents, and this plugin had **no mention of it anywhere** while `docs/ROADMAP.md` M2 states the harness-neutral direction outright. The spec says nothing about coexisting with tool-specific files, so the behaviour was **measured rather than read** (Claude Code 2.1.280, three throwaway directories, a fresh `claude -p` with read tools disabled so the answer comes from what was *loaded*): `AGENTS.md` alone is read; `AGENTS.md` **beside** a `CLAUDE.md` is **ignored entirely**; a `CLAUDE.md` containing `@AGENTS.md` loads both. **That kills the cheapest-looking option** — a pointer would be read by no Claude Code session while looking installed, the failure class logged twice this week. Two further measurements are recorded so M2 does not re-derive them: the generated `CLAUDE.md` is **~86% harness-neutral**, its Claude-bound lines clustering in three identifiable blocks rather than diffusing; and `CLAUDE.md` is referenced **77 times across 10 skills** plus 10 templates, which is what makes a rename disproportionate today. The recommendation carried forward is the **`@AGENTS.md` import**, not a pointer and not a rename. One argument found along the way **does not need M2**: the methodology's entry points are written *as commands* (*"decided → `/groundrules:add-adr`"*), meaningless to another tool or to a human without the plugin — splitting would force the portable half to state the **act and the place**, which is a better generated file regardless. - **The ADR index invariant becomes a check** (`test/check-adr-index.sh`, **dogfood only**). On 2026-09-21 an ADR landed on `main` without its row in `docs/decisions/README.md` and survived, because the only thing guarding that invariant was a line in the release checklist — read at release, days after the omission. A set comparison has exactly one correct answer, so it is a check, not a reminder, and not an eval case: an eval measures whether configuration changes behaviour, which is the wrong instrument for *is every file listed*. The script compares both directions — a file with no row, a row with no file — and flags duplicates, since a row listed twice reads as agreement until someone diffs them. Deterministic, offline, zero tokens; verified to fail as well as pass. **It runs on every pull request** (`.github/workflows/adr-index.yml` — one workflow, one job, one step; the stack is pure Markdown and JSON, so CI is a real surface and stays the size of the invariant it guards). The first placement considered was a git `pre-commit` hook, rejected on a mix of one sound reason and one that turned out false — **corrected the same day, see below**. `core.hooksPath` on the maintainer's machine does point at `ggshield`'s directory, and that setting *replaces* the hooks directory rather than adding to it, so **git itself** never looks in `.git/hooks`. But the occupying script **calls the repository's hook and fails the commit if it fails**, so a local hook would have run after all. CI is the better instrument rather than the fallback: it travels with the clone, `--no-verify` cannot walk past it, and it fires at the **pull request**, which is where the omission landed. **Proven both ways** before shipping — a throwaway PR removing an index row went red naming the file, restoring the row turned the same workflow green. A workflow whose red has never been seen is a badge. The lesson generalises in `docs/LEARNINGS.md`, sharpened by the correction: **read what occupies the slot — occupancy is not a second question, it answers the first.** Whether your control runs is a property of the occupant's *code*, not of the configuration pointing at it; here the configuration said *unreachable* and the script said *I call it myself*. Skipping that read cuts the other way too: writing into the `ggshield` directory itself, where the entries are symlinks into `dotfiles` beside a `.bak-` from a previous overwrite, **would** have destroyed a working guard while reading as installed. diff --git a/README.md b/README.md index 245c6ef..58f546e 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ The flow: `intake/` (raw, untouched) → `docs/` (synthesized, living) → `docs | `docs/I18N.md` | Multilingual project | Supported languages and fallback, translation organization, localized formats, translation process. | | `docs/PROCESS.md` | Phased way of working | The working-method contract: phases, validation gates, interview style, where artifacts live. Claude follows it and never skips a phase. | | `RELEASE.md` | Project deploys somewhere | Operational release runbook: TL;DR commands, environments table, pre-release checklist, secrets, rollback, known fragilities. | +| `INBOX.md` | Work arrives from outside | **What was handed to this project** and is not yet triaged — a colleague, a ticket, another repository, a note made away from the desk. One dated line per subject, verbatim; triage marks it, never deletes it. Named **first** in the generated `CLAUDE.md`'s session-start list, so a session reads what arrived before deciding what to do. groundrules gives it a name and a place and **never fills it**: how lines get there is yours. | | `docs/AGENT-EVALS.md` | Long agent-driven project | A log of the **agent's own** failure modes here (recurring mistakes, hallucinations, drifts) + the guard added for each. Distinct from `LEARNINGS.md` (project/domain). Fed by the checkpoint-capture ritual (before a push/release). | | `docs/ADOPTION-LOG.md` | `adopt`, opt-in | A dated, frozen record of the adopt run — what was here, what groundrules did (and why) — with a **Remarks** section to annotate and **share back to improve groundrules**. A field → plugin feedback channel. | | `loop/` + `## Invariants` in `CLAUDE.md` | Opt-in (off by default) | **Loop scaffolding**: `maker.md`/`verifier.md` (an independent verifier that distrusts the maker's report — the runner spawns it as a **separate** invocation with a fresh context, so the independence is enforced rather than requested, [ADR 0044](docs/decisions/0044-runner-enforces-verifier-isolation.md)), `LOOP.md`, a capped `run-loop.sh` (hard iteration ceiling), and `backlog.md` (loop-safe tasks only). Adds an `## Invariants` section the verifier enforces each iteration, and a **triage convention** for `loop/blocked.md` (the backward crossing: re-decompose / decide → ADR / fix interactively, with a `Resolution:` audit trail). **Not offered when superpowers is detected** (it defers the realization). | diff --git a/docs/decisions/0048-the-convention-here-the-transport-elsewhere.md b/docs/decisions/0048-the-convention-here-the-transport-elsewhere.md new file mode 100644 index 0000000..7be7ea7 --- /dev/null +++ b/docs/decisions/0048-the-convention-here-the-transport-elsewhere.md @@ -0,0 +1,101 @@ + +# 0048 — An optional `INBOX.md`: the plugin ships the convention, the caller ships the transport + +**Date**: 2026-09-29 +**Status**: Accepted + +## Context + +Work reaches a project from outside — a colleague, a ticket, another repository, a note somebody +made away from the desk — before anyone in the project has looked at it. The generated `CLAUDE.md` +had no named place for that, so it landed in a chat message, in a commit body, or nowhere. + +The request that surfaced it (`intake/2026-09-29-a-session-reads-its-project-inbox.md`) asked for +something else: a **`SessionStart` hook shipped by the bootstrap**, resolving a project slug, +reaching a private document store through `VAULT_ROOT`, reading `01-projects//inbox.md` and +printing its unpicked lines. **It was refused**, on two grounds this repository had already paid +for, and rewritten to what this ADR records. + +**It would have been the first executable the plugin ever generated.** Verified: no `.sh`, no hook, +nothing runtime in any template today. [ADR 0025](0025-no-runtime-hook-no-watch.md) refused that +class in its own words — *"a runtime hook is machinery against groundrules' nature (ADR 0002 +'template over code'; offline-first; 'pure Markdown + JSON, no runtime')"* — and added that a hook +coupled to one harness's format does not port, which +[ADR 0047](0047-agents-md-deferred-to-m2.md) reaffirmed six days earlier for generated output. The +same line had been applied against this repository's own convenience a week before, when an index +check was sent to CI rather than to a `PostToolUse` hook; a line that holds only when it costs +someone else is not a line. + +**And it would have put one estate's private layout into a public plugin.** `VAULT_ROOT` makes the +*root* configurable; `01-projects//inbox.md` is still a shape that exists in exactly one +person's store. A bootstrapped project elsewhere would have received a hook reading a directory +convention the plugin documents nowhere. That is the September lesson — an ADR rebuilt from scratch +and a brief neutralised to remove exactly this — returning in a new form. + +## Decision + +**1. An optional `INBOX.md` at the repository root**, offered by `bootstrap` (Call 2b) and `adopt`, +unchecked by default: *what was handed to this project from outside, dated, verbatim, not yet +triaged.* Capture is free; deciding where a line belongs happens later. Triage **marks** a line +(`→ ADR 0012`, `→ PLAN.md`, `→ dropped`) rather than deleting it: the file is a record of what +arrived, not a queue that empties. + +**2. It is named first in the generated `CLAUDE.md`'s session-start list**, before `PLAN.md` — +what arrived can change what the rest of the list means. It needs no conditional: the list already +marks optional entries *(if present)*, so one line serves both cases and nothing has to be dropped +at generation time. + +**3. The plugin never fills it.** How lines arrive — typed, pasted, or written by a script of the +user's own — is the caller's business and is described nowhere in the plugin. **This is the whole +decision**: the convention is general and belongs here; the transport is particular and belongs to +whoever has a particular source. The estate that asked for this keeps its hook, in its own +dotfiles, bound to its own layout. + +**4. Nothing executable, nothing naming a harness or a store.** The plugin still ships zero +runtime. + +## Alternatives considered + +- **Ship the `SessionStart` hook as asked** — refused, per the two grounds above. Reopening ADR + 0025 for a convenience, and re-importing a private layout eight days after removing it, would + each have been a bad trade on its own. +- **Ship the hook but make the inner path configurable too** — rejected: a second environment + variable would have hidden the coupling rather than removed it, and the plugin would still be + shipping an executable it cannot test on any machine but one. +- **Reuse `intake/` instead of a new file** — tempting, and wrong. `intake/` is *upstream material + gathered before the work starts* — briefs, specs, transcripts — and its README says so. An inbox + is a **live surface** that fills during the project's life and is triaged line by line. Folding + them would blur a folder that is currently unambiguous, for one fewer file. +- **Make it non-optional** — rejected: a project where every input passes through the person + running the session gains an empty file and a line of ritual. ADR 0006's opt-in shape applies. + +## Consequences + +### Positive +- Work handed to a project has a named place, and a session is told to read it **first**. +- The refusal is recorded with its reasoning, so the next request of this shape is a lookup rather + than a re-derivation. +- The split — convention here, transport there — is stated as a principle, which is the part that + generalises beyond inboxes. + +### Negative / Tradeoffs +- **A named place is not a filled one.** Nothing guarantees a line reaches `INBOX.md`; the plugin + deliberately does not provide the mechanism that would. For a caller with no transport, this is a + file that stays empty — which is why it is optional and unchecked by default. +- **The session-start list is a reminder, not a check** — the distinction this repository keeps + relearning. It relies on the agent reading a loaded file. That is what a Markdown-only plugin can + offer; anything stronger is runtime, and runtime is what ADR 0025 declined. +- One more optional doc in Call 2b, which keeps growing. + +### Neutral +- Projects that decline it are unchanged, and the generated `CLAUDE.md` gains one line either way. + +## Notes + +- Source: `intake/2026-09-29-a-session-reads-its-project-inbox.md`, second version. Its header + records the refusal of the first, which is why the file is worth reading before this ADR. +- Related: [ADR 0025](0025-no-runtime-hook-no-watch.md) (no runtime shipped), + [ADR 0006](0006-optional-specialized-docs.md) (the opt-in shape), + [ADR 0047](0047-agents-md-deferred-to-m2.md) (output stays harness-neutral), + [ADR 0022](0022-agent-evals-and-session-close.md) (`AGENT-EVALS.md`, the closest precedent for + adding an optional doc). diff --git a/docs/decisions/README.md b/docs/decisions/README.md index cd6b710..e5b3025 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -77,3 +77,4 @@ No ADR needed for trivial choices or implementation details. | [0045](0045-the-lock-was-scoped-to-four-skills.md) | `disable-model-invocation` applied per skill: five methodology skills open, five destructive ones stay locked (refines 0003) | Accepted | 2026-09-21 | | [0046](0046-orchestrated-working-regime.md) | Orchestrated regime: the request for work carries the authorisation for the commit (amends 0036's commit rule) | Accepted | 2026-09-21 | | [0047](0047-agents-md-deferred-to-m2.md) | `AGENTS.md` deferred to M2, with the harness behaviour measured now (a pointer is read by nothing) | Accepted | 2026-09-23 | +| [0048](0048-the-convention-here-the-transport-elsewhere.md) | Optional `INBOX.md`: the plugin ships the convention, the caller ships the transport (a `SessionStart` hook was refused) | Accepted | 2026-09-29 | diff --git a/skills/adopt/SKILL.md b/skills/adopt/SKILL.md index 4d2e515..36c6ed1 100644 --- a/skills/adopt/SKILL.md +++ b/skills/adopt/SKILL.md @@ -111,6 +111,7 @@ Pre-check based on the scan. Only offer what **doesn't already exist**: - `docs/PROCESS.md` — pre-check if a process/method doc is detected (phased workflow, validation gates) - `RELEASE.md` — pre-check if CI/CD or hosting config is detected (`.gitlab-ci.yml`, `.github/workflows/`, `netlify.toml`, `vercel.json`…) - `docs/AGENT-EVALS.md` — offered (unchecked by default): a log of the agent's observed failure modes (mistakes, hallucinations, drifts) + the guard added. Distinct from `LEARNINGS.md`. +- `INBOX.md` (root) — offered (unchecked by default): what reaches this project from outside, dated and verbatim, not yet triaged. Worth offering on a brownfield project, where work routinely arrives from people who are not the one running the session. groundrules never fills it. Skip an individual option only if that exact file already exists (then list it under "adopted", not here). If **none** of these exist yet, the full list is shown with the detected ones pre-checked. diff --git a/skills/bootstrap/SKILL.md b/skills/bootstrap/SKILL.md index e32b7c7..d8896da 100644 --- a/skills/bootstrap/SKILL.md +++ b/skills/bootstrap/SKILL.md @@ -24,7 +24,7 @@ You will bootstrap a Claude Code project in the **current working directory**. F - `.git/` (existing git repo) - `.groundrules.json` (state from a previous invocation — if present, **load it**; it is what selects `resume` in the mode table at step 4, and this line does not decide the mode on its own); a legacy pre-1.0 `.starter-kit.json` counts the same — load it and recommend `/groundrules:migrate` (V1.0 renamed the plugin and the state file) - `CLAUDE.md`, `README.md`, `docs/`, `intake/`, `docs/media/`, `PLAN.md`, `CHANGELOG.md`, `.gitignore`, `docs/VISION.md`, `intake/INTENT.md` - - optional specialized docs: `docs/DATA_MODEL.md`, `docs/SECURITY.md`, `docs/DESIGN_SYSTEM.md`, `docs/ROADMAP.md`, `docs/I18N.md`, `docs/PROCESS.md`, `RELEASE.md`, `docs/AGENT-EVALS.md` + - optional specialized docs: `docs/DATA_MODEL.md`, `docs/SECURITY.md`, `docs/DESIGN_SYSTEM.md`, `docs/ROADMAP.md`, `docs/I18N.md`, `docs/PROCESS.md`, `RELEASE.md`, `docs/AGENT-EVALS.md`, `INBOX.md` - **`PLAN.md` equivalents** (planning aliases, **same altitude**) — detection is **case-insensitive** and **nested** (up to ~3 levels, excluding `node_modules`/`.git`): `plan.md`, `TODO.md`, `todo.md`, `todos.md`, `TASKS.md`, `BACKLOG.md`, including under a path (e.g. `docs/gtd/todos.md`). There may be **several** — report all of them. **Case guard**: **never** generate `PLAN.md` if an equivalent name exists in a different case (collision on a case-sensitive FS). - `docs/superpowers/plans/` (superpowers **per-feature** plans — **different altitude**, *not* a `PLAN.md` alias) - **superpowers in use** → `HAS_SUPERPOWERS`, which decides whether the generated `CLAUDE.md` carries the interop section (Phase 5). Three signals, in order, **all read-only and best-effort**; the first that fires wins: @@ -96,6 +96,8 @@ A single **multiSelect** `AskUserQuestion`: *"Which specialized docs do you want - **`RELEASE.md`** — operational release runbook: environments, commands, checklists, rollback, known fragilities. Check **only if the project deploys somewhere** (detected CI config, hosting, or the user says so). - **`docs/AGENT-EVALS.md`** — a log of the **agent's own** observed failure modes on this project (recurring mistakes, hallucinations, drifts) and the guard added for each. Distinct from `LEARNINGS.md` (which is about the project/domain). Offer it (unchecked by default); useful on long-running agent-driven projects. +- **`INBOX.md`** (root, not `docs/`) — what was handed to this project **from outside** and is not yet triaged: a colleague, a ticket, another repository, a note made away from the desk. One dated line per subject, verbatim; triage marks it, never deletes it. Offer it when work is likely to reach the project from somewhere other than the person running the session. **groundrules never fills it** — how lines arrive is the project's business — it only gives the place a name and puts it first in the generated `CLAUDE.md`'s session-start list. + Adapt suggestions to context: if the stack/intent suggests a UI, pre-suggest `DESIGN_SYSTEM`; a DB → `DATA_MODEL`; etc. Impose nothing: no check = no file. Only ask this call for a code project. For a non-code project, only `ROADMAP` may make sense — offer it on its own if relevant. @@ -211,7 +213,7 @@ For each file to create: - `{{STACK}}` — stack or empty string - `{{DATE}}` — today's date in ISO (YYYY-MM-DD) - `{{HAS_PLAN}}`, `{{HAS_ARCHITECTURE}}`, `{{HAS_GLOSSARY}}`, `{{HAS_CHANGELOG}}` — `true`/`false` - - `{{HAS_DATA_MODEL}}`, `{{HAS_SECURITY}}`, `{{HAS_DESIGN_SYSTEM}}`, `{{HAS_ROADMAP}}`, `{{HAS_I18N}}`, `{{HAS_PROCESS}}`, `{{HAS_RELEASE}}`, `{{HAS_AGENT_EVALS}}` — `true`/`false` (specialized docs) + - `{{HAS_DATA_MODEL}}`, `{{HAS_SECURITY}}`, `{{HAS_DESIGN_SYSTEM}}`, `{{HAS_ROADMAP}}`, `{{HAS_I18N}}`, `{{HAS_PROCESS}}`, `{{HAS_RELEASE}}`, `{{HAS_AGENT_EVALS}}`, `{{HAS_INBOX}}` — `true`/`false` (specialized docs) - `{{HAS_LOOP}}` — `true`/`false` (loop scaffolding opted in, Call 2c) - `{{HAS_SUPERPOWERS}}` — `true`/`false` (superpowers detected, Phase 1) @@ -273,6 +275,7 @@ There is **one** template (`CLAUDE.md.tpl`). When a global CLAUDE.md exists, a " | `HAS_PROCESS=true` | `PROCESS.md.tpl` | `docs/PROCESS.md` | | `HAS_RELEASE=true` | `RELEASE.md.tpl` | `RELEASE.md` | | `HAS_AGENT_EVALS=true` | `AGENT-EVALS.md.tpl` | `docs/AGENT-EVALS.md` | +| `HAS_INBOX=true` | `INBOX.md.tpl` | `INBOX.md` | | `intent.source` ∈ `paste`/`file` | `intake-INTENT.md.tpl` | `intake/INTENT.md` | | `intent.source` ≠ `skipped` | `docs-VISION.md.tpl` | `docs/VISION.md` | | `HAS_LOOP=true` | `loop/README.md.tpl` | `loop/README.md` | diff --git a/skills/bootstrap/templates/CLAUDE.md.tpl b/skills/bootstrap/templates/CLAUDE.md.tpl index 04b7929..5f5dea5 100644 --- a/skills/bootstrap/templates/CLAUDE.md.tpl +++ b/skills/bootstrap/templates/CLAUDE.md.tpl @@ -5,10 +5,13 @@ {{GLOBAL_CLAUDE_NOTE}} ## Session start — read first, in order -1. `PLAN.md` — where the project stands **now** (if present) -2. `docs/LEARNINGS.md` — rules learned from past corrections (apply them!) -3. `docs/VISION.md` — goal, scope, non-goals (if present) -4. The artifacts of whatever is in progress per `PLAN.md` +1. `INBOX.md` — what was handed to this project from outside, not yet triaged (if present). Read it + first: it may change what the rest of this list means. Triage what is there, leave nothing + silently. +2. `PLAN.md` — where the project stands **now** (if present) +3. `docs/LEARNINGS.md` — rules learned from past corrections (apply them!) +4. `docs/VISION.md` — goal, scope, non-goals (if present) +5. The artifacts of whatever is in progress per `PLAN.md` diff --git a/skills/bootstrap/templates/INBOX.md.tpl b/skills/bootstrap/templates/INBOX.md.tpl new file mode 100644 index 0000000..e91f416 --- /dev/null +++ b/skills/bootstrap/templates/INBOX.md.tpl @@ -0,0 +1,31 @@ + +# INBOX — {{PROJECT_NAME}} + +What was handed to this project **from outside**, not yet triaged. A colleague, a ticket, another +repository, a note you made away from the desk — anything that arrived before anyone here looked +at it. + +**Capture is free; deciding is not.** A line lands here verbatim and dated, without being judged. +Where it belongs is settled later, in a session, by a person or by an agent reading this file at +session start. + +## Convention + +- One line per subject, newest last, prefixed with the date it arrived: `- 2026-01-15 — `. +- **Verbatim.** Do not summarise an incoming line into what you think it means; that judgement is + the triage, and it happens after. +- **Mark it, do not delete it.** When a line is triaged, append ` → ` — an ADR + number, a `PLAN.md` item, a PRD, or `dropped`. The file is a record of what arrived, not a queue + that empties. +- Nothing here is a commitment. An inbox line that turns out to be a bad idea is triaged to + `dropped` and stays visible. + +## How lines get here + +Whatever suits you: typed by hand, pasted at the end of a conversation, or written by a script of +your own. **groundrules does not fill this file** — it only gives it a name and a place, and names +it in `CLAUDE.md`'s session-start list so a session reads it before deciding what to do. + +--- + +- diff --git a/skills/verify-bootstrap/SKILL.md b/skills/verify-bootstrap/SKILL.md index dfc82c0..531bdc4 100644 --- a/skills/verify-bootstrap/SKILL.md +++ b/skills/verify-bootstrap/SKILL.md @@ -55,7 +55,7 @@ If a signature is found: Search the whole file content for the **known placeholders** from bootstrap phase 5 (exact whitelist): ``` -\{\{(PROJECT_NAME|DESCRIPTION|STACK|DATE|HAS_PLAN|HAS_ARCHITECTURE|HAS_GLOSSARY|HAS_CHANGELOG|HAS_DATA_MODEL|HAS_SECURITY|HAS_DESIGN_SYSTEM|HAS_ROADMAP|HAS_I18N|HAS_PROCESS|HAS_RELEASE|HAS_AGENT_EVALS|GLOBAL_CLAUDE_NOTE|REMOTE_PROVIDER|REMOTE_VISIBILITY|CONTENT|INTENT_SOURCE|GOAL|USERS|CONSTRAINTS|NONGOALS|ACCEPTANCE)\}\} +\{\{(PROJECT_NAME|DESCRIPTION|STACK|DATE|HAS_PLAN|HAS_ARCHITECTURE|HAS_GLOSSARY|HAS_CHANGELOG|HAS_INBOX|HAS_DATA_MODEL|HAS_SECURITY|HAS_DESIGN_SYSTEM|HAS_ROADMAP|HAS_I18N|HAS_PROCESS|HAS_RELEASE|HAS_AGENT_EVALS|GLOBAL_CLAUDE_NOTE|REMOTE_PROVIDER|REMOTE_VISIBILITY|CONTENT|INTENT_SOURCE|GOAL|USERS|CONSTRAINTS|NONGOALS|ACCEPTANCE)\}\} ``` For every match, apply the **backtick rule** before flagging: a placeholder **wrapped in backticks** (`` `{{KEY}}` ``) is a *documentation reference* (a doc/CHANGELOG/PLAN line that mentions the placeholder by name), **not** an unsubstituted placeholder — **ignore it**. Only a **bare** occurrence (not inside backticks) is a real leftover. This matters on self-referential projects: a project's own `CHANGELOG.md`/`PLAN.md` legitimately contains backticked `{{HAS_PROCESS}}`, `{{INTENT_SOURCE}}`, etc.