Skip to content

feat(codegen): add plumb-codegen crate + plumb init --from (#82) - #212

Merged
aram-devdocs merged 1 commit into
mainfrom
claude/issue-82-plumb-codegen
May 6, 2026
Merged

feat(codegen): add plumb-codegen crate + plumb init --from (#82)#212
aram-devdocs merged 1 commit into
mainfrom
claude/issue-82-plumb-codegen

Conversation

@aram-devdocs

Copy link
Copy Markdown
Owner

Closes #82.

Summary

  • Adds a new plumb-codegen workspace crate that walks a project tree and infers a starter plumb.toml from the design-token sources it finds: CSS custom properties under :root, Tailwind config files, and DTCG token JSON files.
  • Wires the new crate through plumb init --from <path>. The default plumb init flow is unchanged.
  • Walk order is sorted; scales are sorted ascending and deduplicated; observable output uses IndexMap insertion order. Two runs over the same tree produce byte-identical output.

Architecture

  • plumb-codegen depends on plumb-core + plumb-config only.
  • Depended on by plumb-cli only; never by plumb-cdp / plumb-format / plumb-mcp.
  • #![forbid(unsafe_code)], no unwrap/expect/panic!, no println!/eprintln!, no wall-clock reads.

V0 inference sources

Source Mechanism
CSS custom properties plumb_config::scrape_css_properties + a name-prefix classifier (--color-*, --space-*, --radius-*, --text-*, …) folded into the corresponding Config slot
Tailwind config Filesystem detection at the project root; the renderer notes the file in the header comment. Theme resolution stays on the linter side via plumb_config::merge_tailwind
DTCG token JSON plumb_config::merge_dtcg, picked up from *.tokens.json files or any *.json under a tokens/ directory

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo nextest run -p plumb-codegen -p plumb-cli — 95 tests pass (26 unit tests in plumb-codegen, 25 existing CLI integration tests, plus 3 new init --from integration tests, MCP, etc.)
  • just determinism-check — three lint runs produce byte-identical JSON
  • cargo deny check — advisories ok, bans ok, licenses ok, sources ok
  • cargo run -p xtask -- pre-release — schema in sync, runbooks valid, release-readiness kits valid

🤖 Generated with Claude Code

Adds a new `plumb-codegen` workspace crate that walks a project tree
and infers a starter `plumb.toml` from the design-token sources it
finds: CSS custom properties under `:root`, Tailwind config files, and
DTCG token JSON files. Wired through `plumb init --from <path>`.

The walker is deterministic: directory entries are sorted before
recursion, scales are sorted ascending and deduplicated, and observable
output uses `IndexMap` insertion order. Two runs over the same tree
produce byte-identical output.

V0 inference sources:
- CSS custom properties via `plumb_config::scrape_css_properties`,
  classified into color / spacing / radius / type-scale buckets by
  property-name prefix and value type.
- Tailwind config presence at the project root (theme resolution stays
  on the linter side via `plumb_config::merge_tailwind`).
- DTCG token JSON via `plumb_config::merge_dtcg`, picked up from
  `*.tokens.json` files or any `*.json` under a `tokens/` directory.

Layer discipline upheld:
- `plumb-codegen` depends on `plumb-core` + `plumb-config` only.
- Depended on by `plumb-cli` only; never by `plumb-cdp` /
  `plumb-format` / `plumb-mcp`.

Closes #82

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

PR #212 Review — feat(codegen): add plumb-codegen crate + plumb init --from

1. Determinism

No violations. Observable output paths use IndexMap throughout. sort_and_dedup normalises scales before return. display_path emits forward slashes unconditionally, avoiding OS-level Path::Display divergence. Walk sorts entries before recursion and does a final .sort() on each accumulated vec — the two-phase approach is redundant but not wrong; output is stable. No SystemTime, no Instant, no HashMap/HashSet in observable positions. ✓

2. Workspace layering

plumb-codegen depends on plumb-core + plumb-config only; consumed by plumb-cli only. No diagonal or cyclic edges. #![forbid(unsafe_code)] present at lib.rs:839. No println!/eprintln! in the new crate. ✓

Gap: .agents/rules/dependency-hierarchy.md still lists six crates; plumb-codegen is absent. The rule file is normative — agents and reviewers who read it will not know where this crate sits in the layer graph.

dependency-hierarchy.md — add plumb-codegen (depends on plumb-core + plumb-config; consumed by plumb-cli only; sits at the same level as plumb-mcp).

3. Error handling

Clean. thiserror-derived CodegenError. #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)] at the crate level. No bare unwrap()/expect() in production paths.

Two soft issues:

  • walk.rs:106 Err(_) => continue silently drops symlink_metadata errors. read_dir errors are surfaced; this one is not. For a best-effort inference tool this is defensible, but a tracing::debug! at minimum would honour the "libraries use tracing" rule.

  • walk.rs:117 unwrap_or_default() — safe (empty-string guard follows), but clippy::unwrap_used does not cover it. Add a comment so it doesn't read as an oversight.

4. Test coverage

Solid. Four source files each have inline #[cfg(test)] modules (22 unit tests total). Three integration tests in crates/plumb-cli/tests/init_from.rs cover the happy path (with a determinism assertion), a missing-directory error, and an empty-dir blank-starter case. The insta snapshot is committed. ✓

5. Documentation

Public API (infer_config, render_toml, CodegenError, InferredConfig, TokenSource, TokenSourceKind, MAX_WALK_DEPTH) fully documented. Both public fallible fns have # Errors sections. ✓

Bug in module doc: classify.rs:18 reads:

//! - **Radius scale + tokens.** Any `--radius-*`, `--rounded-*`,
//!   `--corner-*` declaration …

RadiusSpec has no tokens field (scale only). The comment promises a token map that doesn't exist, which will confuse the next author who goes to add one. Should read "Radius scale."

Punch list

File Line Severity Issue
.agents/rules/dependency-hierarchy.md whole file Warning plumb-codegen not documented in the layer graph — normative gap
crates/plumb-codegen/src/lib.rs 306 Warning display_path duplicated verbatim in render.rs:87 — make it pub(crate) and import in render.rs
crates/plumb-codegen/src/classify.rs 18 Warning Module doc says "Radius scale + tokens" but RadiusSpec has no tokens field
crates/plumb-codegen/src/walk.rs 106 Advisory Err(_) => continue swallows symlink_metadata errors silently — add tracing::debug!

Verdict: REQUEST_CHANGES

@aram-devdocs
aram-devdocs merged commit 6b39937 into main May 6, 2026
29 of 30 checks passed
@aram-devdocs
aram-devdocs deleted the claude/issue-82-plumb-codegen branch May 6, 2026 07:43
@aram-devdocs aram-devdocs mentioned this pull request May 6, 2026
9 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(codegen): plumb-codegen crate + plumb init --from ./

1 participant