From bcfd16b83324dd6fa4102b3110cbe0fe5aced5d1 Mon Sep 17 00:00:00 2001 From: "brynary-fabro[bot]" <265161896+brynary-fabro[bot]@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:54:40 -0400 Subject: [PATCH] Rename `[feature_flags]` to `[features]` (#11) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR renames the `[feature_flags]` configuration section to `[features]` and the corresponding `FeatureFlags` type to `Features` across the entire stack. The change touches the OpenAPI spec (source of truth), Rust config/API crates, the generated TypeScript API client, the web application, and the demo config file to ensure consistency. On the Rust side, the `FeatureFlags` struct becomes `Features` in `fabro-config`, and all field references (`config.feature_flags` → `config.features`) are updated in `fabro-api` demo/test code along with the relevant unit tests. On the TypeScript side, the generated client reflects the OpenAPI rename (`feature-flags.ts` → `features.ts`, `FeatureFlags` → `Features`), and manual edits in `fabro-web` update the `AppConfig` interface, defaults constant (`FEATURES_DEFAULTS`), loader data destructuring, and template usage throughout `config.server.ts`, `app-shell.tsx`, and `start.tsx`. The regeneration also picked up an unrelated new `GitHubConfiguration` schema that was already present in the OpenAPI spec but hadn't been generated yet, resulting in the new `git-hub-configuration.ts` file and its addition to the server configuration type. ### Fabro Details
Ran 7 stages in 15m 4s for $3.08 | Stage | Duration | Cost | Retries | |---|---|---|---| | start | 0s | – | 0 | | toolchain | 0s | – | 0 | | preflight_compile | 0s | – | 0 | | preflight_lint | 0s | – | 0 | | implement | 0s | $2.18 | 0 | | simplify | 0s | $0.90 | 0 | | verify | 0s | – | 0 | | **Total** | **15m 4s** | **$3.08** | **0** |
Ran ImplementAndSimplify.fabro (10 nodes and 13 edges) ```dot digraph ImplementAndSimplify { graph [ goal="Implement and simplify", model_stylesheet=" * { backend: api; model: claude-opus-4-6;} " ] rankdir=LR start [shape=Mdiamond, label="Start"] exit [shape=Msquare, label="Exit"] toolchain [label="Toolchain", shape=parallelogram, 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", max_retries=0] preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check 2>&1", max_retries=0] preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1", max_retries=0] fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3] implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan."] simplify [label="Simplify", prompt="@prompts/simplify.md"] verify [label="Verify", shape=parallelogram, script="cargo clippy -- -D warnings 2>&1 && cargo test 2>&1", goal_gate=true, retry_target="fixup"] fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all clippy lint warnings and test failures.", max_visits=3] start -> toolchain toolchain -> preflight_compile [condition="outcome=success"] toolchain -> exit preflight_compile -> preflight_lint [condition="outcome=success"] preflight_compile -> exit preflight_lint -> implement [condition="outcome=success"] preflight_lint -> fix_lints fix_lints -> preflight_lint implement -> simplify -> verify verify -> exit [condition="outcome=success"] verify -> fixup fixup -> verify } ```
⚒️ Generated with [Fabro](https://fabro.sh) --------- Co-authored-by: Fabro Co-authored-by: Bryan Helmkamp Co-authored-by: Claude Opus 4.6 (1M context) --- apps/fabro-web/app/layouts/app-shell.tsx | 4 +-- apps/fabro-web/app/lib/config.server.ts | 10 +++---- apps/fabro-web/app/routes/start.tsx | 8 +++--- docker/demo-server.toml | 2 +- docs/api-reference/fabro-api.yaml | 6 ++--- lib/crates/fabro-api/src/demo/mod.rs | 2 +- .../fabro-api/tests/openapi_conformance.rs | 2 +- lib/crates/fabro-config/src/server.rs | 14 +++++----- .../src/.openapi-generator/FILES | 3 ++- .../models/{feature-flags.ts => features.ts} | 2 +- .../src/models/git-hub-configuration.ts | 26 +++++++++++++++++++ .../fabro-api-client/src/models/index.ts | 3 ++- .../src/models/server-configuration.ts | 8 ++++-- 13 files changed, 61 insertions(+), 29 deletions(-) rename lib/packages/fabro-api-client/src/models/{feature-flags.ts => features.ts} (93%) create mode 100644 lib/packages/fabro-api-client/src/models/git-hub-configuration.ts diff --git a/apps/fabro-web/app/layouts/app-shell.tsx b/apps/fabro-web/app/layouts/app-shell.tsx index 90c3a8d38..f926bcc29 100644 --- a/apps/fabro-web/app/layouts/app-shell.tsx +++ b/apps/fabro-web/app/layouts/app-shell.tsx @@ -42,13 +42,13 @@ export async function loader({ request }: Route.LoaderArgs) { const { provider } = config.web.auth; const demoMode = isDemoMode(request); if (provider === "insecure_disabled") { - return { user: DEMO_USER, demoMode, feature_flags: config.feature_flags }; + return { user: DEMO_USER, demoMode, features: config.features }; } if (provider === "github" && !isGitHubAppConfigured()) { throw redirect("/setup"); } const user = await requireUser(request); - return { user, provider, demoMode, feature_flags: config.feature_flags }; + return { user, provider, demoMode, features: config.features }; } export async function action({ request }: Route.ActionArgs) { diff --git a/apps/fabro-web/app/lib/config.server.ts b/apps/fabro-web/app/lib/config.server.ts index 1ddb7bc53..dc22d99c1 100644 --- a/apps/fabro-web/app/lib/config.server.ts +++ b/apps/fabro-web/app/lib/config.server.ts @@ -20,7 +20,7 @@ interface GitConfig { slug: string | null; } -interface FeatureFlags { +interface Features { session_sandboxes: boolean; } @@ -33,7 +33,7 @@ interface AppConfig { web: WebConfig; api: ApiConfig; git: GitConfig; - feature_flags: FeatureFlags; + features: Features; } const AUTH_DEFAULTS: AuthConfig = { @@ -58,7 +58,7 @@ const GIT_DEFAULTS: GitConfig = { slug: null, }; -const FEATURE_FLAGS_DEFAULTS: FeatureFlags = { +const FEATURES_DEFAULTS: Features = { session_sandboxes: false, }; @@ -78,7 +78,7 @@ function loadAppConfig(): AppConfig { const rawWebAuth = (rawWeb.auth ?? {}) as Partial; const rawApi = (raw.api ?? {}) as Partial; const rawGit = (raw.git ?? {}) as Partial; - const rawFeatureFlags = (raw.feature_flags ?? {}) as Partial; + const rawFeatures = (raw.features ?? {}) as Partial; const demo = process.env.FABRO_DEMO === "1"; @@ -94,7 +94,7 @@ function loadAppConfig(): AppConfig { ? { ...API_DEFAULTS, ...rawApi, authentication_strategy: "insecure_disabled" } : { ...API_DEFAULTS, ...rawApi }, git: { ...GIT_DEFAULTS, ...rawGit }, - feature_flags: { ...FEATURE_FLAGS_DEFAULTS, ...rawFeatureFlags }, + features: { ...FEATURES_DEFAULTS, ...rawFeatures }, }; } diff --git a/apps/fabro-web/app/routes/start.tsx b/apps/fabro-web/app/routes/start.tsx index ebb6fd8cc..9b0a3366b 100644 --- a/apps/fabro-web/app/routes/start.tsx +++ b/apps/fabro-web/app/routes/start.tsx @@ -33,12 +33,12 @@ export function meta({}: Route.MetaArgs) { } export async function loader({ request }: Route.LoaderArgs) { - const { feature_flags } = getAppConfig(); + const { features } = getAppConfig(); const { data: apiSessions } = await apiJson("/sessions", { request }); const sessionGroups = groupSessionsByDate( apiSessions.map((s) => ({ id: s.id, title: s.title, created_at: s.created_at })) ); - return { sessionGroups, feature_flags }; + return { sessionGroups, features }; } const projects = [ @@ -99,7 +99,7 @@ function SessionSidebar({ groups }: { groups: { label: string; sessions: { id: s } export default function Start({ loaderData }: Route.ComponentProps) { - const { sessionGroups, feature_flags } = loaderData; + const { sessionGroups, features } = loaderData; const [prompt, setPrompt] = useState(""); const [project, setProject] = useState(projects[0]); const [branch, setBranch] = useState(branches[0]); @@ -158,7 +158,7 @@ export default function Start({ loaderData }: Route.ComponentProps) { />
- {feature_flags.session_sandboxes && ( + {features.session_sandboxes && (
ServerConfig { strategy: WebhookStrategy::TailscaleFunnel, }), }, - feature_flags: FeatureFlags { + features: Features { session_sandboxes: true, }, log: LogConfig { diff --git a/lib/crates/fabro-config/src/server.rs b/lib/crates/fabro-config/src/server.rs index 1f03e0d81..0a226761b 100644 --- a/lib/crates/fabro-config/src/server.rs +++ b/lib/crates/fabro-config/src/server.rs @@ -115,7 +115,7 @@ impl Default for WebConfig { } #[derive(Debug, Clone, Default, Deserialize, PartialEq, Serialize)] -pub struct FeatureFlags { +pub struct Features { #[serde(default)] pub session_sandboxes: bool, } @@ -136,7 +136,7 @@ pub struct ServerConfig { #[serde(default)] pub git: GitConfig, #[serde(default)] - pub feature_flags: FeatureFlags, + pub features: Features, #[serde(default)] pub log: LogConfig, #[serde(flatten)] @@ -479,17 +479,17 @@ matcher = "agent_loop" } #[test] - fn parse_feature_flags() { - let toml = "[feature_flags]\nsession_sandboxes = true"; + fn parse_features() { + let toml = "[features]\nsession_sandboxes = true"; let config: ServerConfig = toml::from_str(toml).unwrap(); - assert!(config.feature_flags.session_sandboxes); + assert!(config.features.session_sandboxes); } #[test] - fn parse_feature_flags_defaults() { + fn parse_features_defaults() { let toml = ""; let config: ServerConfig = toml::from_str(toml).unwrap(); - assert!(!config.feature_flags.session_sandboxes); + assert!(!config.features.session_sandboxes); } #[test] diff --git a/lib/packages/fabro-api-client/src/.openapi-generator/FILES b/lib/packages/fabro-api-client/src/.openapi-generator/FILES index 0c2a3bc63..0e119bf95 100644 --- a/lib/packages/fabro-api-client/src/.openapi-generator/FILES +++ b/lib/packages/fabro-api-client/src/.openapi-generator/FILES @@ -58,13 +58,14 @@ models/exe-configuration.ts models/execute-query-request.ts models/execute-query-response-rows-inner-inner.ts models/execute-query-response.ts -models/feature-flags.ts +models/features.ts models/file-checkpoint.ts models/file-diff.ts models/friction-kind.ts models/friction-point.ts models/git-author-configuration.ts models/git-configuration.ts +models/git-hub-configuration.ts models/health-response.ts models/history-entry.ts models/hook-definition.ts diff --git a/lib/packages/fabro-api-client/src/models/feature-flags.ts b/lib/packages/fabro-api-client/src/models/features.ts similarity index 93% rename from lib/packages/fabro-api-client/src/models/feature-flags.ts rename to lib/packages/fabro-api-client/src/models/features.ts index fe63b674b..75e01c2cf 100644 --- a/lib/packages/fabro-api-client/src/models/feature-flags.ts +++ b/lib/packages/fabro-api-client/src/models/features.ts @@ -17,7 +17,7 @@ /** * Feature flags. */ -export interface FeatureFlags { +export interface Features { /** * Enable session sandboxes. */ diff --git a/lib/packages/fabro-api-client/src/models/git-hub-configuration.ts b/lib/packages/fabro-api-client/src/models/git-hub-configuration.ts new file mode 100644 index 000000000..2b73d3c68 --- /dev/null +++ b/lib/packages/fabro-api-client/src/models/git-hub-configuration.ts @@ -0,0 +1,26 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Fabro Run API + * HTTP API for managing Fabro workflow run executions. + * + * The version of the OpenAPI document: 0.1.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * GitHub App token injection configuration. + */ +export interface GitHubConfiguration { + /** + * GitHub API permissions to request (e.g. contents = write). + */ + 'permissions'?: { [key: string]: string; }; +} + diff --git a/lib/packages/fabro-api-client/src/models/index.ts b/lib/packages/fabro-api-client/src/models/index.ts index 6e52f6d77..3bf6eacf0 100644 --- a/lib/packages/fabro-api-client/src/models/index.ts +++ b/lib/packages/fabro-api-client/src/models/index.ts @@ -39,13 +39,14 @@ export * from './exe-configuration'; export * from './execute-query-request'; export * from './execute-query-response'; export * from './execute-query-response-rows-inner-inner'; -export * from './feature-flags'; +export * from './features'; export * from './file-checkpoint'; export * from './file-diff'; export * from './friction-kind'; export * from './friction-point'; export * from './git-author-configuration'; export * from './git-configuration'; +export * from './git-hub-configuration'; export * from './health-response'; export * from './history-entry'; export * from './hook-definition'; diff --git a/lib/packages/fabro-api-client/src/models/server-configuration.ts b/lib/packages/fabro-api-client/src/models/server-configuration.ts index d41fc0968..1728da819 100644 --- a/lib/packages/fabro-api-client/src/models/server-configuration.ts +++ b/lib/packages/fabro-api-client/src/models/server-configuration.ts @@ -24,12 +24,15 @@ import type { AssetsConfiguration } from './assets-configuration'; import type { CheckpointConfiguration } from './checkpoint-configuration'; // May contain unused imports in some cases // @ts-ignore -import type { FeatureFlags } from './feature-flags'; +import type { Features } from './features'; // May contain unused imports in some cases // @ts-ignore import type { GitConfiguration } from './git-configuration'; // May contain unused imports in some cases // @ts-ignore +import type { GitHubConfiguration } from './git-hub-configuration'; +// May contain unused imports in some cases +// @ts-ignore import type { HookDefinition } from './hook-definition'; // May contain unused imports in some cases // @ts-ignore @@ -68,7 +71,7 @@ export interface ServerConfiguration { 'web'?: WebConfiguration; 'api'?: ApiConfiguration; 'git'?: GitConfiguration; - 'feature_flags'?: FeatureFlags; + 'features'?: Features; 'log'?: LogConfiguration; /** * Default working directory. @@ -89,5 +92,6 @@ export interface ServerConfiguration { * Default MCP server configurations. */ 'mcp_servers'?: { [key: string]: McpServerEntry; }; + 'github'?: GitHubConfiguration; }