Foundation Plan
Status: sketch/0.23 is the current provisional document shape. Its strict loader, schema, examples, bounded
Project import, analysis slices, and bounded Compiler path exist. They do not yet accept, analyze, persist,
serialize, or emit the complete format. The evidence index owns the changing proof
boundary; this page owns the document's meaning.
The Foundation Plan is the complete versioned authored document for one Project. Its Head may contain schema-valid meaning the current service cannot yet admit to the relational projection. It combines:
- the application meaning First Draft can structure and ask the Compiler to generate;
- a few authored facts about the application as a whole that no structured subject can imply;
- the Project-selected stack and automatically pinned target-profile release; and
- sparse material realization choices that the profile cannot derive.
Decision 0009 (repository-only) preserves the one-input reasoning. Decision 0011 (repository-only) records the current one-artifact, external-agent direction.
Current implementation boundary
- Document: the strict loader and JSON Schema accept the current example corpus and an empty-Entity draft. Structural validity does not imply complete semantic acceptance.
- Project graph: conditional import stores a bounded slice of application, Entity, Field, relationship, behavior, and application-level subjects. Complete nonempty import, Data import, replacement, and serialization remain open.
- Analysis: target-independent and Rails-qualified passes cover named slices and record unsupported shapes as gaps. Some legacy generated-name collision passes still block instead. Whole-format semantic and target analysis remain open.
- Compilation: one sealed Project generation produces verified bounded Rails and selected iPhone/Android output. Complete Domain and Capability output and broader target support remain open.
- Application facts: the format records domain, Appearance, and sparse native and delivery intent. The bounded Compiler lowers domain into the Rails production mailer host and Appearance into the Rails and selected native shells. Every application receives bookmark icons derived from its name. Native launcher icons, delivery lowering, and broader operational proof remain open.
- Agent context: product meaning that the format cannot express stays with the user's agent. First Draft-owned Continuation prose or packaged Handoff context is deferred pending evidence of information loss.
Every structured subject requests generation, but recognition by the format is not Compiler admission. Under the deployed ignore-and-list default, a valid AnalysisRun lists every service- and target-support omission in one exact-source-bound GapSet before Compile. The supported subset compiles ordinarily and emits that same GapSet; the exact retained Plan bytes preserve the authored form and ship beside it as immutable provenance. Read the relevant living topic for meaning, the machine reference (repository-only) for exact syntax, the target profile for lowering, and the evidence index before claiming present support.
There is no separate App Schema input or identity. The semantic application definition remains visible as a layer inside the Plan and as an ERD-style Schema view. The Head owns what was submitted; the Project-scoped Active Record graph (repository-only) owns admitted semantic meaning used by Analyzer and Compiler; the valid AnalysisRun owns the target-relative verdict and GapSet. JSON is the agent import/export, fixture, and compilation-evidence format, not a second mutable graph.
The signed-in owner's read-only Project page exposes the exact current Head and the current valid AnalysisRun's complete GapSet as raw views or downloads. These resources project the existing retained bytes; they do not add a second graph, a browser mutation path, or a recovery history. The Project graph owner (repository-only) defines their lifecycle.
The shared loader applies the required RFC 7493 I-JSON boundary (repository-only) before it validates the current Plan schema. Its 256-level nesting cap is a separate resource guard.
Authoring and review
The current product experiment assumes that a user works with their existing coding agent. A First Draft Plugin, CLI, or documented API may help that agent maintain one complete local document and conditionally replace the live Project graph. First Draft validates the same meaning and projects it into an ERD, table-like data view, and other review surfaces. A future Designer may edit the normalized graph through ordinary CRUD rather than serializing the whole document after every UI action.
First Draft does not currently need to own that agent's chat, transcript, uploads, or unstructured product understanding. Agent-context transfer is deferred until use demonstrates a need.
The Schema view may show Rails consequences:
- authored realization choices are editable;
- derived profile consequences are explanatory and read-only; and
- application meaning remains phrased independently of Rails macros where that distinction helps review.
Identity and target
A Project has a stable First Draft-owned UUIDv7, selects one stack, and owns one mutable graph whose
graph_version advances for semantic graph changes. Each authored row has an internal surrogate database key. Each
independently mutable document subject also has a document-visible UUIDv7 unique within its Project. Readable keys
and names remain mutable attributes rather than database identity.
For current sketch/0.23, every nested authored object with a free-form key is such a subject and carries
subject_uuid. Enum-keyed, link-keyed, and positional children inherit continuity from their owner. The singleton
Application's key names generated artifacts rather than a nested subject, so it does not carry subject_uuid.
The current endpoint retains the latest accepted PUT bytes, format, and digest in a mutable Head. Its
strong ETag protects that representation and changes for byte-different semantic equivalents without advancing
graph_version. This Head is not a recovery snapshot. The server still plans to retain canonical immutable bytes
for semantic graph generations under a recovery policy. Current Compilation start captures immutable bytes for the
exact admitted generation. The request route names the Project; the document names the exact target-profile release.
A Compilation also records graph, analyzer, Compiler, and toolchain identity rather than relying on current mutable
Project metadata later.
Rails is the only current stack. A future cross-stack experiment may fork a Project into a new target rather than mutating the meaning of earlier Plan snapshots.
The imported document's target ID and profile must match the Project's selected pin. During pre-alpha development, work that changes profiles may start a new Project or explicitly fork an old one. Retained-Project profile migration and coexistence are deferred until observed use requires them; they are not prerequisites for profile work. Ordinary complete-document import does not select a new target or profile.
Semantic ownership
The application definition is nested by semantic ownership. An Entity owns its Fields, References, Associations, Predicates, Orderings, Validations, Trees, Policies, optional Account behavior, standard Scaffold, and any stable reference data. Development data remains application-wide because its records form one connected graph.
The historical predecessor format used fully qualified semantic IDs to refer into this hierarchy without depending on
array positions. Current sketch/0.23 separates stable Project-scoped subject identity from readable keys. Each
independently mutable subject carries a subject_uuid for continuity across submissions, while typed links inside
the document use readable scoped paths. A complete-document rename retains the UUID and updates affected paths in
the same atomic candidate.
A subject can also move to a different semantic owner without changing UUID when its kind stays the same and the complete candidate updates affected paths and satisfies the new owner's structural rules. Import applies that reparenting atomically; a replacement concept uses a new UUID.
The strict sketch/0.23 Ruby adapter pins its schema digest and builds diagnostic normalization from its Field
variant shapes. A test over the six-probe design and parity corpus injects an unknown property into all 183 Fields
across all 20 kinds and proves that each Field gets one exact public pointer. A complementary missing-requirement
mutation proves that the current profile suppresses irrelevant oneOf errors without hiding declared properties.
A Reference's readable final component names its logical local slot. The Project graph mechanically maintains the
inevitable same-key forward Association, and the Compiler conventionally lowers it to belongs_to. The authored
artifact includes only additional direct traversals on either side and indirect traversals whose names or behavior
carry product meaning. Reference and Association links remain unambiguous because typed properties resolve
separate namespaces.
Reference data contains small, stable Entity records required in every environment. Development data contains a disposable application-wide graph for exploring the generated Foundation. Both use one tagged Value algebra, subject UUIDs, owner-local keys, and readable typed record links; neither embeds arbitrary seed code.
Fields describe more than columns
A Field is an application concept, not a database-column declaration.
- A
moneyField may produce a subunit column, a Money-valued model attribute, form controls, formatting, query translation, validation, and tests. - A
rich_textField may produce Action Text records and attachment behavior. - A
counter,position, orstate_machineField includes behavior required to maintain its value. - A derived average Field may require a maintained queryable value; the selected target owns its mechanism.
- A
jsonField describes one intentionally opaque structured value rather than exposing its keys as Plan subjects.
An optional notes string can explain what one Field represents. It is review prose, not an instruction from
which the Compiler invents validations, callbacks, or other behavior.
The Rails profile currently has one intended lowering for these meanings. The Plan does not repeat gem names when the user has no supported choice to make.
A derived Field needs at least one structured consumer, such as a Scaffold display, Predicate, Ordering, or
Association. The Compiler derives the required implementation from those uses. Oscar Party's collection display
and Ordering make movie.average_rating queryable without adding a materialization switch to the Plan.
Application-level facts
Four keys sit beside the Entities and describe the application as a whole:
| Key | Meaning | Presence |
|---|---|---|
domain |
The owner's hostname, source of platform identifiers, linked domains, and a sending address | Optional |
appearance |
Native tint_color/background_color and cross-target theme |
Optional |
native |
Sparse map of native clients to generate | Always present, possibly {} |
delivery |
Sparse map of notification transports to wire | Always present, possibly {} |
These are not application meaning, and they are not realization choices either. No Entity, Field, Reference,
Scaffold, or Policy can imply a brand color or a decision to build an Android client, so nothing exists for the
Compiler to derive them from. Each also carries a material consequence: delivery obliges the owner to obtain an
APNs key, a Firebase project, a VAPID pair, or a mail provider, and domain decides whether the emitted
identifiers can reach a store at all.
native and delivery are sparse enabled maps: a present empty object for a platform or channel requests its one
profile-pinned behavior, while an omitted member declines it. The required outer objects make the complete
decision visible without repeated booleans. "Generated by default" describes what the authoring surface prefills,
not what the format assumes. domain and appearance are genuinely optional. When present, domain replaces the
Core example.com host in production config.action_mailer.default_url_options with the exact authored hostname.
This Rails result does not configure deployment or DNS, choose a URL protocol, or add any native-client behavior.
Native identity and origin use separate platform lowerings. When domain is omitted, both selected native
clients use visibly non-production placeholder identities. A future prerequisite or coverage record
should disclose that limitation; the current Compilation Record does not.
Each Appearance color is one six-digit hexadecimal sRGB value or a { light, dark } pair. A scalar applies to both
modes. Omitted theme means light, including when appearance is absent. Dark support is an explicit choice:
auto follows the operating-system light/dark preference; dark fixes dark mode. Fixed light and dark ignore the
OS preference and any old saved browser override. auto follows only the OS. These three choices offer no user
control or browser preference storage. Omitted theme also selects light in iOS and Android native configuration.
toggle offers a Light / Dark / System selector in the browser. It starts in System, remembers the selected
preference in that browser, and follows OS changes only while System is selected. The authored choice requests the
selector; a person's browser preference does not change the Plan or synchronize with an Account. Selected native
clients retain automatic shell and embedded-content appearance without a selector or stored browser preference.
Their missing user selection is a reviewed partial-generation gap; the native target
owner describes that boundary.
Tint and background colors brand native-client surfaces. Web components, embedded web content, browser and manifest theme metadata, and static errors use the Rails target's neutral Zinc theme. The selected iPhone shell carries paired tint/background assets; iOS Core applies them to native launch and runtime surfaces. Android receives paired Material color resources and the same explicit theme choice. The native colors remain authored even when the cross-target mode is fixed. They do not derive or override web component colors. Web bookmark icons use the fixed black-on-white artwork described below, independently of Appearance.
Much derives from these rather than being authored beside them. Launch surfaces, navigation-bar branding, path
configuration, per-channel notification preferences, and platform identifiers are consequences. Icons are also a
derived consequence. When no native client is emitted, the Web assets leave no Appearance icon-assets gap.
When either native client is emitted, one foundation_plan.gap.appearance.icon_assets.not_generated entry reports
partially_generated because the emitted native launcher icons remain stock Core assets. The entry is addressed to
/application/appearance; it does not discount the generated shell colors, theme, or Web icons.
Decision 0016 (repository-only) records the test that admits a fact here
and requires each to name what would convert it back into a derivation.
Bookmark icons
Every application receives favicon and home-screen bookmark artwork derived from its name. The first character after trimming surrounding whitespace selects an outlined A–Z or 0–9 monogram; lowercase ASCII selects uppercase artwork. Other initials select a neutral circle. The application name itself stays unchanged. Artwork is black on an opaque white background, with padding for browser and operating-system masks.
The Compiler copies one finished SVG and its 192px and 512px PNG variants. The catalog, font, and artwork tools stay upstream. Owners can replace the ordinary public files directly. Existing application-name, touch-icon, and manifest metadata remain part of this modest baseline; the Rails profile owns their concrete paths and defaults.
Plan sketch/0.23 removes application.pwa; the strict schema rejects that former key. There is no replacement
authoring choice. Broader PWA product scope and installed-app qualification are deferred until a concrete user need
establishes what Compilation should add. Custom icon fonts and colors are deferred separately; Appearance still
controls its existing page/theme and native-shell behavior.
Home page
An optional application.home_index selects the existing Web index owned by one Entity:
"home_index": "movie"
The link uses the Entity's current local key. That Entity already selects index in scaffold.resource_routes and
defines scaffold.index. There is no separate Home subject or route expression. Import resolves the link to Entity
identity; renaming the Entity retains that identity and updates the readable link on export.
Omitting the choice keeps a simple welcome Home. Its chosen copy is “Your foundation is ready to build on.” Apps with no Web indexes also use this default. Entity order and navigation order never choose Home. A selected index keeps its ordinary resource URL, query, authorization, and presentation; the choice gives it the Web root as well. Unused welcome controller, view, and copy are omitted when the selected index is generated.
A missing Entity or an Entity with no authored index is invalid meaning. If a valid selected index is omitted for a service or target support limit, its dependent Home choice is disclosed in the reviewed GapSet and the default welcome remains as the residual root. The Compiler never substitutes another index. This choice does not reorder native navigation or introduce arbitrary routes, a homepage-description field, or legal-page options.
The consumed Core supplies the welcome copy in config/locales/home.en.yml. A generated Home index omits that
locale along with the unused welcome controller and view. The
routes decision (repository-only) links the route checks and
paired Core integration evidence; this source support does not claim a published release.
Sparse target choices
A realization property is useful only when several supported target implementations preserve the same product
meaning but differ materially. The current examples have one such choice: a closed multi-target Reference can
lower to an exclusive arc or a Rails polymorphic association.
The Reference owns that choice because its consequences are local to the relationship. Authentication, policies, attachments, rich text, state machines, counters, positions, and money do not carry realization objects while the Rails profile supports only one lowering for each.
Derived consequences
A Capability or prerequisite that any Plan subject implies is derived from structured meaning, realization choices, and the selected target profile. It is never repeated as an authored list.
The authoring surface may display those consequences before approval. The Compiler recomputes and validates them. There is no parallel authored list that can drift from the subjects that activated it.
Some consequences have no activating subject. Native output, push and transactional email machinery, a brand color, and an application icon set cannot be implied by any Field, Reference, Scaffold, Policy, or Account. Forbidding an authored fact there prevents no drift, because nothing exists to drift from; it only makes the fact inexpressible. Those facts are authored at application level when they also carry a material consequence, such as an external credential the owner must personally obtain.
Each authored application-level fact carries a revisit trigger. When meaning arrives that could derive it, it converts to a derived consequence and the authored key is removed. Delivery machinery converts once the causal seam (repository-only) closes and notification rules can name their own channels. Decision 0016 (repository-only) records the test and its boundary.
Generation boundary
Every structured definition in an approved Plan requests generation. The initial format does not let a user mark
one otherwise supported Field, Reference, Association, or Scaffold surface as unique; doing so could leave
dependent validations, Predicates, transitions, routes, views, or tests incoherent.
A subject may still be recognized by the format but unsupported by the current service or one target-profile and
Compiler release. Analysis records the complete distinction in firstdraft.foundation-gaps/2: a
service_support_gap was skipped before semantic analysis, while a target_support_gap names admitted and
analyzed meaning not fully realized by the selected target. The generated application contains the exact reviewed
record at .firstdraft/gaps.json; clients may render it for people without creating another persisted authority.
Application-specific work that the current vocabulary cannot generate remains with the user's agent. It does not enter the Foundation Plan as Continuation prose. A future context or Handoff feature should be based on observed loss of information rather than assumed need.
Presence and empty values
The serialized format distinguishes absence deliberately:
- ordinary empty collections are omitted, and a present collection must contribute at least one item;
- required
application.entitiesis the authoring-draft exception: it may be[]before the agent has defined the first Entity, and authoring analysis should warn rather than force a placeholder Entity; settings.within: []is the deliberate exception, where an empty position scope means one global list;- required feature maps such as
nativeanddeliverymay be{}, while optional feature objects such as Accountlockoutuse presence to enable their one profile-pinned behavior; - optional singleton and variant-specific objects are omitted when absent; and
nullis allowed only when it has a documented meaning distinct from omission.
Optional scalar settings have documented defaults. Omission and an explicit default value mean the same thing,
although examples normally omit default-valued settings. Boolean names state the natural affirmative fact rather
than being inverted to force a false default. The
Import and serialization design (repository-only) records the format-wide rule
and its sources.
An authored Field default is different from a format setting's default. It is an optional tagged Value that asks
for an initial application value. The bounded importer stores its decoded JSON meaning for its ten scalar kinds and
enum; the Head separately retains exact submitted bytes. Omission stores SQL NULL, while
{"kind":"literal","value":null} stores an authored literal null.
Import does not prove literal compatibility, enum membership, readable-link resolution, Field nullability or
normalization behavior, or target lowering; those remain semantic analysis and Compiler work.
The format should not require placeholder realization objects or nullable feature slots merely to make every
object look alike.
Count-valued integers, currently Association cardinality and length-Validation bounds, are nonnegative and no
larger than 9,007,199,254,740,991. That is the largest integer common JSON implementations agree on exactly
under RFC 8259. A target profile may reject a much smaller count it cannot enforce, but parsing
and transport must not silently change the approved Plan's value.
Open questions
- Which remaining structured subject should become the next coherent import-analysis-emission vertical?
- Which focused Designer mutations are useful after the agent-first whole-document journey works?
- Which Rails consequences materially help review without turning the Schema view into framework noise?
- Which deterministic serialization rules make UI CRUD and agent-authored documents round-trip cleanly?
- What evidence would justify First Draft-owned agent context or a Handoff bundle?