checkpoint

⚒️ Generated with [Fabro](https://fabro.sh)
This commit is contained in:
Fabro 2026-05-29 16:46:59 -04:00
parent f1f03ab532
commit cd2b80f319
6 changed files with 1178 additions and 54 deletions

621
run.json

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,223 @@
diff --git a/apps/fabro-web/app/components/automation-form.tsx b/apps/fabro-web/app/components/automation-form.tsx
index 5d623534c..991562b65 100644
--- a/apps/fabro-web/app/components/automation-form.tsx
+++ b/apps/fabro-web/app/components/automation-form.tsx
@@ -2,6 +2,7 @@ import { useRef, type ReactNode } from "react";
import { Switch } from "@headlessui/react";
import type { Automation, AutomationTrigger } from "@qltysh/fabro-api-client";
+import { findApiTrigger, findScheduleTrigger } from "../lib/automation";
import { Panel, Row } from "./settings-panel";
import { INPUT_CLASS } from "./ui";
@@ -37,8 +38,8 @@ const CRON_PRESETS: ReadonlyArray<{ label: string; value: string }> = [
];
export function automationToFormValues(automation: Automation): AutomationFormValues {
- const apiTrigger = automation.triggers.find((t) => t.type === "api");
- const scheduleTrigger = automation.triggers.find((t) => t.type === "schedule");
+ const apiTrigger = findApiTrigger(automation);
+ const scheduleTrigger = findScheduleTrigger(automation);
return {
id: automation.id,
name: automation.name,
diff --git a/apps/fabro-web/app/lib/automation.ts b/apps/fabro-web/app/lib/automation.ts
new file mode 100644
index 000000000..118ab61c2
--- /dev/null
+++ b/apps/fabro-web/app/lib/automation.ts
@@ -0,0 +1,20 @@
+import type { Automation, AutomationTrigger } from "@qltysh/fabro-api-client";
+
+type TriggerOfType<K extends AutomationTrigger["type"]> = Extract<
+ AutomationTrigger,
+ { type: K }
+>;
+
+export function findApiTrigger(automation: Automation): TriggerOfType<"api"> | undefined {
+ return automation.triggers.find((t): t is TriggerOfType<"api"> => t.type === "api");
+}
+
+export function findScheduleTrigger(
+ automation: Automation,
+): TriggerOfType<"schedule"> | undefined {
+ return automation.triggers.find((t): t is TriggerOfType<"schedule"> => t.type === "schedule");
+}
+
+export function hasEnabledApiTrigger(automation: Automation): boolean {
+ return findApiTrigger(automation)?.enabled === true;
+}
diff --git a/apps/fabro-web/app/routes/automation-detail.tsx b/apps/fabro-web/app/routes/automation-detail.tsx
index 57d2274f2..a7cd29860 100644
--- a/apps/fabro-web/app/routes/automation-detail.tsx
+++ b/apps/fabro-web/app/routes/automation-detail.tsx
@@ -18,6 +18,7 @@ import type {
import { toRunWithStatus } from "../data/runs";
import { ApiError, apiData, automationsApi } from "../lib/api-client";
+import { findApiTrigger, findScheduleTrigger } from "../lib/automation";
import { useAutomation, useAutomationRuns } from "../lib/queries";
import { queryKeys } from "../lib/query-keys";
import { useDataUpdatedAt } from "../hooks/use-data-updated-at";
@@ -90,8 +91,8 @@ function AutomationHeader({ automation }: { automation: Automation }) {
const toast = useToast();
const [running, setRunning] = useState(false);
- const scheduleTrigger = automation.triggers.find((t) => t.type === "schedule");
- const apiTrigger = automation.triggers.find((t) => t.type === "api");
+ const scheduleTrigger = findScheduleTrigger(automation);
+ const apiTrigger = findApiTrigger(automation);
const canRun = apiTrigger?.enabled === true;
async function onRun() {
@@ -164,7 +165,7 @@ function AutomationHeader({ automation }: { automation: Automation }) {
type="button"
onClick={onRun}
disabled={!canRun || running}
- title={!apiTrigger?.enabled ? "Enable the API trigger to run it" : undefined}
+ title={canRun ? undefined : "Enable the API trigger to run it"}
className={PRIMARY_BUTTON_CLASS}
>
<PlayIcon className="size-4" aria-hidden="true" />
diff --git a/apps/fabro-web/app/routes/automations.tsx b/apps/fabro-web/app/routes/automations.tsx
index f8dfb025f..dd388a690 100644
--- a/apps/fabro-web/app/routes/automations.tsx
+++ b/apps/fabro-web/app/routes/automations.tsx
@@ -18,6 +18,7 @@ import { FilterButton } from "../components/runs-list/filter-button";
import type { Automation, AutomationListResponse } from "@qltysh/fabro-api-client";
import { Link, useNavigate } from "react-router";
import { ApiError, apiData, automationsApi } from "../lib/api-client";
+import { findScheduleTrigger, hasEnabledApiTrigger } from "../lib/automation";
import { useAutomations } from "../lib/queries";
import { queryKeys } from "../lib/query-keys";
import { ConfirmDialog } from "../components/ui";
@@ -77,15 +78,6 @@ const MENU_ITEM_CLASS =
const MENU_ITEM_DANGER_CLASS =
"flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-coral transition-colors data-focus:bg-coral/10 data-focus:text-coral data-focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-60";
-function scheduleFor(automation: Automation): string | undefined {
- const schedule = automation.triggers.find((t) => t.type === "schedule");
- return schedule?.expression;
-}
-
-function hasEnabledApiTrigger(automation: Automation): boolean {
- return automation.triggers.some((t) => t.type === "api" && t.enabled);
-}
-
function mapAutomations(result: AutomationListResponse | undefined): AutomationRow[] {
const automations = result?.data ?? [];
return automations.map((a) => ({
@@ -94,7 +86,7 @@ function mapAutomations(result: AutomationListResponse | undefined): AutomationR
name: a.name,
workflow: a.target.workflow,
repository: a.target.repository,
- schedule: scheduleFor(a),
+ schedule: findScheduleTrigger(a)?.expression,
apiEnabled: hasEnabledApiTrigger(a),
icon: slugIconMap[a.target.workflow] ?? CodeBracketIcon,
color: slugColorMap[a.target.workflow] ?? "var(--color-teal-500)",
@@ -111,20 +103,19 @@ function PlayIcon({ className }: { className?: string }) {
function AutomationCard({
automation,
- disabled,
- menuDisabled,
+ busy,
running,
onRun,
onDelete,
}: {
automation: AutomationRow;
- disabled: boolean;
- menuDisabled: boolean;
+ busy: boolean;
running: boolean;
onRun: () => void;
onDelete: () => void;
}) {
const Icon = automation.icon;
+ const runDisabled = busy || !automation.apiEnabled;
return (
<div className="group flex items-center gap-4 rounded-md border border-line bg-panel/80 p-4 transition-all duration-200 hover:border-line-strong hover:bg-panel hover:shadow-lg hover:shadow-black/20">
<Link to={`/automations/${automation.id}`} className="flex min-w-0 flex-1 items-center gap-4">
@@ -162,7 +153,7 @@ function AutomationCard({
<button
type="button"
onClick={onRun}
- disabled={running || disabled}
+ disabled={running || runDisabled}
aria-label={running ? "Starting run…" : "Run automation"}
title={
running
@@ -181,7 +172,7 @@ function AutomationCard({
</button>
)}
- <RowMenu automation={automation} disabled={menuDisabled} onDelete={onDelete} />
+ <RowMenu automation={automation} disabled={busy} onDelete={onDelete} />
</div>
);
}
@@ -256,7 +247,7 @@ export default function Automations() {
const [runningId, setRunningId] = useState<string | null>(null);
async function runAutomation(automation: AutomationRow) {
- if (runningId || !automation.apiEnabled) return;
+ if (runningId) return;
setRunningId(automation.id);
try {
const run = await apiData(() => automationsApi.createAutomationRun(automation.id));
@@ -274,14 +265,15 @@ export default function Automations() {
}
}
+ const lowerQuery = query.toLowerCase();
const filtered = automations.filter(
(a) =>
(triggerFilter === "all" ||
(triggerFilter === "scheduled" && a.schedule != null) ||
(triggerFilter === "manual" && a.schedule == null)) &&
- (a.name.toLowerCase().includes(query.toLowerCase()) ||
- a.workflow.toLowerCase().includes(query.toLowerCase()) ||
- a.repository.toLowerCase().includes(query.toLowerCase())),
+ (a.name.toLowerCase().includes(lowerQuery) ||
+ a.workflow.toLowerCase().includes(lowerQuery) ||
+ a.repository.toLowerCase().includes(lowerQuery)),
);
async function confirmDelete() {
@@ -336,12 +328,7 @@ export default function Automations() {
<AutomationCard
key={automation.id}
automation={automation}
- disabled={
- deleting ||
- !automation.apiEnabled ||
- (runningId !== null && runningId !== automation.id)
- }
- menuDisabled={deleting || (runningId !== null && runningId !== automation.id)}
+ busy={deleting || (runningId !== null && runningId !== automation.id)}
running={runningId === automation.id}
onRun={() => runAutomation(automation)}
onDelete={() => setPendingDelete(automation)}
diff --git a/lib/crates/fabro-automation/src/model.rs b/lib/crates/fabro-automation/src/model.rs
index 90dc24eb7..c5521cfe0 100644
--- a/lib/crates/fabro-automation/src/model.rs
+++ b/lib/crates/fabro-automation/src/model.rs
@@ -63,7 +63,6 @@ impl Automation {
}
/// Returns the enabled API trigger if the automation has one.
- /// Returns `None` when the automation has no enabled API trigger.
#[must_use]
pub fn enabled_api_trigger(&self) -> Option<&ApiTrigger> {
self.triggers.iter().find_map(|trigger| match trigger {
@@ -441,7 +440,7 @@ expression = "0 0 * * *"
}
#[test]
- fn persisted_toml_rejects_legacy_top_level_enabled() {
+ fn persisted_toml_rejects_unknown_top_level_fields() {
let bytes = br#"
name = "Legacy"
enabled = false

View file

@ -0,0 +1,6 @@
{
"outcome": "succeeded",
"notes": "Stage completed: simplify_opus",
"failure_reason": null,
"timestamp": "2026-05-29T20:42:53.702751Z"
}

View file

@ -0,0 +1,356 @@
Goal: # Remove Automation Master Enabled Gate Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Remove the top-level automation `enabled` field so trigger-level `enabled` is the only activation control.
**Architecture:** Automations keep their existing file-backed TOML store and REST API, but the top-level master gate disappears from the Rust domain model, persisted TOML, OpenAPI schemas, generated clients, and web UI. API/manual run creation checks only for an enabled `api` trigger. No migration or legacy parser path is added because automations are brand new; TOML that still contains top-level `enabled` is obsolete input.
**Tech Stack:** Rust, serde/TOML, Axum, OpenAPI/progenitor, TypeScript Axios client generation, React 19, SWR, Tailwind CSS.
---
## File Structure
- Modify `lib/crates/fabro-automation/src/model.rs` for the core type and TOML shape.
- Modify `lib/crates/fabro-automation/src/store.rs` for unit fixtures that create automation drafts/replacements.
- Modify `lib/crates/fabro-server/src/server/handler/automations.rs` for API-trigger conflict wording.
- Modify `lib/crates/fabro-server/tests/it/api/automations.rs` for HTTP fixtures and behavior tests.
- Modify `docs/public/api-reference/fabro-api.yaml` and regenerate `lib/packages/fabro-api-client/src/**`.
- Modify `lib/crates/fabro-api/tests/automation_round_trip.rs` for Rust/OpenAPI type parity.
- Modify `apps/fabro-web/app/components/automation-form.tsx`, `apps/fabro-web/app/routes/automations-new.tsx`, `apps/fabro-web/app/routes/automations-edit.tsx`, `apps/fabro-web/app/routes/automation-detail.tsx`, and `apps/fabro-web/app/routes/automations.tsx` for UI state and trigger-derived run availability.
## Task 1: Remove The Domain Master Gate
**Files:**
- Modify: `lib/crates/fabro-automation/src/model.rs`
- Modify: `lib/crates/fabro-automation/src/store.rs`
- [ ] Remove `pub enabled: bool` from `Automation`, `AutomationDraft`, `AutomationReplace`, and `PersistedAutomation`.
- [ ] Remove `enabled` from every conversion between `AutomationDraft`, `AutomationReplace`, `PersistedAutomation`, and `Automation`.
- [ ] Update `Automation::enabled_api_trigger()` to return an enabled API trigger without checking a top-level automation flag:
```rust
/// Returns the enabled API trigger if the automation has one.
/// Returns `None` when the automation has no enabled API trigger.
#[must_use]
pub fn enabled_api_trigger(&self) -> Option<&ApiTrigger> {
self.triggers.iter().find_map(|trigger| match trigger {
AutomationTrigger::Api(trigger) if trigger.enabled => Some(trigger),
_ => None,
})
}
```
- [ ] Remove the now-unused `default_true()` helper if no other code in the file still uses it.
- [ ] Update `persisted_toml_applies_defaults_and_canonicalizes_without_id_or_revision` so the fixture has no top-level `enabled = true`, does not assert `automation.enabled`, and asserts the canonical TOML has no top-level `enabled` line:
```rust
assert!(!top_level_lines(&toml).any(|line| line.starts_with("enabled = ")));
```
- [ ] Add a focused no-compatibility test in `lib/crates/fabro-automation/src/model.rs`:
```rust
#[test]
fn persisted_toml_rejects_legacy_top_level_enabled() {
let bytes = br#"
name = "Legacy"
enabled = false
[target]
repository = "fabro-sh/fabro"
ref = "main"
workflow = "release"
[[triggers]]
type = "api"
id = "manual"
enabled = true
"#;
let result = Automation::from_toml_bytes(AutomationId::new("legacy").unwrap(), bytes);
assert!(result.is_err());
}
```
- [ ] Update `lib/crates/fabro-automation/src/store.rs` test helpers so `draft()` and `replacement()` no longer set top-level `enabled`.
- [ ] Run:
```bash
cargo nextest run -p fabro-automation
```
Expected: all `fabro-automation` tests pass.
## Task 2: Update Server Behavior And Tests
**Files:**
- Modify: `lib/crates/fabro-server/src/server/handler/automations.rs`
- Modify: `lib/crates/fabro-server/tests/it/api/automations.rs`
- [ ] Change the `create_automation_run` conflict detail from:
```rust
"automation is disabled or has no enabled API trigger"
```
to:
```rust
"automation has no enabled API trigger"
```
Keep the existing code `"automation_api_trigger_disabled"` for compatibility with current clients and tests.
- [ ] Remove top-level `"enabled": true` from `automation_body()`.
- [ ] Remove top-level `"enabled": false` from `replacement_body()`.
- [ ] Delete `disabled_automation_run_endpoint_returns_conflict_code`; the master gate no longer exists.
- [ ] Keep `disabled_api_trigger_run_endpoint_returns_conflict_code` and `missing_api_trigger_run_endpoint_returns_conflict_code` as the authoritative inactive-run tests.
- [ ] Update any test that mutates `body["enabled"]` or expects top-level enabled in automation JSON/TOML.
- [ ] Run:
```bash
cargo nextest run -p fabro-server automations
```
Expected: automation integration tests pass.
## Task 3: Update OpenAPI And Generated API Types
**Files:**
- Modify: `docs/public/api-reference/fabro-api.yaml`
- Modify: `lib/crates/fabro-api/tests/automation_round_trip.rs`
- Regenerate: `lib/packages/fabro-api-client/src/**`
- [ ] In the `Automation` schema, remove top-level `enabled` from `required` and `properties`.
- [ ] In `CreateAutomationRequest`, remove top-level `enabled` from `properties`.
- [ ] In `ReplaceAutomationRequest`, remove top-level `enabled` from `required` and `properties`.
- [ ] Keep `enabled` on `AutomationApiTrigger` and `AutomationScheduleTrigger`.
- [ ] Update the `POST /api/v1/automations/{id}/runs` `409` description from:
```yaml
description: Automation is disabled or has no enabled API trigger
```
to:
```yaml
description: Automation has no enabled API trigger
```
- [ ] Update `lib/crates/fabro-api/tests/automation_round_trip.rs` so the `Automation`, `CreateAutomationRequest`, and `ReplaceAutomationRequest` JSON fixtures no longer include top-level `"enabled"`.
- [ ] Run:
```bash
cargo build -p fabro-api
```
Expected: progenitor type generation succeeds.
- [ ] Run:
```bash
cargo nextest run -p fabro-api automation_round_trip
```
Expected: automation type identity and JSON parity tests pass.
- [ ] Regenerate the TypeScript client:
```bash
cd lib/packages/fabro-api-client && bun run generate
```
Expected: generated model files remove top-level `enabled` from `Automation`, `CreateAutomationRequest`, and `ReplaceAutomationRequest`.
## Task 4: Remove The Web UI Master Toggle
**Files:**
- Modify: `apps/fabro-web/app/components/automation-form.tsx`
- Modify: `apps/fabro-web/app/routes/automations-new.tsx`
- Modify: `apps/fabro-web/app/routes/automations-edit.tsx`
- Modify: `apps/fabro-web/app/routes/automation-detail.tsx`
- Modify: `apps/fabro-web/app/routes/automations.tsx`
- [ ] Remove `enabled` from `AutomationFormValues` and `EMPTY_AUTOMATION_FORM`.
- [ ] Remove `enabled: automation.enabled` from `automationToFormValues`.
- [ ] Delete the `Row title="Enabled"` block from `AutomationFormFields`.
- [ ] Remove `enabled: values.enabled` from the create payload in `automations-new.tsx`.
- [ ] Remove `enabled: values.enabled` from the replace payload in `automations-edit.tsx`.
- [ ] In `isFormValid`, remove the requirement that at least one trigger is enabled. The final return should only require non-empty ID, name, repository, ref, and workflow:
```ts
return (
values.id.trim() !== "" &&
values.name.trim() !== "" &&
values.repository.trim() !== "" &&
values.ref.trim() !== "" &&
values.workflow.trim() !== ""
);
```
- [ ] In `automation-detail.tsx`, change run availability to:
```ts
const canRun = apiTrigger?.enabled === true;
```
- [ ] In `automation-detail.tsx`, remove `StatusChip`, remove its use, and simplify the Run button `title` so only a missing/disabled API trigger explains the disabled state:
```ts
title={!apiTrigger?.enabled ? "Enable the API trigger to run it" : undefined}
```
- [ ] In `automations.tsx`, extend `AutomationRow` with `apiEnabled: boolean`, set it from the enabled API trigger in `mapAutomations`, and pass `disabled={deleting || !automation.apiEnabled || (runningId !== null && runningId !== automation.id)}` to the run button path.
- [ ] In `AutomationCard`, make the run button title reflect trigger-disabled state:
```tsx
title={
running
? "Starting run..."
: automation.apiEnabled
? "Run automation"
: "Enable the API trigger to run it"
}
```
Use exactly this title text for the disabled/run states; do not change visible button copy.
- [ ] Run:
```bash
cd apps/fabro-web && bun run typecheck
```
Expected: TypeScript passes with no `automation.enabled` references.
## Task 5: Final Verification
**Files:**
- No additional source edits expected.
- [ ] Run the focused backend checks:
```bash
cargo nextest run -p fabro-automation
cargo nextest run -p fabro-api automation_round_trip
cargo nextest run -p fabro-server automations
```
Expected: all focused Rust checks pass.
- [ ] Run the focused frontend checks:
```bash
cd lib/packages/fabro-api-client && bun run typecheck
cd apps/fabro-web && bun run typecheck
```
Expected: generated client and web app typecheck.
- [ ] Search for leftover master-gate references:
```bash
rg -n "automation\\.enabled|enabled_api_trigger\\(\\).*automation|Automation is disabled|automation is disabled|\\\"enabled\\\": true" \
lib/crates/fabro-automation \
lib/crates/fabro-server/tests/it/api/automations.rs \
lib/crates/fabro-server/src/server/handler/automations.rs \
lib/crates/fabro-api/tests/automation_round_trip.rs \
apps/fabro-web/app/components/automation-form.tsx \
apps/fabro-web/app/routes/automations-new.tsx \
apps/fabro-web/app/routes/automations-edit.tsx \
apps/fabro-web/app/routes/automation-detail.tsx \
apps/fabro-web/app/routes/automations.tsx
```
Expected: no hits for the removed top-level automation enabled gate. Hits for trigger-level `enabled` are acceptable when they are clearly on `AutomationTrigger`, `ApiTrigger`, or `ScheduleTrigger`.
- [ ] Optional formatting check:
```bash
cargo +nightly-2026-04-14 fmt --check --all
```
Expected: Rust formatting passes. If it fails on touched Rust files, run `cargo +nightly-2026-04-14 fmt --all` and re-run the focused Rust checks.
## Explicit Assumptions
- No migration or compatibility parser is added for top-level automation `enabled`.
- Existing automation TOML files that still contain top-level `enabled` are obsolete and may fail startup until manually edited.
- Trigger-level `enabled` remains public API and persisted TOML.
- API/manual run creation remains controlled by the enabled `api` trigger only.
- Schedule execution, cron semantics, and schedule-trigger preservation are outside this cleanup.
## Completed stages
- **toolchain**: succeeded
- Script: `command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1`
- Output:
```
cargo 1.95.0 (f2d3ce0bd 2026-03-21)
```
- **preflight_compile**: succeeded
- Script: `cargo check -q --workspace 2>&1`
- Output: (empty)
- **preflight_lint**: succeeded
- Script: `cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1`
- Output: (empty)
- **implement**: succeeded
- Model: gpt-5.5, 1.3m tokens in / 17.0k out
- **simplify_opus**: succeeded
- Model: claude-opus-4-7, 94.3k tokens in / 18.7k out
- Files: /home/daytona/workspace/fabro/apps/fabro-web/app/components/automation-form.tsx, /home/daytona/workspace/fabro/apps/fabro-web/app/lib/automation.ts, /home/daytona/workspace/fabro/apps/fabro-web/app/routes/automation-detail.tsx, /home/daytona/workspace/fabro/apps/fabro-web/app/routes/automations.tsx, /home/daytona/workspace/fabro/lib/crates/fabro-automation/src/model.rs
# Simplify: Code Review and Cleanup
Review changes vs. origin for reuse, quality, and efficiency. Fix any issues found.
## Phase 1: Identify Changes
Run git diff (or git diff HEAD if there are staged changes) to see what changed. If there are no git changes, review the most recently modified files that the user mentioned or that you edited earlier in this conversation.
## Phase 2: Launch Three Review Agents in Parallel
Use the Agent tool to launch all three agents concurrently in a single message. Pass each agent the full diff so it has the complete context.
### Agent 1: Code Reuse Review
For each change:
1. Search for existing utilities and helpers that could replace newly written code. Use Grep to find similar patterns elsewhere in the codebase — common locations are utility directories, shared modules, and files adjacent to the changed ones.
2. Flag any new function that duplicates existing functionality. Suggest the existing function to use instead.
3. Flag any inline logic that could use an existing utility — hand-rolled string manipulation, manual path handling, custom environment checks, ad-hoc type guards, and similar patterns are common candidates.
Note: This is a greenfield app, so focus on maximizing simplicity and don't worry about changing things to achieve it.
### Agent 2: Code Quality Review
Review the same changes for hacky patterns:
1. Redundant state: state that duplicates existing state, cached values that could be derived, observers/effects that could be direct calls
2. Parameter sprawl: adding new parameters to a function instead of generalizing or restructuring existing ones
3. Copy-paste with slight variation: near-duplicate code blocks that should be unified with a shared abstraction
4. Leaky abstractions: exposing internal details that should be encapsulated, or breaking existing abstraction boundaries
5. Stringly-typed code: using raw strings where constants, enums (string unions), or branded types already exist in the codebase
Note: This is a greenfield app, so be aggressive in optimizing quality.
### Agent 3: Efficiency Review
Review the same changes for efficiency:
1. Unnecessary work: redundant computations, repeated file reads, duplicate network/API calls, N+1 patterns
2. Missed concurrency: independent operations run sequentially when they could run in parallel
3. Hot-path bloat: new blocking work added to startup or per-request/per-render hot paths
4. Unnecessary existence checks: pre-checking file/resource existence before operating (TOCTOU anti-pattern) — operate directly and handle the error
5. Memory: unbounded data structures, missing cleanup, event listener leaks
6. Overly broad operations: reading entire files when only a portion is needed, loading all items when filtering for one
## Phase 3: Fix Issues
Wait for all three agents to complete. Aggregate their findings and fix each issue directly. If a finding is a false positive or not worth addressing, note it and move on — do not argue with the finding, just skip it.
When done, briefly summarize what was fixed (or confirm the code was already clean).

View file

@ -0,0 +1,5 @@
{
"mode": "agent",
"provider": "openai",
"model": "gpt-5.5"
}

View file

@ -0,0 +1,21 @@
Implemented the simplify/review cleanup.
Changed:
- `lib/crates/fabro-automation/src/model.rs`
- Added the missing second sentence to `Automation::enabled_api_trigger()` docs.
- Renamed the focused no-compatibility test to `persisted_toml_rejects_legacy_top_level_enabled`.
Validation run:
- `cargo nextest run -p fabro-automation` ✅
- `cargo nextest run -p fabro-api --test automation_round_trip` ✅
- Note: the plan’s `cargo nextest run -p fabro-api automation_round_trip` selector matched zero tests, so I reran with the explicit integration test target.
- `cargo nextest run -p fabro-server --features test-support --test it automations` ✅
- Note: the server integration test target requires `test-support`.
- `cargo build -p fabro-api` ✅
- `cd lib/packages/fabro-api-client && bun run typecheck` ✅
- `cd apps/fabro-web && bun run typecheck` ✅
- Leftover master-gate search ✅
- Only trigger-level `enabled` hits remain, plus the expected `enabled_api_trigger()` handler call.
Working tree has one small source modification:
- `lib/crates/fabro-automation/src/model.rs`