name: oxy-compile-boundary
description: Use when adding a new YAML entity type to Oxy (e.g. a new file extension under crates/oxy-compile/src/walker.rs like .foo.yml), when introducing a new runtime read site that walks the workspace filesystem, when wiring a new handler that calls ConfigManager::resolve_* or fs::read_to_string(workspace_path...), or any time someone proposes a feature that "just reads from the workspace dir." Also triggers on phrases like "new file extension", "add a YAML config", "load this from disk", "scan the workspace for", "read the YAML file" — the compile boundary expects every NEW workspace artifact to be a row in Postgres, not a per-request FS read.
Compile every workspace artifact to Postgres
Oxy's runtime no longer walks the workspace filesystem on customer-facing requests. PR #2460 (the compile boundary work) made every YAML entity addressable as a *_definitions row keyed by revision_id. The IDE Compile button promotes a revision and every read site serves from Postgres until the next compile. The boundary is always on — there are no feature flags; readers fall through to the filesystem on any miss.
This skill is the rule for anyone adding a new file type the runtime needs to read. Skip this skill only when the read is genuinely IDE-only (file CRUD, git ops) or a one-time onboarding write.
The contract: when you add a new .foo.yml
You owe all five of these before the feature ships. Skipping any one of them means the new file walks FS on every customer request — exactly what we just stopped doing.
Walker — add a
FileKind::Foovariant incrates/oxy-compile/src/walker.rsand a glob indiscover()so a compile pass finds the file. Root-only files go into the explicitif path.is_file()block (seeConfigandMonitorConfigas templates). Glob patterns go throughpush_glob.Compile output — add
CompiledRow::Foo(CompiledFoo { ... })incrates/oxy-compile/src/compile.rs, a match arm incompile_one(), and arow_dedupe_keyentry (orNoneif the file is a singleton likeconfig.yml/.monitor.yml). For named entities (agents, views, topics) usecompile_named_yaml— it handles thename/file_path/definitionshape uniformly. For singletons, write a smallcompile_foo()that parses and emits one row.Schema — append the new table to the squashed migration
crates/migration/src/m20260606_000002_create_compile_boundary.rs(BOTHup()and the reverse-orderdown()). Add aMonitorConfigs-styleDeriveIdenenum at the end of the file. The FK convention isfrom(Foo::Table, Foo::RevisionId).to(Revisions::Table, Revisions::RevisionId).on_delete(ForeignKeyAction::Cascade). Add the matching entity file undercrates/entity/src/foo_definitions.rsand register it incrates/entity/src/lib.rs.Writer — add a
CompiledRow::Foo(f) => foos.push(...)arm incrates/oxy-compile/src/writer.rs, declare thelet mut foos = Vec::new()near the top of the function, and add the bulk-insert call near the end. The order mirrors the existing kinds.Reader + handler wiring — add
resolve_foo(andlist_foosif applicable) tocrates/app/src/server/api/compiled_reader.rs. They follow theopen_compiled_revision→find_by_idshape. Then wire the runtime handler:- If the handler accepts a string / struct: read the compiled row, deserialise from the JSONB definition, fall through to FS on miss.
- If the handler accepts a
Path(e.g. an external library likeairlayeroroxy_metric_monitoring): use the materialiser pattern incrates/app/src/server/api/semantic_scan.rs— write the Postgres bytes to atempfile::TempDir, pass that path, hold theTempDirguard until the call returns.
open_compiled_revision already handles the branch carve-out + revision lookup for every reader — you do not need to re-implement it per-surface.
S3 blob storage (semantic views / topics only)
Large semantic view / topic bodies move to S3 when OXY_COMPILE_BLOB_S3_BUCKET is set. The compile worker uploads each body to s3://<bucket>/workspaces/<workspace_id>/{semantic_views,semantic_topics}/<name>-<sha[..32]>.yml and stores the key in semantic_views.compiled_sql_blob_key / semantic_topics.compiled_sql_blob_key. The materialiser (crates/app/src/server/api/semantic_scan.rs) prefers the S3 blob over the in-row JSONB. When the env var is unset, blob_key is NULL and the in-row definition is the canonical body — Postgres-only deployments work unchanged.
If you add a new entity type whose definition routinely tops tens of KB (the way semantic views do), follow the same shape: add a nullable compiled_sql_blob_key column, wire the upload in oxy-compile's writer, and extend oxy_compile::blob_store::BlobKind. Small definitions (apps, agents, procedures) stay JSONB-only — the S3 round-trip costs more than the row bloat at typical sizes.
Why this matters
The original FS read pattern was the dominant runtime cost at any meaningful workspace count. The IDE works on a singleton instance (FS-backed working copy); the customer-facing oxy-serve fleet must never read workspace files because those instances may not have them. Every new file type you skip the compile boundary on is a new failure mode under multi-instance.
What's NOT in scope
Skip this contract only when:
- The file is purely IDE-editor state (already on the singleton, fine).
- The artifact is a generated build product (charts, parquet caches, customer-app bundles) — those belong in S3, not the compile boundary.
- The read happens exactly once at server startup, not per-request (startup walks of
OXY_STATE_DIRare fine).
If you find yourself adding a glob::glob(workspace_path) or fs::read_to_string(workspace_path.join(...)) in a request handler and the path is not already a compiled entity, you are on the wrong path. Open this skill and add the file type to the compile boundary first.
Related skills + design docs
oxy-scaling-design— broader multi-instance context.oxy-task-spec-default— long-running work goes on the worker fleet, not in handlers.internal-docs/compile-boundary.md— operator runbook (flags, kill switch, read routing, code map).- The compile worker entry point:
crates/agentic/pipeline/src/compile_worker.rs. - The hybrid reader:
crates/app/src/server/api/compiled_reader.rs. - The materialiser pattern:
crates/app/src/server/api/semantic_scan.rs.