Skip to content

fix(config): keep the generated header on its own line, and parse the spec once - #871

Merged
jdx merged 1 commit into
mainfrom
agent/config-header
Aug 14, 2026
Merged

fix(config): keep the generated header on its own line, and parse the spec once#871
jdx merged 1 commit into
mainfrom
agent/config-header

Conversation

@jdx

@jdx jdx commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Two findings from the review of #864, which merged before I had read them — my sweep was baselining on
the head commit, and these arrived nine minutes earlier.

The generated header could end its own comment. The first line says which spec the file came
from, in a line comment, and the name is a file name — or whatever a caller hands to
source_of_spec. A newline in it ended the comment and everything after it read as code, in a file
the adopter did not write. It goes through one_line now, like every other name that lands in a
comment (the prop:: doc comments already did).

The spec was parsed twice on every build. generate_to asked source() for the registry and
watched() for the files to watch, and each of them parsed the whole spec. Once now — and the
includes are printed before the registry is built, so a spec whose settings are refused still
watches the file its author is about to go and edit.

The related half of that finding — reporting include paths whose own read or parse fails — I am
leaving. It needs usage-lib to report paths out through the error path, and it buys nothing: a build
script that fails produces no output, so cargo re-runs it on the next build regardless of what it
watched. The case that matters is a successful build followed by an edit, which is covered.

generate_to now returns what it watched. Printing to cargo is not something a test in this
process can see, so the list it prints and the list it returns are one value rather than two
assembled the same way — a watch list nothing checks is one that can quietly lose a file. The test
asserts the returned list against watched(), and dropping the includes from it fails.

Two mutations, two dead tests: the header name interpolated raw, and the includes dropped from the
watch list.

AI-assisted — Tool: Claude Code; model: anthropic/claude-opus-5; version: unavailable.


Note

Low Risk
Build-time code generation and cargo rerun hints only; no runtime CLI or auth behavior.

Overview
Hardens generated registry headers so a spec name (or arbitrary source_of_spec label) cannot break out of the line comment via embedded newlines — the name now goes through one_line, same as other comment-bound strings.

Build pipeline changes in generate_to: the spec is parsed once (parse) instead of separately in source() and watched(); cargo::rerun-if-changed for includes is emitted before registry generation so invalid specs still watch the files the author will edit. generate_to returns Vec<PathBuf> of watched paths (the same list it prints), so tests can assert watch behavior without relying on cargo output.

Reviewed by Cursor Bugbot for commit ce1d0e0. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed an issue where spec names containing line breaks could corrupt generated configuration comments.
    • Improved change detection so generated files are rebuilt when watched source files change.
  • Improvements

    • Generation now reports the source files it monitors, enabling more reliable build updates.
    • Avoids rewriting generated output when no changes are detected.

… spec once

Two from the review of #864, which merged before I had read them.

The header says which spec a file came from, in a *line* comment, and the name is
a file name — or whatever a caller hands to `source_of_spec`. A newline in it
ended that comment and everything after it read as code, in a file the adopter
did not write. Through `one_line`, like every other name that lands in a comment.

And the watch list and the registry were each parsing the whole spec, so every
build parsed it twice. Once now, with the includes printed before the registry is
built — so a spec whose settings are refused still watches the file its author is
about to go and edit.

`generate_to` returns what it watched. Printing to cargo is not something a test
in this process can see, so the list it prints and the list it returns are now one
value rather than two assembled the same way: a watch list nothing checks is one
that can quietly lose a file.
@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: f3158535-af8d-49c5-90ac-4d120de2b7f2

📥 Commits

Reviewing files that changed from the base of the PR and between 9a66098 and ce1d0e0.

📒 Files selected for processing (3)
  • config-build/src/emit.rs
  • config-build/src/lib.rs
  • config-build/tests/refusals.rs

📝 Walkthrough

Walkthrough

The config-build generator now parses specs once, returns watched paths from generate_to, reuses parsed sources for generation, and sanitizes source names before writing generated comments. Tests cover watched paths, temporary output cleanup, and newline-containing spec names.

Changes

Config generation

Layer / File(s) Summary
Parsed generation and watched paths
config-build/src/lib.rs, config-build/tests/refusals.rs
generate_to returns watched paths, reuses parsed sources, and returns the path list for unchanged and successful writes. Tests verify the returned paths and temporary output cleanup.
Safe generated header names
config-build/src/emit.rs, config-build/tests/refusals.rs
registry converts source names to one line before embedding them in generated comments. Tests verify that newline characters cannot create standalone Rust code.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Mergeability Score: ⚪ Minimal · up to ce1d0

The PR keeps generated headers safe and ensures specification includes remain watched while avoiding duplicate parsing; no actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

  • jdx/usage#864: Introduced the config-build generator extended by these changes.

Poem

A rabbit checked the source with care,
And kept each newline in its lair.
Paths now return in tidy rows,
Safe comments guard the code that grows.
“Hop on,” says Bun, “the build now knows!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: generated header sanitization and parsing the specification once.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR sanitizes spec names embedded in generated line comments and refactors registry generation to parse each spec once while returning the same source list emitted to Cargo.

  • Normalizes newlines and carriage returns in generated header names.
  • Reuses one parsed Spec for registry generation and watched-file reporting.
  • Adds regression coverage for header injection and included-file watch lists.

Confidence Score: 5/5

The PR appears safe to merge with no actionable correctness or security issues identified.

The single-parse path preserves source ordering and watch behavior, all repository callers remain type-correct, and the header normalization blocks newline escape from the generated Rust comment.

Important Files Changed

Filename Overview
config-build/src/emit.rs Sanitizes the generated header’s interpolated spec name with the existing one-line normalization helper.
config-build/src/lib.rs Consolidates spec parsing, emits included watch paths before registry construction, and returns the emitted watch list.
config-build/tests/refusals.rs Adds focused regression tests for multiline header names and consistency between generated and independently queried watch lists.

Reviews (1): Last reviewed commit: "fix(config): keep the generated header o..." | Re-trigger Greptile

@github-actions

Copy link
Copy Markdown
Contributor

Instruction counts

Nothing was compared, and so nothing was gated. No series appears on both sides: either the base has no measurements recorded, or the two were measured on different runner classes, which are deliberately not comparable — counts shift between machine types by more than a real regression does.

New, nothing to compare against: markdown on bamboo-v2-ubuntu24.04-x64-30vcpu-24gb-rust1.97.1, startup on bamboo-v2-ubuntu24.04-x64-30vcpu-24gb-rust1.97.1

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

Shadow comparison

Parsing mise use -g node@20 against a shadow of mise's committed spec.
Reported, not gated: the shadow grows as the derive learns to express more, so
what to watch is the ratio rather than either column.

usage clap ratio
instructions, cold parse 29877 5875919 196x
usage: argv -> struct                             872 ns      0.87 µs
clap: build tree + parse -> struct             501328 ns    501.33 µs
clap: parse -> struct, tree reused              23268 ns     23.27 µs
clap: build tree only                          304397 ns    304.40 µs

ce1d0e03a7c0 vs 9a66098f4a58 · measured on the runner, not pushed to the history.

@jdx
jdx merged commit 5facc90 into main Aug 14, 2026
9 checks passed
@jdx
jdx deleted the agent/config-header branch August 14, 2026 00:08
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.

1 participant