feat: v0.9.0 — First 5 Minutes Magic - #116
Conversation
…scovery Two new built-in templates: node-fullstack (4-agent frontend/backend split, detects package.json) and python-api (3-agent, detects pyproject.toml/requirements.txt). Custom template discovery from ~/.config/tutti/templates/ with validation and warning on malformed files. Unified scoring via score_template_detection(). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ate list Progressive disclosure bootstrap: auto-detect repo → show team preview table → [L]aunch / [C]ustomize / [Q]uit. Customize mode lets users swap runtimes, remove agents, and change project name with live preview re-rendering. tt template list shows all built-in and custom templates with detection rules. InputSource trait enables full test coverage of interactive flows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Emits step-level timing and pass/fail data to .tutti/state/run-telemetry.jsonl after each tt run completes. Includes template_id/version attribution. Duration clamped to max(0.0) to handle clock skew. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds v0.9.0: an interactive Changes
Sequence Diagram(s)sequenceDiagram
participant User as User (tty)
participant CLI as tt init CLI
participant Templates as Template resolver
participant FS as Filesystem / .tutti
participant Up as tt up (launcher)
User->>CLI: run `tt init` (interactive)
CLI->>Templates: resolve_template (explicit | detect | custom)
Templates-->>CLI: selected template + metadata
CLI->>CLI: render_team_preview()
alt user chooses Customize
CLI->>CLI: customize_template() loop -> render preview
end
opt user chooses Save or Launch
CLI->>FS: write_config_and_register(tutti.toml)
end
opt user chooses Launch
CLI->>Up: launch_agents (run `tt up`)
Up-->>CLI: success/failure
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/cli/init.rs (1)
428-455:⚠️ Potential issue | 🟠 MajorGlobal workspace registration is still a racy read-modify-write.
Lines 451-453 load, mutate, and save the global config with no lock. Two concurrent
tt initruns can overwrite each other's workspace additions, which matches the parallel-test flake already called out in the PR summary.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/init.rs` around lines 428 - 455, The GlobalConfig read-modify-write in write_config_and_register is racy: replace the unguarded GlobalConfig::load(); global.register_workspace(...); global.save() sequence with a protected critical section that acquires an exclusive lock on the global config (or its parent directory) before loading, mutating, and saving; use a stable locking mechanism (e.g., an OS file lock via fs2 or a lockfile around the same global path) so register_workspace and save occur while holding the lock and are retried/return an error if the lock cannot be acquired.
🧹 Nitpick comments (3)
src/automation/mod.rs (1)
2591-2603: Make telemetry step IDs unique per step execution.At Line 2595,
StepTimingEntry.idis set tos.step_type, which can collide for repeated step types (e.g., multiplecommandsteps). Use a stable unique ID (index + type, or step key) for reliable downstream analysis.Proposed tweak
- id: s.step_type.clone(), + id: format!("{:03}-{}", s.index, s.step_type),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/automation/mod.rs` around lines 2591 - 2603, The telemetry step IDs are currently set to s.step_type which can collide for repeated step types; in the mapping that builds step_timings (iterating over result.step_results and constructing StepTimingEntry) change the id to a stable unique value—e.g., use .enumerate() to include the index (format as index + step_type) or use an existing unique step key if available on the step object—so StepTimingEntry.id is unique per execution rather than just the step type; update the mapping where StepTimingEntry is created and leave the duration_secs and status handling unchanged.src/main.rs (1)
188-190: Routetemplatehandling through a dedicated CLI subcommand module.At Line 189,
Commands::Templateis dispatched tocli::init::template_list(), which couples two subcommands. Prefer a dedicatedsrc/cli/template.rshandler (cli::template::list()), withinitandtemplatekept separate.As per coding guidelines:
src/cli/*.rs: Organize code structure withsrc/cli/for Clap command handlers (one file per subcommand).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main.rs` around lines 188 - 190, The Commands::Template arm currently calls cli::init::template_list(), coupling template handling into the init module; create a dedicated template handler and route to it by (1) adding a new cli::template module with a public list() function to handle TemplateSubcommand::List, (2) update the match arm for Commands::Template to call cli::template::list() instead of cli::init::template_list(), and (3) wire the new module into the cli module exports (pub mod template) so the symbol cli::template::list is available.src/template/mod.rs (1)
122-173: These discovery helpers probably shouldn't be public.Lines 122, 146, and 176 are CLI-oriented helpers, but exposing them as
publocks inVec/usizereturn shapes and the current stderr-warning behavior. If external callers do not need them,pub(crate)is the safer default; otherwise they should follow the crate's normalResult<T, TuttiError>contract.As per coding guidelines, "All public functions must return
Result<T, TuttiError>."Also applies to: 175-219
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/template/mod.rs` around lines 122 - 173, The two CLI-oriented helpers detect_templates and score_template_detection are currently public but should not be part of the external API; change their visibility to pub(crate) (or to non-pub) so callers within the crate can still use them without locking in return types/behaviour, and update any internal call sites accordingly; if these functions must be public, instead change their signatures to return Result<..., TuttiError> following the crate convention (e.g., detect_templates -> Result<Vec<(String, ParsedTemplate, usize)>, TuttiError> and score_template_detection -> Result<usize, TuttiError>) and propagate/convert errors, referencing the functions detect_templates and score_template_detection and the TemplateMetadata type to locate and modify the definitions and call sites.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/automation/mod.rs`:
- Around line 12-15: Run rustfmt (cargo fmt) to fix formatting issues in
src/automation/mod.rs: tidy the import list containing AutomationRunRecord,
ControlEvent, RunTelemetryEntry, StepTimingEntry, VerifyLastSummary,
WorkflowStepIntentRecord, WorkflowStepOutcomeRecord, append_automation_run,
append_control_event, append_policy_decision, append_run_telemetry,
load_workflow_checkpoint, load_workflow_intent, save_verify_last_summary so it
adheres to rustfmt rules, and reformat the duration_secs expression around the
code that computes duration_secs (the wrapping at the duration_secs expression
near where run telemetry or step timing is handled) so it matches rustfmt line
wrapping. After running cargo fmt, verify the imports and the expression are
properly wrapped and commit the formatted file.
In `@src/cli/init.rs`:
- Around line 21-45: The prompt_choice function currently treats EOF/read errors
as selecting the first option which can be "Q" but still lets the caller perform
side effects; update prompt_choice to explicitly return "Q" (or a clear "QUIT")
on EOF or read error so non-interactive stdin always aborts, and then modify the
caller logic that handles the selection (the branch that writes tutti.toml and
calls workspace registration) to check for the explicit "Q"/"QUIT" value and
immediately return/abort without creating or overwriting files; refer to
prompt_choice and the code block that writes tutti.toml / registers the
workspace and ensure the Quit path is side-effect free.
In `@src/state/mod.rs`:
- Around line 652-684: The new public function append_run_telemetry currently
swallows all errors and returns (), violating the guideline that public APIs
return Result; either make it non-public for best-effort telemetry (change pub
to pub(crate) on append_run_telemetry) or change its signature to return
Result<(), TuttiError> and propagate failures (filesystem and serde errors) as
TuttiError from create_dir_all, serde_json::to_string, OpenOptions::open and
writeln! so callers can decide how to handle/log them; update call sites
accordingly.
In `@templates/node-fullstack.toml`:
- Around line 11-18: The template defines a dedicated template.roles.frontend
agent but never routes any implementation steps to it (planner, backend, tester
are used exclusively), so either add a frontend step or remove the unused agent:
update the planner's plan generation or the implementation dispatch logic to
include a frontend implementation path that sends UI-related steps to the
template.roles.frontend agent (e.g., create a "frontend" implementation step
alongside "backend" and "tester" and ensure the implementation dispatcher sends
that step to template.roles.frontend), or alternatively delete
template.roles.frontend and any references to a frontend role so the template
only advertises agents actually used.
---
Outside diff comments:
In `@src/cli/init.rs`:
- Around line 428-455: The GlobalConfig read-modify-write in
write_config_and_register is racy: replace the unguarded GlobalConfig::load();
global.register_workspace(...); global.save() sequence with a protected critical
section that acquires an exclusive lock on the global config (or its parent
directory) before loading, mutating, and saving; use a stable locking mechanism
(e.g., an OS file lock via fs2 or a lockfile around the same global path) so
register_workspace and save occur while holding the lock and are retried/return
an error if the lock cannot be acquired.
---
Nitpick comments:
In `@src/automation/mod.rs`:
- Around line 2591-2603: The telemetry step IDs are currently set to s.step_type
which can collide for repeated step types; in the mapping that builds
step_timings (iterating over result.step_results and constructing
StepTimingEntry) change the id to a stable unique value—e.g., use .enumerate()
to include the index (format as index + step_type) or use an existing unique
step key if available on the step object—so StepTimingEntry.id is unique per
execution rather than just the step type; update the mapping where
StepTimingEntry is created and leave the duration_secs and status handling
unchanged.
In `@src/main.rs`:
- Around line 188-190: The Commands::Template arm currently calls
cli::init::template_list(), coupling template handling into the init module;
create a dedicated template handler and route to it by (1) adding a new
cli::template module with a public list() function to handle
TemplateSubcommand::List, (2) update the match arm for Commands::Template to
call cli::template::list() instead of cli::init::template_list(), and (3) wire
the new module into the cli module exports (pub mod template) so the symbol
cli::template::list is available.
In `@src/template/mod.rs`:
- Around line 122-173: The two CLI-oriented helpers detect_templates and
score_template_detection are currently public but should not be part of the
external API; change their visibility to pub(crate) (or to non-pub) so callers
within the crate can still use them without locking in return types/behaviour,
and update any internal call sites accordingly; if these functions must be
public, instead change their signatures to return Result<..., TuttiError>
following the crate convention (e.g., detect_templates -> Result<Vec<(String,
ParsedTemplate, usize)>, TuttiError> and score_template_detection ->
Result<usize, TuttiError>) and propagate/convert errors, referencing the
functions detect_templates and score_template_detection and the TemplateMetadata
type to locate and modify the definitions and call sites.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 5de598c7-c5ef-4512-82ff-8ed2c0bd014f
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (10)
CHANGELOG.mdCargo.tomlsrc/automation/mod.rssrc/cli/init.rssrc/cli/mod.rssrc/main.rssrc/state/mod.rssrc/template/mod.rstemplates/node-fullstack.tomltemplates/python-api.toml
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (3)
src/state/mod.rs (1)
652-684:⚠️ Potential issue | 🟠 MajorMake the telemetry writer internal or return
Result<()>.This new
pub fnswallows serialization and filesystem failures behindeprintln!, so callers cannot decide whether telemetry loss should warn, retry, or fail the command. If telemetry is intentionally best-effort, make the functionpub(crate); otherwise returnResult<()>and let the caller downgrade it to a warning. As per coding guidelines, "All public functions must returnResult<T, TuttiError>."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/state/mod.rs` around lines 652 - 684, The public function append_run_telemetry currently swallows serialization and IO errors with eprintln!; change it to either be internal (pub(crate) fn append_run_telemetry) if telemetry should stay best-effort, or update the signature to pub fn append_run_telemetry(project_root: &Path, entry: &RunTelemetryEntry) -> Result<(), TuttiError> and propagate failures instead of printing: return Err(TuttiError::Serialization(...)) for serde_json::to_string errors and Err(TuttiError::Io(...)) for create_dir_all/OpenOptions/writeln failures (or map them to existing TuttiError variants), removing eprintln! calls so callers can decide to downgrade to a warning or fail.src/cli/init.rs (2)
353-387:⚠️ Potential issue | 🟠 MajorBlock agent removal while workflows still reference it.
This removes the
[[agent]]block and maybe the role, but it never rewrites or validates workflow steps that still target that agent. Customize can therefore save a config that parses but fails later when the workflow reaches the removed agent.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/init.rs` around lines 353 - 387, Before removing an [[agent]] block, check all workflows in the parsed config for any steps that reference that agent (by name or by role) and refuse the removal if any reference exists; specifically, in the "2" branch where you call remove_agent_from_config_body(&mut parsed.config_body, agent_name) and mutate parsed.metadata.roles, first iterate config.workflows (and each workflow's steps/targets) to detect any step.agent == agent_name or any step that targets the agent's role, and if any are found print a clear blocking message (e.g., "Cannot remove agent — referenced by workflow X step Y.") and continue without removing; only proceed to remove the [[agent]] block and metadata.roles when no workflow references the agent.
20-33:⚠️ Potential issue | 🟠 MajorAbort on EOF/read errors, and keep
Quitside-effect free.
prompt_choicestill turns EOF/read failures into the first real option, so non-interactive stdin can back up configs, auto-pick template1, or launch immediately. The main"Q"branch then writestutti.tomland registers the workspace anyway, which contradicts the prompt text. Please surface an explicit abort fromprompt_choiceand have callers short-circuit before any write or launch.Also applies to: 82-93, 111-156
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/init.rs` around lines 20 - 33, prompt_choice on StdinInput currently treats EOF/read errors as selecting the first real option, causing non-interactive runs to proceed and trigger side-effects like writing tutti.toml and registering the workspace; change prompt_choice to return a Result<String, PromptError> (or Option<String>) and on Ok return the chosen option, but on EOF or read Err return Err/None to indicate explicit abort; update all callers of InputSource::prompt_choice (the init flow that writes tutti.toml and registers the workspace/launch logic) to short-circuit on Err/None before performing any writes or launches, and ensure the "Q"/Quit branch in callers performs no side-effects when abort is returned.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/cli/init.rs`:
- Around line 401-423: The removal logic in remove_agent_from_config_body stops
at "\n[[" only, so regular TOML tables like [hooks] or [env] after the agent get
swallowed; change the block_end computation (the code that uses
after_block.find("\n[[") and maps to block_end) to look for the next table
header start "\n[" (not just "\n[[") and compute the same offset from
block_start + "[[agent]]".len(), so the deletion stops at any next TOML table
header; keep the existing trim_start behavior and replace_range call.
In `@src/template/mod.rs`:
- Around line 200-208: Discovery currently appends every parsed template (via
parse_template) into the templates Vec using parsed.metadata.name which allows
duplicate names; change discovery to dedupe by template name and warn on
collisions: instead of blindly pushing into templates, track seen names (e.g.,
HashMap<String, ParsedTemplate> or a HashSet of names) when handling each parsed
result in the loop over entries, and if parsed.metadata.name already exists emit
a warning (use the existing logger) and skip adding the duplicate so only the
first occurrence is kept; ensure this same dedup logic is applied to the other
discovery block that builds templates (the code path around load_template) so
load_template continues to find an unambiguous exact-name hit.
- Around line 150-152: The functions score_template_detection and
discover_custom_templates are exposed as public but are internal helpers; make
them crate-private or convert their signatures to return a Result to comply with
the public API rule. Update the visibility of score_template_detection and
discover_custom_templates from pub to pub(crate) if they should remain internal;
otherwise change their return types to Result<usize, TuttiError> and
Result<Vec<Template>, TuttiError> (or appropriate inner types) and propagate
errors from their internals so callers can handle failures. Ensure you adjust
all call sites to match the new signatures and import TuttiError where needed.
---
Duplicate comments:
In `@src/cli/init.rs`:
- Around line 353-387: Before removing an [[agent]] block, check all workflows
in the parsed config for any steps that reference that agent (by name or by
role) and refuse the removal if any reference exists; specifically, in the "2"
branch where you call remove_agent_from_config_body(&mut parsed.config_body,
agent_name) and mutate parsed.metadata.roles, first iterate config.workflows
(and each workflow's steps/targets) to detect any step.agent == agent_name or
any step that targets the agent's role, and if any are found print a clear
blocking message (e.g., "Cannot remove agent — referenced by workflow X step
Y.") and continue without removing; only proceed to remove the [[agent]] block
and metadata.roles when no workflow references the agent.
- Around line 20-33: prompt_choice on StdinInput currently treats EOF/read
errors as selecting the first real option, causing non-interactive runs to
proceed and trigger side-effects like writing tutti.toml and registering the
workspace; change prompt_choice to return a Result<String, PromptError> (or
Option<String>) and on Ok return the chosen option, but on EOF or read Err
return Err/None to indicate explicit abort; update all callers of
InputSource::prompt_choice (the init flow that writes tutti.toml and registers
the workspace/launch logic) to short-circuit on Err/None before performing any
writes or launches, and ensure the "Q"/Quit branch in callers performs no
side-effects when abort is returned.
In `@src/state/mod.rs`:
- Around line 652-684: The public function append_run_telemetry currently
swallows serialization and IO errors with eprintln!; change it to either be
internal (pub(crate) fn append_run_telemetry) if telemetry should stay
best-effort, or update the signature to pub fn
append_run_telemetry(project_root: &Path, entry: &RunTelemetryEntry) ->
Result<(), TuttiError> and propagate failures instead of printing: return
Err(TuttiError::Serialization(...)) for serde_json::to_string errors and
Err(TuttiError::Io(...)) for create_dir_all/OpenOptions/writeln failures (or map
them to existing TuttiError variants), removing eprintln! calls so callers can
decide to downgrade to a warning or fail.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7ae00dcb-5310-4f1b-80cf-709cf7ce1e0f
📒 Files selected for processing (5)
src/automation/mod.rssrc/cli/init.rssrc/error.rssrc/state/mod.rssrc/template/mod.rs
✅ Files skipped from review due to trivial changes (1)
- src/error.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/automation/mod.rs
1. init: Quit option now exits cleanly without writing files; EOF/read errors map to Quit (abort) instead of the first option. Added Save-only option [S] for writing config without launching. 2. init: Removing an agent now also removes dangling workflow steps that reference it, via new remove_workflow_steps_for_agent helper. 3. state: Changed append_run_telemetry from pub to pub(crate) since it is best-effort telemetry that swallows errors intentionally. 4. templates/node-fullstack: Added ensure_running and implement steps for the frontend agent so it actually participates in the workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
templates/node-fullstack.toml (1)
130-134: Consider usingcwd = "agent_worktree"instead of hard-coded path.The hard-coded path
.tutti/worktrees/backendcouples this step to the agent name. Using the workflow config's built-incwdoption would be more maintainable:♻️ Suggested improvement
[[workflow.step]] id = "validate" type = "command" -run = "cd .tutti/worktrees/backend && npm test --quiet" +run = "npm test --quiet" +cwd = "agent_worktree" +agent = "backend" fail_mode = "closed"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@templates/node-fullstack.toml` around lines 130 - 134, The workflow step with id "validate" currently hardcodes the agent path in run ("cd .tutti/worktrees/backend && npm test --quiet"); change this to use the workflow step's cwd option (cwd = "agent_worktree") and simplify run to just "npm test --quiet" so the step no longer depends on the agent name; update the [[workflow.step]] block for id "validate" accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@templates/node-fullstack.toml`:
- Around line 130-134: The workflow step with id "validate" currently hardcodes
the agent path in run ("cd .tutti/worktrees/backend && npm test --quiet");
change this to use the workflow step's cwd option (cwd = "agent_worktree") and
simplify run to just "npm test --quiet" so the step no longer depends on the
agent name; update the [[workflow.step]] block for id "validate" accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 72c750d6-cda7-4145-84ed-a86a61a62dfa
📒 Files selected for processing (4)
src/cli/init.rssrc/config/mod.rssrc/state/mod.rstemplates/node-fullstack.toml
✅ Files skipped from review due to trivial changes (1)
- src/cli/init.rs
- Stop agent block removal at any table header (not just [[) - Make score_template_detection and discover_custom_templates pub(crate) - Reject duplicate custom template names during discovery Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/template/mod.rs (1)
185-188: Optional: Consider cross-platform home directory resolution for custom template discovery.The
HOMEenvironment variable is unset on Windows; consider using thedirsorhomecrate for portable home directory detection. This function currently returns an empty template list on missingHOME, which is acceptable for optional discovery, but would improve Windows compatibility if it becomes a supported platform.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/template/mod.rs` around lines 185 - 188, The current discovery code assigns `home` from std::env::var("HOME") and returns early when missing; update it to use a cross‑platform home resolver (e.g., the dirs crate's dirs::home_dir() or the home crate) instead of relying solely on the HOME env var: add the chosen crate to Cargo.toml, replace the std::env::var("HOME") match that produces the `home` variable with a call to the cross‑platform helper (fall back to returning `templates` if that helper returns None), and ensure subsequent code still uses the `home` variable unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/template/mod.rs`:
- Around line 185-188: The current discovery code assigns `home` from
std::env::var("HOME") and returns early when missing; update it to use a
cross‑platform home resolver (e.g., the dirs crate's dirs::home_dir() or the
home crate) instead of relying solely on the HOME env var: add the chosen crate
to Cargo.toml, replace the std::env::var("HOME") match that produces the `home`
variable with a call to the cross‑platform helper (fall back to returning
`templates` if that helper returns None), and ensure subsequent code still uses
the `home` variable unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 449a7ae0-4876-4a83-850e-166b47ae7f2b
📒 Files selected for processing (2)
src/cli/init.rssrc/template/mod.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- src/cli/init.rs
Summary
tt init: Progressive disclosure bootstrap — auto-detects repo type, shows team preview table, offers Launch/Customize/Quittt upin one steptt template list: Shows all available templates with detection rules~/.config/tutti/templates/*.tomlauto-discovered.tutti/state/run-telemetry.jsonlPre-Landing Review
4 auto-fixed from adversarial review (EOF stdin loop, scoring inconsistency, negative duration, swallowed read errors). 0 critical remaining. 10 informational noted (acceptable).
Test plan
cargo clippyclean (1 pre-existing dead code warning)tt template listrenders correctly with 5 templates🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation