Conversation
Durable project memory now lives in lode/, so an agent starting cold reads the system as it is instead of re-deriving it from 52K of README and a CLAUDE.md whose Rails floor the gemspec never declares. What the lode holds: summary, terminology and practices as the baseline; lode-map.md as the index; seven area summaries — core (loader, engine, #render_page, Scope), config (the 36 knobs and the value objects), registry-and-versions (the page DSL, DocVersion, Snapshot), components (the 30 DocsUI classes and the one docs-nav controller), ai-surfaces (the Markdown twin, llms.txt, search, MCP, the OpenAPI bridge), install-path (the generator's 20 steps, SyncReport, the migrations, the CLI), and testing-and-ci (the four spec layers, the three CI jobs, release, deploy, the dogfood site). Every line range and count in them was computed with a script, not read off the screen. lode/workflow.md fills all ten headings from the code, CLAUDE.md, .claude/rules and the local commands it replaces, so the shared /lode: skills behave as if written for this repo: the SimpleCov floor that makes a single-file rspec run exit non-zero, the docs-site job's BUNDLE_FROZEN escape, the tracked docs/ lockfiles and how to resolve them, and the reviewer suggestions that are wrong here (add railties, use CurrentAttributes, name the reader #config, interpolate a Tailwind class, render json: the JSON-RPC envelope). Review rules: one, in lode/review/cli-and-templates.md — that docs-kit new resolves every DOCS_KIT_* value before the template runs, so the CLI default, its two help-text mentions and the template fallback must move together. It merges cubic learning ec3d720b with PR #70's accepted review thread, the only merged PR in the repo carrying review comments. The other six cubic learnings describe TAILWIND_SOURCES_COVER, ignore_generated_css_sources and SyncReport.tailwind_sources_rule, none of which exist on main — they live on open PR #72 — so writing them here would have asserted behaviour the shipping tree does not have. They are held with their safe failure directions for /lode:learn to import the day #72 merges. Retired in favour of the plugin: /lfg, /plan, /tdd, /github-review-pr, /github-review-failures and /github-review-comments. Their repo-specific content — the conflict runbook, the CI quirks, the constraint tables — moved into lode/workflow.md rather than being deleted. /architect, /security and /review-pr stay: they are docs-kit specialists the plugin does not cover. Claude-Session: https://claude.ai/code/session_01Hga3idYBp7fWdj7GmmfofF
There was a problem hiding this comment.
10 issues found across 24 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lode/config/summary.md">
<violation number="1" location="lode/config/summary.md:30">
P3: This description misclassifies several configuration objects as `Data.define` values. Split the immutable `Data.define` objects from the plain mutable/config-normalization classes so maintainers do not infer the wrong initialization or mutability contract.</violation>
<violation number="2" location="lode/config/summary.md:46">
P3: Malformed `markup:` and `file:` logos are not checked when configuration assigns them. `Configuration#brand_logo` builds and validates the logo on first read, so change this claim to describe lazy validation or eagerly normalize in `brand_logo=`.</violation>
</file>
<file name="lode/plans/README.md">
<violation number="1" location="lode/plans/README.md:13">
P3: The claim that `.claude/README.md` said `docs/plans/` exists doesn't hold against the current file: it only records `/plan` as retired in favour of `/lode:plan` and never mentions `docs/plans/`. Nothing else in the repo references `docs/plans/`. Since `lode/` is meant to be precisely verifiable, either cite the historical text (e.g. the retired command's old description) or drop the `.claude/README.md` attribution.</violation>
</file>
<file name="lode/workflow.md">
<violation number="1" location="lode/workflow.md:18">
P3: `ruby exe/docs-kit --help` exits 1 because the CLI treats `--help` as an invalid command and only accepts `new`. Document a valid `new NAME` invocation or add a real help option before using this command as the scaffolder check.</violation>
<violation number="2" location="lode/workflow.md:36">
P3: The AI-surfaces row lists `lib/docs_kit/{blocks,inline,table}.rb`, but those files live under `lib/docs_kit/markdown_export/`. An agent following this profile to find them would land on nonexistent paths. Fix the brace list to reflect the real locations: `markdown_export/` for blocks/inline/table and `search_index.rb`/`search_hit.rb` flat.</violation>
<violation number="3" location="lode/workflow.md:135">
P3: The flake-stress command is fish-only, so it fails before running any specs in Bash. Use a POSIX loop with `$(seq 50)`, `do`, and `done` instead.</violation>
</file>
<file name=".claude/settings.json">
<violation number="1" location=".claude/settings.json:11">
P2: The `lode@zoolutions` plugin is enabled with `true` (no version) and the marketplace source declares no `commit`, so Claude Code resolves the pre-PR gate to the latest state of the `zoolutions/claude-plugins` default branch. The enforced gate can therefore change on every external push without review, and it runs with broad permissions from `.claude/settings.local.json` (`Bash(git *)`, `Bash(bundle exec:*)`, `Bash(rake *)`, `mcp__github__*`). Pin the marketplace commit and the plugin version so the gate is immutable and reproducible.</violation>
</file>
<file name="lode/practices.md">
<violation number="1" location="lode/practices.md:11">
P3: The citation `Snapshot::Entry#markdown` does not resolve in `snapshot.rb`: the file defines no `Entry` class and no `#markdown` method (only `markdown_for` at line 112). Every other claim in this file resolves to its cited location; this one will send a reader searching the wrong file. Point the citation at the file/method that actually implements the unreadable-file-returns-`""` behavior.</violation>
</file>
<file name="lode/testing-and-ci/summary.md">
<violation number="1" location="lode/testing-and-ci/summary.md:42">
P2: This summary incorrectly says the published image includes `.git`; the repo-root `.dockerignore` removes it from the build context. Document the fallback gemspec packaging path instead, while retaining the valid `persist-credentials: false` hardening.</violation>
</file>
<file name="lode/ai-surfaces/summary.md">
<violation number="1" location="lode/ai-surfaces/summary.md:3">
P3: The opening architecture claim incorrectly includes the OpenAPI bridge among the page-derived surfaces. Clarify that the four registry-backed AI surfaces share these seams, while OpenAPI remains spec-driven.</violation>
</file>
Heads up: you’ve reached your flex budget. Increase your flex budget or wait for usage to reset.
Re-trigger cubic
| } | ||
| }, | ||
| "enabledPlugins": { | ||
| "lode@zoolutions": true |
There was a problem hiding this comment.
P2: The lode@zoolutions plugin is enabled with true (no version) and the marketplace source declares no commit, so Claude Code resolves the pre-PR gate to the latest state of the zoolutions/claude-plugins default branch. The enforced gate can therefore change on every external push without review, and it runs with broad permissions from .claude/settings.local.json (Bash(git *), Bash(bundle exec:*), Bash(rake *), mcp__github__*). Pin the marketplace commit and the plugin version so the gate is immutable and reproducible.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/settings.json, line 11:
<comment>The `lode@zoolutions` plugin is enabled with `true` (no version) and the marketplace source declares no `commit`, so Claude Code resolves the pre-PR gate to the latest state of the `zoolutions/claude-plugins` default branch. The enforced gate can therefore change on every external push without review, and it runs with broad permissions from `.claude/settings.local.json` (`Bash(git *)`, `Bash(bundle exec:*)`, `Bash(rake *)`, `mcp__github__*`). Pin the marketplace commit and the plugin version so the gate is immutable and reproducible.</comment>
<file context>
@@ -0,0 +1,13 @@
+ }
+ },
+ "enabledPlugins": {
+ "lode@zoolutions": true
+ }
+}
</file context>
|
|
||
| Two things bite callers. The caller must **grant** `packages: write` — a reusable workflow can only narrow the permissions it is given, so with the repo default of read-only the build job's `packages: write` exceeds the grant and the run is a `startup_failure`. And the `secrets:` inputs are declared `required: false` on purpose: callers pass them with `secrets: inherit`, and GitHub cannot statically confirm an inherited secret satisfies `required: true`, which is itself a `startup_failure`. They are still effectively required — the deploy step fails fast on an empty `DEPLOY_HOST`/`DEPLOY_DOMAIN`. | ||
|
|
||
| The build job checks out with `persist-credentials: false`: the image `COPY`s the repo root including `.git` (kept for the gemspec's `git ls-files`) and is pushed to a public GHCR package, so a persisted `GITHUB_TOKEN` in `.git/config` must not ride along into a published layer. |
There was a problem hiding this comment.
P2: This summary incorrectly says the published image includes .git; the repo-root .dockerignore removes it from the build context. Document the fallback gemspec packaging path instead, while retaining the valid persist-credentials: false hardening.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/testing-and-ci/summary.md, line 42:
<comment>This summary incorrectly says the published image includes `.git`; the repo-root `.dockerignore` removes it from the build context. Document the fallback gemspec packaging path instead, while retaining the valid `persist-credentials: false` hardening.</comment>
<file context>
@@ -0,0 +1,55 @@
+
+Two things bite callers. The caller must **grant** `packages: write` — a reusable workflow can only narrow the permissions it is given, so with the repo default of read-only the build job's `packages: write` exceeds the grant and the run is a `startup_failure`. And the `secrets:` inputs are declared `required: false` on purpose: callers pass them with `secrets: inherit`, and GitHub cannot statically confirm an inherited secret satisfies `required: true`, which is itself a `startup_failure`. They are still effectively required — the deploy step fails fast on an empty `DEPLOY_HOST`/`DEPLOY_DOMAIN`.
+
+The build job checks out with `persist-credentials: false`: the image `COPY`s the repo root including `.git` (kept for the gemspec's `git ls-files`) and is pushed to a public GHCR package, so a persisted `GITHUB_TOKEN` in `.git/config` must not ride along into a published layer.
+
+## The dogfood site — `docs/`
</file context>
| The build job checks out with `persist-credentials: false`: the image `COPY`s the repo root including `.git` (kept for the gemspec's `git ls-files`) and is pushed to a public GHCR package, so a persisted `GITHUB_TOKEN` in `.git/config` must not ride along into a published layer. | |
| The build job checks out with `persist-credentials: false`: the repo-root `.dockerignore` excludes `.git` from the image context, and `docs-kit.gemspec` falls back to a directory glob when Git metadata is absent. This prevents a persisted `GITHUB_TOKEN` in `.git/config` from entering the published layer. |
|
|
||
| `Shortcut` exists because three places must agree on one chord: the config surface, the server-rendered `<kbd>` hint (`#label`), and the `docs-nav` matcher (`#to_h`, serialised to JSON). `"mod"` stays abstract on the server — ⌘ on mac, Ctrl elsewhere — and the browser resolves it, so one config entry works on every platform. | ||
|
|
||
| `BrandLogo` accepts exactly one of five form keys (`svg:`, `paths:`, `markup:`, `file:`, `src:`); giving none or several is ambiguous and raises. The `markup:`/`file:` forms are shape-checked as an `<svg>` element at config time, because `DocsUI::Logo` embeds them verbatim through `raw(safe(...))`. |
There was a problem hiding this comment.
P3: Malformed markup: and file: logos are not checked when configuration assigns them. Configuration#brand_logo builds and validates the logo on first read, so change this claim to describe lazy validation or eagerly normalize in brand_logo=.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/config/summary.md, line 46:
<comment>Malformed `markup:` and `file:` logos are not checked when configuration assigns them. `Configuration#brand_logo` builds and validates the logo on first read, so change this claim to describe lazy validation or eagerly normalize in `brand_logo=`.</comment>
<file context>
@@ -0,0 +1,51 @@
+
+`Shortcut` exists because three places must agree on one chord: the config surface, the server-rendered `<kbd>` hint (`#label`), and the `docs-nav` matcher (`#to_h`, serialised to JSON). `"mod"` stays abstract on the server — ⌘ on mac, Ctrl elsewhere — and the browser resolves it, so one config entry works on every platform.
+
+`BrandLogo` accepts exactly one of five form keys (`svg:`, `paths:`, `markup:`, `file:`, `src:`); giving none or several is ambiguous and raises. The `markup:`/`file:` forms are shape-checked as an `<svg>` element at config time, because `DocsUI::Logo` embeds them verbatim through `raw(safe(...))`.
+
+## Related
</file context>
| `BrandLogo` accepts exactly one of five form keys (`svg:`, `paths:`, `markup:`, `file:`, `src:`); giving none or several is ambiguous and raises. The `markup:`/`file:` forms are shape-checked as an `<svg>` element at config time, because `DocsUI::Logo` embeds them verbatim through `raw(safe(...))`. | |
| The `markup:`/`file:` forms are shape-checked when `#brand_logo` first normalizes them, because `DocsUI::Logo` embeds them verbatim through `raw(safe(...))`. |
|
|
||
| ## Value objects | ||
|
|
||
| All are `Data.define` with a keyword `initialize` supplying defaults, and most carry a `.from` that passes an existing instance through and coerces a Hash with `transform_keys(&:to_sym)` — so a YAML- or JSON-sourced config loads cleanly. |
There was a problem hiding this comment.
P3: This description misclassifies several configuration objects as Data.define values. Split the immutable Data.define objects from the plain mutable/config-normalization classes so maintainers do not infer the wrong initialization or mutability contract.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/config/summary.md, line 30:
<comment>This description misclassifies several configuration objects as `Data.define` values. Split the immutable `Data.define` objects from the plain mutable/config-normalization classes so maintainers do not infer the wrong initialization or mutability contract.</comment>
<file context>
@@ -0,0 +1,51 @@
+
+## Value objects
+
+All are `Data.define` with a keyword `initialize` supplying defaults, and most carry a `.from` that passes an existing instance through and coerces a Hash with `transform_keys(&:to_sym)` — so a YAML- or JSON-sourced config loads cleanly.
+
+| Object | File | Role |
</file context>
| All are `Data.define` with a keyword `initialize` supplying defaults, and most carry a `.from` that passes an existing instance through and coerces a Hash with `transform_keys(&:to_sym)` — so a YAML- or JSON-sourced config loads cleanly. | |
| The immutable value objects (`NavItem`, `TopbarLink`, `DocVersion`, `SearchHit`, `ApiClient`, and `ApiRequest`) use `Data.define` with keyword initializers; `Shortcut`, `BrandLogo`, `SeoConfig`, and `LandingConfig` are plain classes, and most provide `.from` coercion for Hash-backed config. |
| `lode/plans/YYYY-MM-DD-<slug>.md`. Use it only when the plan should not be public | ||
| or is a working draft that will become an issue. | ||
|
|
||
| `docs/plans/` does **not** exist in this repository, despite what the retired |
There was a problem hiding this comment.
P3: The claim that .claude/README.md said docs/plans/ exists doesn't hold against the current file: it only records /plan as retired in favour of /lode:plan and never mentions docs/plans/. Nothing else in the repo references docs/plans/. Since lode/ is meant to be precisely verifiable, either cite the historical text (e.g. the retired command's old description) or drop the .claude/README.md attribution.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/plans/README.md, line 13:
<comment>The claim that `.claude/README.md` said `docs/plans/` exists doesn't hold against the current file: it only records `/plan` as retired in favour of `/lode:plan` and never mentions `docs/plans/`. Nothing else in the repo references `docs/plans/`. Since `lode/` is meant to be precisely verifiable, either cite the historical text (e.g. the retired command's old description) or drop the `.claude/README.md` attribution.</comment>
<file context>
@@ -0,0 +1,21 @@
+`lode/plans/YYYY-MM-DD-<slug>.md`. Use it only when the plan should not be public
+or is a working draft that will become an issue.
+
+`docs/plans/` does **not** exist in this repository, despite what the retired
+`/plan` command and `.claude/README.md` said. `docs/` is the dogfood Rails app;
+nothing under it is a plan.
</file context>
| |---|---|---| | ||
| | Client runtime | `app/javascript/docs_kit/controllers/docs_nav_controller.js` (the only one) | owned here — but it may only *enhance*. Never add a second controller; never make a page require it. | | ||
| | Components | `app/components/docs_ui/*.rb` (30 classes + `page_helpers.rb`) | owned here. New chrome is a `DocsUI::` component, never raw daisyUI markup, and it reads `DocsKit.configuration` rather than hardcoding. | | ||
| | AI surfaces | `lib/docs_kit/{markdown_export,blocks,inline,table,llms_text,search_index,search_hit,mcp_tools,mcp_server,open_api}.rb`, `app/controllers/docs_kit/*.rb` | owned here. A new consumer calls `LlmsText.pages` / `.renderable_for`; it does not re-derive the page list. | |
There was a problem hiding this comment.
P3: The AI-surfaces row lists lib/docs_kit/{blocks,inline,table}.rb, but those files live under lib/docs_kit/markdown_export/. An agent following this profile to find them would land on nonexistent paths. Fix the brace list to reflect the real locations: markdown_export/ for blocks/inline/table and search_index.rb/search_hit.rb flat.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/workflow.md, line 36:
<comment>The AI-surfaces row lists `lib/docs_kit/{blocks,inline,table}.rb`, but those files live under `lib/docs_kit/markdown_export/`. An agent following this profile to find them would land on nonexistent paths. Fix the brace list to reflect the real locations: `markdown_export/` for blocks/inline/table and `search_index.rb`/`search_hit.rb` flat.</comment>
<file context>
@@ -0,0 +1,136 @@
+|---|---|---|
+| Client runtime | `app/javascript/docs_kit/controllers/docs_nav_controller.js` (the only one) | owned here — but it may only *enhance*. Never add a second controller; never make a page require it. |
+| Components | `app/components/docs_ui/*.rb` (30 classes + `page_helpers.rb`) | owned here. New chrome is a `DocsUI::` component, never raw daisyUI markup, and it reads `DocsKit.configuration` rather than hardcoding. |
+| AI surfaces | `lib/docs_kit/{markdown_export,blocks,inline,table,llms_text,search_index,search_hit,mcp_tools,mcp_server,open_api}.rb`, `app/controllers/docs_kit/*.rb` | owned here. A new consumer calls `LlmsText.pages` / `.renderable_for`; it does not re-derive the page list. |
+| Registry + versions | `lib/docs_kit/{registry,doc_version,snapshot,nav_item}.rb` | owned here. `Registry`'s public API is what sites depend on — additive only. |
+| Config | `lib/docs_kit/configuration.rb` plus the value objects | owned here. A new knob ships with a default that renders nothing. |
</file context>
| | AI surfaces | `lib/docs_kit/{markdown_export,blocks,inline,table,llms_text,search_index,search_hit,mcp_tools,mcp_server,open_api}.rb`, `app/controllers/docs_kit/*.rb` | owned here. A new consumer calls `LlmsText.pages` / `.renderable_for`; it does not re-derive the page list. | | |
| | AI surfaces | `lib/docs_kit/markdown_export/{blocks,inline,table}.rb`, `lib/docs_kit/{llms_text,search_index,search_hit,mcp_tools,mcp_server,open_api}.rb`, `app/controllers/docs_kit/*.rb` | owned here. A new consumer calls `LlmsText.pages` / `.renderable_for`; it does not re-derive the page list. | |
|
|
||
| ## Degrade on the render path; raise at config time | ||
|
|
||
| A render must not take the site down for a config mistake that has a sensible fallback: an unresolvable Rouge theme name returns nil and falls back to the default (`Configuration#resolve_theme`, `configuration.rb:650-654`), an unreadable snapshot manifest reads back as an empty snapshot (`Snapshot#read_manifest`, `snapshot.rb:140-149`), an unreadable snapshot file renders as `""` (`Snapshot::Entry#markdown`), an unknown `params[:version]` resolves to the current version rather than 404ing (`Configuration#resolve_version`, `configuration.rb:391-393`). |
There was a problem hiding this comment.
P3: The citation Snapshot::Entry#markdown does not resolve in snapshot.rb: the file defines no Entry class and no #markdown method (only markdown_for at line 112). Every other claim in this file resolves to its cited location; this one will send a reader searching the wrong file. Point the citation at the file/method that actually implements the unreadable-file-returns-"" behavior.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/practices.md, line 11:
<comment>The citation `Snapshot::Entry#markdown` does not resolve in `snapshot.rb`: the file defines no `Entry` class and no `#markdown` method (only `markdown_for` at line 112). Every other claim in this file resolves to its cited location; this one will send a reader searching the wrong file. Point the citation at the file/method that actually implements the unreadable-file-returns-`""` behavior.</comment>
<file context>
@@ -0,0 +1,49 @@
+
+## Degrade on the render path; raise at config time
+
+A render must not take the site down for a config mistake that has a sensible fallback: an unresolvable Rouge theme name returns nil and falls back to the default (`Configuration#resolve_theme`, `configuration.rb:650-654`), an unreadable snapshot manifest reads back as an empty snapshot (`Snapshot#read_manifest`, `snapshot.rb:140-149`), an unreadable snapshot file renders as `""` (`Snapshot::Entry#markdown`), an unknown `params[:version]` resolves to the current version rather than 404ing (`Configuration#resolve_version`, `configuration.rb:391-393`).
+
+The exceptions are deliberate and are all *configuration shape* errors, where there is nothing meaningful to degrade to: `c.topbar_brand` outside `TOPBAR_BRAND_MODES` raises `ArgumentError` at assignment (`configuration.rb:219`), `on_page` outside `ON_PAGE_MODES` raises in `coerce_on_page_mode` (`configuration.rb:535-543`), a malformed `c.brand_logo` raises on first read (`configuration.rb:352-356`), and `openapi_document` raises `DocsKit::Error` naming the knob when `c.openapi` is unset (`configuration.rb:443-451`). Loud at boot beats silently wrong in production.
</file context>
| @@ -0,0 +1,53 @@ | |||
| # AI surfaces — the Markdown twin, llms.txt, search, MCP | |||
|
|
|||
| Four consumer-facing surfaces, all derived from the pages themselves so none can drift from the docs. `DocsKit::LlmsText.pages` is the shared enumeration seam and `DocsKit::MarkdownExport` is the shared rendering seam; every surface below is a thin skin over those two. | |||
There was a problem hiding this comment.
P3: The opening architecture claim incorrectly includes the OpenAPI bridge among the page-derived surfaces. Clarify that the four registry-backed AI surfaces share these seams, while OpenAPI remains spec-driven.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/ai-surfaces/summary.md, line 3:
<comment>The opening architecture claim incorrectly includes the OpenAPI bridge among the page-derived surfaces. Clarify that the four registry-backed AI surfaces share these seams, while OpenAPI remains spec-driven.</comment>
<file context>
@@ -0,0 +1,53 @@
+# AI surfaces — the Markdown twin, llms.txt, search, MCP
+
+Four consumer-facing surfaces, all derived from the pages themselves so none can drift from the docs. `DocsKit::LlmsText.pages` is the shared enumeration seam and `DocsKit::MarkdownExport` is the shared rendering seam; every surface below is a thin skin over those two.
+
+## `DocsKit::MarkdownExport` — the twin
</file context>
| Four consumer-facing surfaces, all derived from the pages themselves so none can drift from the docs. `DocsKit::LlmsText.pages` is the shared enumeration seam and `DocsKit::MarkdownExport` is the shared rendering seam; every surface below is a thin skin over those two. | |
| The Markdown twin and three registry-backed AI surfaces below derive from the pages themselves, so they cannot drift from the docs. `DocsKit::LlmsText.pages` is the shared enumeration seam and `DocsKit::MarkdownExport` is the shared rendering seam; the separate OpenAPI bridge is spec-driven. |
|
|
||
| - The manual check a user of the change would do: `cd docs && bin/dev`, open `/docs`, and look at the page the change touches — with JavaScript disabled as well as enabled. For a Markdown-twin or AI-surface change, also fetch `/docs/<slug>.md`, `/llms.txt` and `/docs/search?q=…`. For a generator change, run `rails g docs_kit:install` twice against a throwaway app and diff — the second run must change nothing. | ||
| - A CSS-affecting change (a new emitted class) needs `cd docs && bun run build:css` and a look at the built output; a class no source line spells literally will be missing. | ||
| - Stress iterations for a flake proof: 50 runs of the suspect spec file with `--seed` varied (`for i in (seq 50); bundle exec rspec <file> --seed $i; end`); for an ordering-dependent failure, reproduce the exact `--seed` CI printed first. |
There was a problem hiding this comment.
P3: The flake-stress command is fish-only, so it fails before running any specs in Bash. Use a POSIX loop with $(seq 50), do, and done instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/workflow.md, line 135:
<comment>The flake-stress command is fish-only, so it fails before running any specs in Bash. Use a POSIX loop with `$(seq 50)`, `do`, and `done` instead.</comment>
<file context>
@@ -0,0 +1,136 @@
+
+- The manual check a user of the change would do: `cd docs && bin/dev`, open `/docs`, and look at the page the change touches — with JavaScript disabled as well as enabled. For a Markdown-twin or AI-surface change, also fetch `/docs/<slug>.md`, `/llms.txt` and `/docs/search?q=…`. For a generator change, run `rails g docs_kit:install` twice against a throwaway app and diff — the second run must change nothing.
+- A CSS-affecting change (a new emitted class) needs `cd docs && bun run build:css` and a look at the built output; a class no source line spells literally will be missing.
+- Stress iterations for a flake proof: 50 runs of the suspect spec file with `--seed` varied (`for i in (seq 50); bundle exec rspec <file> --seed $i; end`); for an ordering-dependent failure, reproduce the exact `--seed` CI printed first.
+- Where evidence goes: `lode/tmp/` (gitignored, never committed) unless the PR needs an auditable trail.
</file context>
| - Stress iterations for a flake proof: 50 runs of the suspect spec file with `--seed` varied (`for i in (seq 50); bundle exec rspec <file> --seed $i; end`); for an ordering-dependent failure, reproduce the exact `--seed` CI printed first. | |
| - Stress iterations for a flake proof: 50 runs of the suspect spec file with `--seed` varied (`for i in $(seq 50); do bundle exec rspec spec/path_to_spec.rb --seed "$i" || exit $?; done`); for an ordering-dependent failure, reproduce the exact `--seed` CI printed first. |
| | one CI cell locally | `bundle exec rake` for the `rake` job; `cd docs && bundle exec rspec` for `docs-site` (needs a Playwright chromium: `cd docs && bunx --bun playwright install chromium`); `bundle config set --local without mcp && bundle install && bundle exec rspec` for `without-mcp` — **unset it afterwards** (`bundle config unset --local without`) or every later run hides the MCP specs | | | ||
| | docs build / check | `cd docs && bin/ci` (setup → rubocop → bundler-audit → `bin/importmap audit` → brakeman → rspec). `cd docs && bun run build:css` rebuilds the Tailwind/daisyUI CSS | always from inside `docs/` — it is a separate app with its own bundle, `.rubocop.yml`, `.rspec` and lockfiles. There is no `package.json` at the repo root. | | ||
| | run the app | `cd docs && bin/dev` (`bin/rails server`) | the dogfood site is the only runnable app in the repo | | ||
| | the scaffolder | `ruby exe/docs-kit --help` | `docs-kit new NAME [--image OWNER/REPO] [--service NAME] [--gem-source SRC]`; it shells out to `rails new`, so only run it for real in a throwaway directory | |
There was a problem hiding this comment.
P3: ruby exe/docs-kit --help exits 1 because the CLI treats --help as an invalid command and only accepts new. Document a valid new NAME invocation or add a real help option before using this command as the scaffolder check.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/workflow.md, line 18:
<comment>`ruby exe/docs-kit --help` exits 1 because the CLI treats `--help` as an invalid command and only accepts `new`. Document a valid `new NAME` invocation or add a real help option before using this command as the scaffolder check.</comment>
<file context>
@@ -0,0 +1,136 @@
+| one CI cell locally | `bundle exec rake` for the `rake` job; `cd docs && bundle exec rspec` for `docs-site` (needs a Playwright chromium: `cd docs && bunx --bun playwright install chromium`); `bundle config set --local without mcp && bundle install && bundle exec rspec` for `without-mcp` — **unset it afterwards** (`bundle config unset --local without`) or every later run hides the MCP specs | |
+| docs build / check | `cd docs && bin/ci` (setup → rubocop → bundler-audit → `bin/importmap audit` → brakeman → rspec). `cd docs && bun run build:css` rebuilds the Tailwind/daisyUI CSS | always from inside `docs/` — it is a separate app with its own bundle, `.rubocop.yml`, `.rspec` and lockfiles. There is no `package.json` at the repo root. |
+| run the app | `cd docs && bin/dev` (`bin/rails server`) | the dogfood site is the only runnable app in the repo |
+| the scaffolder | `ruby exe/docs-kit --help` | `docs-kit new NAME [--image OWNER/REPO] [--service NAME] [--gem-source SRC]`; it shells out to `rails new`, so only run it for real in a throwaway directory |
+
+## Branches and PRs
</file context>
| | the scaffolder | `ruby exe/docs-kit --help` | `docs-kit new NAME [--image OWNER/REPO] [--service NAME] [--gem-source SRC]`; it shells out to `rails new`, so only run it for real in a throwaway directory | | |
| | the scaffolder | `ruby exe/docs-kit new NAME [--image OWNER/REPO] [--service NAME] [--gem-source SRC]` | it shells out to `rails new`, so only run it for real in a throwaway directory | |
Summary
This repository joins the shared
lodeplugin from zoolutions/claude-plugins: durable memory inlode/, a pre-PR gate that reviews every branch against the repo's own rules and learnings before anything can be pushed, and the shared workflows (/lode:lfg,/lode:review-pr,/lode:finish-prs,/lode:debug-flaky,/lode:tdd,/lode:plan) readinglode/workflow.md.lode/— summary, terminology, practices, map, and seven subsystem summaries (core, config, registry and versions, components, AI surfaces, install path, testing and CI). Every claim cites a file and method; every count was computed (36 configuration knobs, 30 components, 20 Thor steps, 16 install templates, 62 spec files with 953 examples).lode/review/cli-and-templates.md— one rule, merging cubic learningec3d720bwith the accepted PR fix: repoint the fleet at the zoolutions org after the repo move #70 thread. The other six cubic learnings describeTAILWIND_SOURCES_COVERandSyncReport.tailwind_sources_rule, which exist only on open PR feat(generator): gitignore the generated tailwind.sources.css fleet-wide #72; they are held in the PR notes below, ready for/lode:learnonce feat(generator): gitignore the generated tailwind.sources.css fleet-wide #72 merges.lode/workflow.md(136 lines) — the profile, including the fact that the fast loop exits non-zero on a green single file because SimpleCov'sminimum_coverage 80measures the whole tree.lfg,plan,tdd,github-review-pr,github-review-failures,github-review-comments. Kept:architect,security,review-pr..claude/settings.jsonenableslode@zoolutions;CLAUDE.mdand.claude/README.mdgain the Memory section and the new command rows (AGENTS.md already points at CLAUDE.md);.gitignoregainslode/tmp/.No Ruby changes. Reading the code for the lode surfaced one defect, filed as #80:
rake releaseregenerates the trackeddocs/Gemfile.lockbut commits onlyversion.rb, so the docs pin drifts every release (it says 1.1.0 today against 1.1.1), hidden byBUNDLE_FROZEN: "false"in docs CI. Five smaller doc-versus-code gaps (adocs_kit:snapshottask referenced but never shipped, an unenforced Rails floor in CLAUDE.md, CLAUDE.md describing one of three CI jobs, adocs/plans/that does not exist) are recorded in the lode where they apply.Held for #72 (cubic learnings, each with its safe direction): c7868bf8, 353e3ed7, 383697c0, 5aa014b9, 75df49dc, 524e5700.
Test plan
bundle exec rakein the fresh clone: 953 examples green, 149 files RuboCop-clean;ruby exe/docs-kit --help/lode:lfgon the fix for rake release refreshes docs/Gemfile.lock but commits only version.rb, so the docs pin drifts every release #80 reads the profile and reaches the gateDeviations & judgment calls
/lode:seed, resumed once after a rate limit (every draft re-verified; two defects in them fixed: an 11-accessor list with 10 entries, and the Rakefile's own "both lockfiles are gitignored" repeated as fact), then gated here.LODE_SKIP_GATE=1after the gate passed: the orchestrating session runs the cached 0.1.0 push hook keyed on a different checkout. A session opened here will not need it.Gate
Rounds: 1. Findings: 0 P1, 0 P2, 2 P3 needing no change. Rejected 0, deferred 0.
https://claude.ai/code/session_01Hga3idYBp7fWdj7GmmfofF
Summary by cubic
Seeds the repository's durable knowledge in
lode/and enables the sharedlodeplugin's pre-PR gate, so an agent session starts from a verified map of the repo and every push is reviewed against the repo's own rules. The local/lfg,/plan,/tdd,/github-review-pr,/github-review-failures, and/github-review-commentscommands move to the plugin's/lode:*workflows, with their repo-specific content now inlode/workflow.md.lode/with an index, based onsummary.md,terminology.md,practices.md, a plans README, a workflow profile, and seven area summaries (core, config, registry and versions, components, AI surfaces, install path, testing and CI), all counts computed from the code.lode/review/cli-and-templates.mdabout thedocs-kit newCLI./architect,/security, and/review-pr; enableslode@zoolutionsin.claude/settings.json.rake releasedriftsdocs/Gemfile.lock) plus four smaller doc-versus-code gaps in the lode.TAILWIND_SOURCES_COVERandSyncReport.tailwind_sources_rulewait for PR feat(generator): gitignore the generated tailwind.sources.css fleet-wide #72 because those symbols are not on this branch.Written for commit 708e334. Summary will update on new commits.