From 757ad8a7419679b00aeab3c3424161c30b20fa58 Mon Sep 17 00:00:00 2001 From: GitLab CI Date: Tue, 25 Aug 2026 16:09:21 -0500 Subject: [PATCH 1/2] fix(ipa): partition Diataxis trees by audience and read placement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit diataxis-classification.md §12 adds an audience axis as an explicit project extension rather than Diataxis doctrine: an audience is a label plus the object its readers act on, the test keys on the object and returns the label, and resolution runs five ranks where aliases are permitted and fuzzy matching is forbidden. §13 fixes the four documentation roots and scopes the axis to the first row, so a request naming one of the other three is not a page request at all. ai-diataxis-scaffold asks the audience question once, before any directory exists, because the answer decides how many roots there are and no later answer can restructure a tree already seeded. The default answer reproduces the previous output byte for byte and prints no line naming an audience, a readership, or a second axis — a builder with one audience should not learn the axis exists. Any other answer creates one root per label, each holding four seeded mode directories and a landing page, and registers every root in the sidebar and navbar: a skill never creates an audience root it cannot register. `## Audiences` is optional and ai-init never interviews for it, which is why that section is absent from the gap table. It is distinct from the `Audience` row there — that one calibrates register for /ai-outline and /ai-draft and selects no placement, while `## Audiences` selects placement and calibrates nothing. framework-detection.md now carries the placement convention as a second read, independent of docs-root detection, since a project can set docs_path and still record placement only in prose. Both reads open CLAUDE.md by path rather than assuming ambient context, and read `.kiro/steering/*.md` under Kiro, where CLAUDE.md is never loaded. --- .claude/skills/ai-diataxis-scaffold/SKILL.md | 161 ++- .../ai-diataxis-scaffold/references/seeds.md | 124 ++- .claude/skills/ai-diataxis/SKILL.md | 155 ++- .claude/skills/ai-init/SKILL.md | 2 + .../diataxis-classification.md | 250 +++++ .../framework-detection.md | 4 +- .gitlab-ci.yml | 261 ----- .gitlab/merge_request_templates/default.md | 14 - docs/docs/developer-docs/internal/CLAUDE.md | 9 - .../internal/exploration/index.md | 20 - .../launchbridge-ipa-convergence.md | 299 ------ docs/docs/developer-docs/internal/index.md | 28 - .../internal/operations/index.md | 11 - .../internal/operations/runbooks/index.md | 11 - .../internal/operations/runbooks/releasing.md | 372 ------- internal/README.md | 22 - internal/infra/README.md | 106 -- internal/infra/codepipeline/Makefile | 325 ------ internal/infra/codepipeline/README.md | 227 ---- internal/infra/codepipeline/archive.py | 172 ---- internal/infra/codepipeline/empty_bucket.py | 206 ---- .../integration-test-pipeline.yml | 615 ----------- internal/infra/credential-vendor/Makefile | 243 ----- internal/infra/credential-vendor/README.md | 153 --- .../credential-vendor/credential-vendor.yml | 183 ---- internal/integration-tests/.gitignore | 20 - internal/integration-tests/.python-version | 1 - internal/integration-tests/DECISIONS.md | 952 ----------------- internal/integration-tests/Makefile | 127 --- internal/integration-tests/README.md | 239 ----- internal/integration-tests/compose/Makefile | 76 -- internal/integration-tests/compose/README.md | 89 -- .../integration-tests/compose/conftest.py | 48 - internal/integration-tests/compose/pytest.ini | 25 - .../integration-tests/compose/test_compose.py | 229 ---- internal/integration-tests/deploy/Makefile | 102 -- internal/integration-tests/deploy/README.md | 121 --- internal/integration-tests/deploy/conftest.py | 105 -- internal/integration-tests/deploy/pytest.ini | 21 - .../integration-tests/deploy/test_deploy.py | 451 -------- .../integration-tests/deploy/test_teardown.py | 107 -- .../integration-tests/harness/__init__.py | 0 internal/integration-tests/harness/answers.py | 344 ------- .../integration-tests/harness/artifacts.py | 191 ---- internal/integration-tests/harness/aws.py | 75 -- .../integration-tests/harness/cognito_auth.py | 136 --- internal/integration-tests/harness/driver.py | 606 ----------- .../integration-tests/harness/fixtures.py | 186 ---- .../integration-tests/harness/lifecycle.py | 86 -- .../integration-tests/harness/preflight.py | 693 ------------- internal/integration-tests/harness/reaper.py | 352 ------- .../integration-tests/harness/standing.py | 266 ----- internal/integration-tests/harness/sweep.py | 249 ----- .../harness/tests/pytest.ini | 16 - .../harness/tests/test_answers.py | 232 ----- .../harness/tests/test_artifacts.py | 233 ----- .../harness/tests/test_nudge.py | 337 ------ .../harness/tests/test_reaper.py | 348 ------- .../harness/tests/test_redaction.py | 193 ---- .../harness/tests/test_workspace.py | 185 ---- .../integration-tests/harness/workspace.py | 212 ---- internal/integration-tests/pyproject.toml | 22 - internal/integration-tests/uv.lock | 974 ------------------ internal/release/README.md | 107 -- internal/release/publish-github.sh | 452 -------- internal/release/templates/README.md | 63 -- internal/release/templates/cliff.toml | 77 -- internal/release/templates/release.mk | 117 --- scripts/.gitignore | 4 - 69 files changed, 673 insertions(+), 12769 deletions(-) delete mode 100644 .gitlab-ci.yml delete mode 100644 .gitlab/merge_request_templates/default.md delete mode 100644 docs/docs/developer-docs/internal/CLAUDE.md delete mode 100644 docs/docs/developer-docs/internal/exploration/index.md delete mode 100644 docs/docs/developer-docs/internal/exploration/launchbridge-ipa-convergence.md delete mode 100644 docs/docs/developer-docs/internal/index.md delete mode 100644 docs/docs/developer-docs/internal/operations/index.md delete mode 100644 docs/docs/developer-docs/internal/operations/runbooks/index.md delete mode 100644 docs/docs/developer-docs/internal/operations/runbooks/releasing.md delete mode 100644 internal/README.md delete mode 100644 internal/infra/README.md delete mode 100644 internal/infra/codepipeline/Makefile delete mode 100644 internal/infra/codepipeline/README.md delete mode 100644 internal/infra/codepipeline/archive.py delete mode 100644 internal/infra/codepipeline/empty_bucket.py delete mode 100644 internal/infra/codepipeline/integration-test-pipeline.yml delete mode 100644 internal/infra/credential-vendor/Makefile delete mode 100644 internal/infra/credential-vendor/README.md delete mode 100644 internal/infra/credential-vendor/credential-vendor.yml delete mode 100644 internal/integration-tests/.gitignore delete mode 100644 internal/integration-tests/.python-version delete mode 100644 internal/integration-tests/DECISIONS.md delete mode 100644 internal/integration-tests/Makefile delete mode 100644 internal/integration-tests/README.md delete mode 100644 internal/integration-tests/compose/Makefile delete mode 100644 internal/integration-tests/compose/README.md delete mode 100644 internal/integration-tests/compose/conftest.py delete mode 100644 internal/integration-tests/compose/pytest.ini delete mode 100644 internal/integration-tests/compose/test_compose.py delete mode 100644 internal/integration-tests/deploy/Makefile delete mode 100644 internal/integration-tests/deploy/README.md delete mode 100644 internal/integration-tests/deploy/conftest.py delete mode 100644 internal/integration-tests/deploy/pytest.ini delete mode 100644 internal/integration-tests/deploy/test_deploy.py delete mode 100644 internal/integration-tests/deploy/test_teardown.py delete mode 100644 internal/integration-tests/harness/__init__.py delete mode 100644 internal/integration-tests/harness/answers.py delete mode 100644 internal/integration-tests/harness/artifacts.py delete mode 100644 internal/integration-tests/harness/aws.py delete mode 100644 internal/integration-tests/harness/cognito_auth.py delete mode 100644 internal/integration-tests/harness/driver.py delete mode 100644 internal/integration-tests/harness/fixtures.py delete mode 100644 internal/integration-tests/harness/lifecycle.py delete mode 100644 internal/integration-tests/harness/preflight.py delete mode 100644 internal/integration-tests/harness/reaper.py delete mode 100644 internal/integration-tests/harness/standing.py delete mode 100644 internal/integration-tests/harness/sweep.py delete mode 100644 internal/integration-tests/harness/tests/pytest.ini delete mode 100644 internal/integration-tests/harness/tests/test_answers.py delete mode 100644 internal/integration-tests/harness/tests/test_artifacts.py delete mode 100644 internal/integration-tests/harness/tests/test_nudge.py delete mode 100644 internal/integration-tests/harness/tests/test_reaper.py delete mode 100644 internal/integration-tests/harness/tests/test_redaction.py delete mode 100644 internal/integration-tests/harness/tests/test_workspace.py delete mode 100644 internal/integration-tests/harness/workspace.py delete mode 100644 internal/integration-tests/pyproject.toml delete mode 100644 internal/integration-tests/uv.lock delete mode 100644 internal/release/README.md delete mode 100755 internal/release/publish-github.sh delete mode 100644 internal/release/templates/README.md delete mode 100644 internal/release/templates/cliff.toml delete mode 100644 internal/release/templates/release.mk delete mode 100644 scripts/.gitignore diff --git a/.claude/skills/ai-diataxis-scaffold/SKILL.md b/.claude/skills/ai-diataxis-scaffold/SKILL.md index e542c35..1c05706 100644 --- a/.claude/skills/ai-diataxis-scaffold/SKILL.md +++ b/.claude/skills/ai-diataxis-scaffold/SKILL.md @@ -40,6 +40,17 @@ before you run.** cannot adopt a structure they have never seen, and that every directory here ships with a page in it. +**The same disclaimer covers the second axis.** When step 2's audience question is +answered with anything but its default, this skill builds a tree partitioned by +reader as well as by mode. The one upstream page that raises that shape — + +(snapshot captured 2026-08-02, retrieved 2026-08-24; the live URL returns 404 as of +2026-08-24, so the guidance is published-but-withdrawn) — **presents it only as a +question it declines to answer**: "Which better? There seems to be a lot of +repetition in either cases." So claim no source support for the two-axis shape +either. State that Diátaxis does not endorse it, name that page as raising and +declining it, and say the argument for offering it is this project's own. + **So: never create a directory you do not also seed.** That is not a stylistic preference. Git does not track empty directories, so four empty directories produce zero commits and there is nothing to hand over; and an autogenerated @@ -95,6 +106,40 @@ On confirmation, write `docs_path` into `.context/README.md` frontmatter with th Edit tool and report that the interview will not repeat. If the user declines, use the value for this run only. +### Step 2b: Ask the audience question — once, before anything is created + +**Ask this in step 2 and not later.** Step 5 creates directories, and the above-mode +shape cannot be adopted after that: once `docs_path/how-to/` holds six pages, moving +to `user-docs/how-to/` relocates every page and breaks every cross-mode index link. +The one moment the choice costs nothing is before the tree exists. + +Use `AskUserQuestion` with three options: + +- **No audiences** *(recommended)* — the four mode directories go directly under + `docs_path`. One readership, which is the ordinary case. +- **A user tree and a developer tree** — two audience roots, each holding its own + four mode directories. +- **Something else** — the builder names their audiences. + +Write the question to the same standard step 2 already sets for the docs root: say +that **the answer determines where the four mode directories go**, name what will be +created under each option, and say that **changing it later moves every page.** + +**The options are examples of a shape, not a vocabulary.** `user` and `developer` are +offered because they are the common pair; a project that picks "something else" names +its own labels with no suggestion from this skill. An audience has a **label** — the +project's own word — and an **object**, the thing that audience's readers act on +(*the released command-line tool*, *this repository's source tree*). **The object is +never a person**, and it is what the landing page seed needs. The rule in full is +reference §12 in +`.claude/skills/ai-skills-reference/diataxis-classification.md`. + +**Do not write the answer into `.context/README.md`.** `/ai-init` is the sole writer +of that file's body, and `## Audiences` is never interviewed for there. Instead, when +the answer is anything but the default, **print the `## Audiences` section for the +user to paste**, one bullet per audience carrying its label and the object its readers +act on. + ## Step 3: Detect the static-site generator Run the detection table in `framework-detection.md` again, this time for the @@ -133,6 +178,32 @@ generator: Docusaurus (docs/docusaurus.config.ts) Creating 2 directories, 2 index pages, 5 template files. Writing 0 existing files. ``` +**When step 2b returned audiences, report the whole shape in the same form**, and +report it **before writing anything** — the count is the number most worth seeing +early, because it is more than double the default: + +```text +docs_path: docs/docs (from .context/README.md) +generator: Docusaurus (docs/docusaurus.config.ts) +audiences: user, developer (from the step 2b answer) + + user-docs/ missing — will create + seed index.md + user-docs/tutorial/ missing — will create + seed index.md + user-docs/how-to/ missing — will create + seed index.md + user-docs/reference/ missing — will create + seed index.md + user-docs/explanation/ missing — will create + seed index.md + developer-docs/ missing — will create + seed index.md + developer-docs/tutorial/ missing — will create + seed index.md + developer-docs/how-to/ missing — will create + seed index.md + developer-docs/reference/ missing — will create + seed index.md + developer-docs/explanation/ missing — will create + seed index.md + _templates/ missing — will copy 5 templates + ../CLAUDE.md missing — will write + +Creating 10 directories, 10 index pages, 5 template files. Writing 0 existing files. +Against 4 directories and 4 pages for the no-audiences answer. +``` + **Never overwrite.** Where a target file already exists, warn and ask whether to overwrite or skip; default to skip. An existing page must be byte-identical after the run. @@ -148,10 +219,58 @@ say plainly that moving existing pages into modes is not this skill's job: ## Step 5: Create the missing directories and seed each one -For each of the four modes that is missing, create the directory **and** write its -`index.md`. The four seed shapes are in `references/seeds.md` — read it and use -them; they carry the frontmatter keys, the reader-facing paragraph, the fenced -worked example, and the cross-mode links. +Branch on step 2b's answer. The two branches share every non-negotiable below. + +### 5a. The default answer — no audiences + +Create the four mode directories directly under `docs_path`, seed each one, and +**change nothing else.** This path must be **byte-identical to a run of this skill +before the audience question existed**: four directories, four index pages, and **no +output line naming an audience, a readership, a shape, or a second axis.** Not even +to say the question was asked and declined. + +That is the point rather than a nicety. A builder who answered "no audiences" has one +readership, and a scaffold that then tells them about a distinction selecting nothing +has reintroduced exactly the noise the axis was designed to avoid. + +### 5b. Any other answer — one root per audience + +Create **one audience root per declared audience**, each holding the four seeded mode +directories, plus **one landing page per root**. For two audiences that is ten +directories and ten pages, reported in step 4's form before any of it is written. + +Then **print the registration lines and stop there**: + +- **Docusaurus** — one sidebar entry and one navbar item per root: + + ```text + // sidebars.ts — one per audience root + userDocsSidebar: [{type: 'autogenerated', dirName: 'user-docs'}], + developerDocsSidebar: [{type: 'autogenerated', dirName: 'developer-docs'}], + + // docusaurus.config.ts — navbar.items, one per audience root + { type: 'docSidebar', sidebarId: 'userDocsSidebar', position: 'left', label: 'User docs' }, + { type: 'docSidebar', sidebarId: 'developerDocsSidebar', position: 'left', label: 'Developer docs' }, + ``` + +- **Another generator, or none detected** — name what it registers a top-level tree + with, or say that no generator was detected and the tree will not render at all. + +**Edit no configuration file, and say the tree is unreachable from the site's +navigation until those lines are added.** This is the failure most easily produced by +being helpful: pages that exist, build, and no reader can reach. Reference §12 states +the rule — a skill never creates an audience root it cannot register, so a skill that +creates one hands over the registration. + +**Repeat the no-endorsement disclaimer here**, in one line, naming the withdrawn +upstream page as raising the two-axis shape and declining to recommend it. + +### Seeding, both branches + +The four mode seed shapes are in `references/seeds.md` — read it and use them; they +carry the frontmatter keys, the reader-facing paragraph, the fenced worked example, +and the cross-mode links. The **audience landing page** is the fifth shape in that +same file, used only in 5b. Non-negotiables, all checked in step 8: @@ -198,9 +317,21 @@ else: directory becomes a page, so the agent-context file would publish as documentation. -The content is in `references/seeds.md`. Then **print the `AGENTS.md` snippet -rather than writing it** — no skill in this collection writes that file; `/ai-init` -prints one too. +The content is in `references/seeds.md`, and **which variant you use follows step +2b**: + +- **Default answer (no audiences)** → use the docs-tree `CLAUDE.md` seed **exactly as + written, byte for byte.** It gains nothing about audiences, shapes, or thresholds. +- **Any other answer** → use the audience variant in the same file. It adds the + audience paths filled in from the interview answer, plus the two lines a two-axis + tree needs that a flat one does not: **a page's home is its mode**, and **a mode + index gets grouped only once its list passes seven items.** + +Both lines are conditional for the same reason 5a is byte-identical: the default path +must not acquire prose about an axis the builder declined. + +Then **print the `AGENTS.md` snippet rather than writing it** — no skill in this +collection writes that file; `/ai-init` prints one too. ## Step 8: Self-audit @@ -215,6 +346,22 @@ build: `onBrokenMarkdownLinks` defaults to `warn`, so a page carrying an unfille failure this skill's whole justification rests on not happening. - **Every file you did not intend to touch is unchanged.** +When step 2b returned audiences, also check: + +- **Every audience root holds at least one landing page and four seeded mode + directories.** A root with modes and no landing page has no entry point; a root with + a landing page and no modes is an empty structure by another name. +- **Every cross-mode link inside a root resolves within that root.** `../how-to/` from + `user-docs/tutorial/index.md` must reach `user-docs/how-to/`, never + `docs_path/how-to/`. A link that escapes its root sends a reader to the other + audience's tree, and the site build will not catch it because the target exists. +- **No landing page contains a `{`.** The registration lines you printed do contain + braces; they are printed output, not an emitted page, so the rule does not reach + them — but it does reach every landing page, and that is the one seed shape written + from the interview answer rather than copied. +- **No configuration file was modified.** `sidebars.ts`, `docusaurus.config.ts`, and + their equivalents must be byte-identical after the run. + Fix anything that fails before reporting completion. ## Report diff --git a/.claude/skills/ai-diataxis-scaffold/references/seeds.md b/.claude/skills/ai-diataxis-scaffold/references/seeds.md index 42faca4..cde5c7f 100644 --- a/.claude/skills/ai-diataxis-scaffold/references/seeds.md +++ b/.claude/skills/ai-diataxis-scaffold/references/seeds.md @@ -1,14 +1,23 @@ # Mode Index Seed Shapes -The four index pages the scaffold skill writes, one per Diátaxis mode. Each is a -complete page, not a fragment: a reader who opens a freshly scaffolded tree finds -prose that tells them what the mode is for and one example of a page in it. - -Every seed below is **generic by design**. It describes its mode in reader terms -and carries a fenced example with no project specifics, because the scaffold runs -before any page exists to be specific about. Project-specific pages come from +The pages the scaffold skill writes: **four mode index pages**, one per Diátaxis +mode, and — only when the audience question returned audiences — **one landing page +per audience root**. Each is a complete page, not a fragment: a reader who opens a +freshly scaffolded tree finds prose that tells them what the mode is for and one +example of a page in it. + +The four mode seeds are **generic by design**. Each describes its mode in reader +terms and carries a fenced example with no project specifics, because the scaffold +runs before any page exists to be specific about. Project-specific pages come from `ai-diataxis` CREATE afterwards. +**The audience landing seed is the one exception, and it is a shape rather than a +copyable block.** Its content is *this* project's audience label and the object that +audience's readers act on, both of which come from the interview answer, so there is +nothing generic to copy. It uses `` slots and every one of them is +filled before the page is written — the no-`{` rule below covers braces, and an +emitted page carries no unfilled angle brackets either. + This file is shared byte-identically by both runtimes, so it names skills without a leading slash and cites no path that only one runtime has. @@ -162,6 +171,59 @@ For the steps to do something, see [how-to](../how-to/). For what a setting does, see [reference](../reference/). ```` +## Seed: the audience landing page + +**Used only when the audience question returned audiences.** One per audience root, +written at `/index.md`. A shape to fill rather than a block to copy: +`` is the label the builder gave, and `` is the thing that +audience's readers act on — *the released command-line tool*, *this repository's +source tree*. **The object is never a person.** + +````markdown +--- +title: "" +sidebar_label: "" +sidebar_position: 1 +--- + +# documentation + +For readers working on . Everything in this section is written for that +work; documentation for a different reader lives in its own section. + +The four kinds of page here answer four different questions: + +- [Tutorial](tutorial/) — teach me to use for the first time +- [How-to guides](how-to/) — I already know what I want to do; show me the steps +- [Reference](reference/) — tell me exactly what this option does +- [Explanation](explanation/) — help me understand why it works this way + +A page belongs to one of those four and to no other. Which one it belongs to +depends on what the reader needs at that moment, not on what the page is about. +```` + +**Filling it:** + +- **Replace every ``.** A finished page contains no `` placeholder and + no `{` character. Both are checked at skill step 8. +- **The mode links are relative to the root** — `tutorial/`, not `../tutorial/`. + This page sits one level *above* the mode directories, where a mode index sits + inside one. Getting this wrong sends every link to the other audience's tree, or + out of the docs root entirely. +- **Name the object in the builder's own words from the interview, never a job + title:** "the released command-line tool", not "end users". A label names the + audience; the object names what they act on, and the two are not + interchangeable. +- **Add a cross-link to the other roots only if there is more than one, and only to + roots this run created.** A link to a tree that does not exist fails the build + under `onBrokenLinks: 'throw'`. +- **Expect the modes to sort alphabetically in the sidebar, not in Diátaxis + order.** All four mode index pages carry `sidebar_position: 1`, so inside one + audience root they tie and an autogenerated sidebar falls back to alphabetical — + explanation, how-to, reference, tutorial. Say so when handing over the + registration lines. Renumbering them is not available here: the four mode seeds + are shared with the no-audiences answer, which must stay byte-identical. + ## The docs-tree `CLAUDE.md` Written to the **parent** of `docs_path` when that parent is inside the docs site @@ -192,6 +254,54 @@ implicit "About". Tutorial and reference titles carry no pattern. name begins with `_` are excluded from the build by convention. ``` +### The audience variant + +**Used only when the audience question returned audiences**, in place of the block +above — never in addition to it. On the default answer the block above is written +exactly as it stands, so that path acquires no prose about an axis the builder +declined. + +Fill one `- /` line per audience root from the interview answer. + +```markdown +# / + +Documentation content, partitioned first by **audience** and then by +[Diátaxis](https://diataxis.fr) mode — one directory per audience, four per audience. + +- / — for readers working on +- / — for readers working on + +Inside each audience directory: + +- `tutorial/` — learning-oriented: one guided path, no choices +- `how-to/` — task-oriented: the reader brings the goal +- `reference/` — information-oriented: mirrors the product +- `explanation/` — understanding-oriented: why, not how + +A page's home is its **mode**. The audience directory says who the page is for; the +mode directory inside it says what kind of page it is, and it is the mode that +decides which of the four directories a page goes in. A page serves exactly one +mode. When a page starts serving two, split it rather than adding a section — the +mode a reader is in determines what they can absorb. + +Put a new page in the audience whose readers act on the thing it describes, then in +the directory matching what those readers need — not what the content is about. + +A mode index lists its pages flatly and gets grouped only once that list passes +seven items. Below that, grouping costs a reader more than the flat list does. + +A how-to title begins "How to". An explanation title reads naturally after an +implicit "About". Tutorial and reference titles carry no pattern. + +`_templates/` holds the page templates and is not published. Directories whose +name begins with `_` are excluded from the build by convention. +``` + +Replace every `` before writing. This file is agent context rather than a +published page, so the no-`{` rule does not reach it — but an unfilled slot here +misinforms every agent that reads it, which is worse than a broken build. + ## The `AGENTS.md` snippet Always printed, never written — no skill in this collection writes that file. diff --git a/.claude/skills/ai-diataxis/SKILL.md b/.claude/skills/ai-diataxis/SKILL.md index 6572a8a..5727fb0 100644 --- a/.claude/skills/ai-diataxis/SKILL.md +++ b/.claude/skills/ai-diataxis/SKILL.md @@ -69,8 +69,21 @@ Read `.context/README.md` and resolve `docs_path` from its frontmatter: the Edit tool, and report that the interview will not repeat. Do not block if the user declines — proceed with the detected value for this run only. +**Read the placement convention unconditionally.** Separately from resolving +`docs_path`, read `dirname(docs_path)/CLAUDE.md` **by path** — and the project +root's `CLAUDE.md` when that is a different file. This is rank 3 of the audience +resolution ladder (reference §12.3), and it is **not** conditional on `docs_path` +being absent: a project can carry `docs_path` in its context file and record where +a new page goes only in prose, so a read that fires only on a missing `docs_path` +never consults the top-ranked signal. +`.claude/skills/ai-skills-reference/framework-detection.md` ranks this signal +first for that reason. Do not rely on either file being in ambient context. + Also read the project's Voice, Key Terms, Principles, and Constraints from -`.context/README.md` — CREATE applies them to every page it writes. +`.context/README.md` — CREATE applies them to every page it writes. Read its +`## Audiences` section too, if it has one (reference §12.2). **Absence is the +normal case and is not a warning** — a project that declares no audiences has one, +and the axis selects nothing there. ### The walk exclusion rule @@ -153,6 +166,37 @@ also wrong" the compass warns about. The gate always stops and asks. The request describes content that does not exist yet. Produce exactly one page, in exactly one mode directory. +### Step C0: Check which root the request belongs to + +**Run this before C1.** A request that should never become a published page must +not be classified either — classifying it assigns a mode, and a mode assignment is +what sends it into the tree. + +Reference §13 lists four documentation roots. This skill writes to exactly one of +them, the published docs tree. If the request names an artifact belonging to one +of the other three, **write no page** and report which skill owns it: + +| The request names | Owner | Root | +|---|---|---| +| how a feature, subsystem, or module was actually built | `/ai-as-built` | the working root | +| an architectural decision, or a record of one | `/ai-adr` | the decision log | +| agent context for a source directory — a `CLAUDE.md`, or a directory's own `README.md` | `/ai-context` | agent context, by proximity | +| research, a plan, a design, an outline, or a draft | the working-root pipeline — `/ai-research`, `/ai-architect`, `/ai-plan`, `/ai-outline`, `/ai-draft` | the working root | + +"Document how we built the release pipeline" is the case this step exists for. It +reads as a documentation request, resolves to no existing path, and therefore +reaches CREATE — where Mode Detection has already branched on path resolution +alone and nothing has yet asked which root it belongs to. Name `/ai-as-built` and +stop. + +**One test, so this step does not swallow legitimate work.** A request for a page +*about* one of these things is still a page request: "explain why we keep a +decision log" is an explanation page, not a decision record. The discriminator is +whether the request asks for **the artifact itself** or for **a page addressed to +a reader who came to the site.** When it reads as both, ask with +`AskUserQuestion`, offering the two readings — the same posture Mode Detection +takes on an ambiguous argument. + ### Step C1: Classify the request Run **Shared: Classify** on the request text. @@ -164,13 +208,53 @@ caching works and show me how to configure it" (cognition/acquisition + action/application) — stops here and proposes two cross-linked pages. Write nothing until the user confirms. +### Step C2b: Resolve the audience + +**After the gate and before placement.** It cannot live in Mode Detection, which +resolves `$ARGUMENTS` against `docs_path` first and would then be reading the same +argument twice for two different purposes. + +1. Work down the **five-rank ladder** in reference §12.3, stopping at the first + rank that decides: the request's own words (matched by *position* — `for the +