Rename [feature_flags] to [features] (#11)

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

<details>
<summary>Ran 7 stages in 15m 4s for $3.08</summary>

| 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** |

</details>

<details>
<summary>Ran <code>ImplementAndSimplify.fabro</code> (10 nodes and 13
edges)</summary>

```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
}

```

</details>

⚒️ Generated with [Fabro](https://fabro.sh)

---------

Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
brynary-fabro[bot] 2026-03-15 19:54:40 -04:00 • committed by GitHub
parent d90a805f1a
commit bcfd16b833
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 61 additions and 29 deletions

View file

@ -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) {

View file

@ -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<AuthConfig>;
const rawApi = (raw.api ?? {}) as Partial<ApiConfig>;
const rawGit = (raw.git ?? {}) as Partial<GitConfig>;
const rawFeatureFlags = (raw.feature_flags ?? {}) as Partial<FeatureFlags>;
const rawFeatures = (raw.features ?? {}) as Partial<Features>;
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 },
};
}

View file

@ -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<PaginatedSessionList>("/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) {
/>
<div className="absolute bottom-3 inset-x-3 flex items-center justify-between">
{feature_flags.session_sandboxes && (
{features.session_sandboxes && (
<div className="flex items-center gap-1.5">
<Picker
value={project}

View file

@ -1,7 +1,7 @@
[api]
base_url = "http://api:3000"
[feature_flags]
[features]
session_sandboxes = false
[checkpoint]

View file

@ -4273,8 +4273,8 @@ components:
$ref: "#/components/schemas/ApiConfiguration"
git:
$ref: "#/components/schemas/GitConfiguration"
feature_flags:
$ref: "#/components/schemas/FeatureFlags"
features:
$ref: "#/components/schemas/Features"
log:
$ref: "#/components/schemas/LogConfiguration"
work_dir:
@ -4498,7 +4498,7 @@ components:
enum:
- tailscale_funnel
FeatureFlags:
Features:
description: Feature flags.
type: object
properties:

View file

@ -3282,7 +3282,7 @@ mod settings {
author: Default::default(),
webhooks: None,
},
feature_flags: FeatureFlags {
features: Features {
session_sandboxes: false,
},
log: Default::default(),

View file

@ -270,7 +270,7 @@ fn fully_populated_server_config() -> ServerConfig {
strategy: WebhookStrategy::TailscaleFunnel,
}),
},
feature_flags: FeatureFlags {
features: Features {
session_sandboxes: true,
},
log: LogConfig {

View file

@ -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]

View file

@ -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

View file

@ -17,7 +17,7 @@
/**
* Feature flags.
*/
export interface FeatureFlags {
export interface Features {
/**
* Enable session sandboxes.
*/

View file

@ -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; };
}

View file

@ -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';

View file

@ -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;
}