The publish checklist

This page lists everything an app must pass before Brydio publishes it (A8-F04-S03). It covers Brydio's own apps and everyone else's, with no exceptions.

brydio validate runs these checks on your own machine. brydio publish runs brydio build and brydio validate before it uploads anything. Then Brydio's publish route (POST /api/v1/apps/publish) checks the upload again, because an older or edited command line is still just a client. Every check reports a code and one sentence, one item per failure. Warnings don't stop a build or a publish; errors do.

The columns:

packages/cli/test/checklist-doc.test.ts fails if a code exists in the SDK and not on this page, or the other way round.

1. The manifest parses, and every placement names a screen

The manifest is .brydio/app.json (or app.json), read with Brydio's own schema (@brydio/manifest). validate and the publish route refuse the same way: the check's own code (manifest_invalid for a field of the wrong shape) and the sentence behind app.json: "<path>": , or behind app.json: when the problem isn't about one field. The sentences below are what follows that prefix. validate lists every problem; the route answers with the first.

CodeRefused withvalidateserverHow to fix
manifest_missingThere is no manifest. Put one at .brydio/app.json.yespublish, as bundle_manifest_missingAdd .brydio/app.json.
manifest_not_jsonapp.json is not valid JSON. (validate adds where the parser stopped.)yespublish, as manifest_invalidFix the JSON.
manifest_invalidThe schema's sentence for the field at <path>. For example, the name must be lowercase letters, digits and dashes; the version must look like 1.2.0; "Custom tools are not available yet."yespublishChange the field the path names.
placement_screen_unknownA <kind> placement opens "<screen>", which is not one of the app's screens.yespublishDeclare the screen under screens, or point the placement at one that is declared.
data_too_many_collectionsAn app may keep at most <n> collections.yespublishKeep fewer collections.
data_collection_name_format<collection> is not a collection name: lower-case letters, digits and _, starting with a letter, at most 40 characters.yespublishRename the collection.
data_label_format<collection>'s label must be one lower-case word the tools can be named with, at most 40 characters.yespublishGive label one lower-case word.
data_label_taken<collection> and <other> would both make tools called <label>.yespublishGive one of them a different label.
data_too_many_fieldsA collection may have at most <n> fields.yespublishSplit the collection or drop fields.
data_field_reserved<collection>.<field>: Brydio keeps <field> on every record itself.yespublishRename the field; Brydio already keeps it.
data_field_name_format<collection>.<field> is not a field name: letters, digits and _, starting with a lower-case letter, at most 40 characters.yespublishRename the field.
data_project_field_twice<collection> links to a project twice (<a> and <b>); keep one.yespublishKeep one project field.
data_field_type_unknown<collection>.<field>: "<type>" is not a field type.yespublishUse one of the field types in the README.
data_enum_empty<collection>.<field>: A choice needs at least one allowed value.yespublishList at least one value.
data_enum_too_many<collection>.<field>: A choice may have at most <n> values.yespublishAllow fewer values.
data_enum_value_invalid<collection>.<field>: Each allowed value is a word or two of text, at most <n> characters.yespublishShorten or fix the value.
data_enum_duplicate<collection>.<field>: A choice names the same value twice.yespublishTake out the duplicate.
data_default_not_allowed<collection>.<field>: Only a choice or a boolean may have a default, not a <type>.yespublishTake the default out, or make the field a choice or a boolean.
data_default_on_required<collection>.<field>: A field with a default is one a create may leave out: add "optional": true (or write "boolean?").yespublishMake the field optional, or take the default out.
data_default_invalid<collection>.<field>: The default must be one of the choice's values (or true or false for a boolean).yespublishUse one of the allowed values.
data_labels_not_allowed<collection>.<field>: Only a choice may label its values, not a <type>.yespublishTake labels out, or make the field a choice.
data_label_invalid<collection>.<field>: The label for "<value>" is 1 to <n> characters of text.yespublishGive each label 1 to <n> characters.
data_label_unknown_value<collection>.<field>: "<value>" is not one of the choice's values, so it can't have a label.yespublishLabel only values the choice allows.
data_field_key_unknown<collection>.<field>: "<key>" is not something a field may say; use type, optional, default and labels.yespublishKeep to type, optional, default and labels.
custom_name_takentools.custom: <name> is already a tool this app has: give the custom tool another name, or switch generated tools off.yespublishRename the custom tool.
custom_collection_unknowntools.custom: <name> works on <collection>, which the app does not keep.yespublishName a collection in data, or leave collection out.
custom_input_invalidtools.custom: <name>'s input <field>: not a field type.yespublishWrite the input in the field-type grammar.
data_search_unknown_field<collection> searches <field>, which is not one of its fields.yespublishSearch only declared fields.
data_search_not_text<collection>.<field> cannot be searched: only text fields can.yespublishSearch only text fields.
manifest_sdk_overwritten (warning, from brydio build)The manifest says "sdk": <value>, which brydio build writes itself. The build says <version>; take the line out of the manifest.buildnoDelete sdk from the manifest.

2. Screens use only catalogue elements and the settings each one takes

Checked by reading each screen's source with TypeScript's parser, against @brydio/ui's catalogue, which is a copy of Brydio's own. Only what the source writes out is checked: a value or an element name worked out while the screen runs isn't guessed at. Brydio refuses anything that gets past this check, node by node, while drawing (tree/refused), using the same sentences. The publish route can't check compiled code for this.

CodeRefused withvalidateserverHow to fix
element_unknownBrydio has no element called "<name>". A screen draws with <the catalogue>.yesruntimeUse a catalogue element.
prop_unknown<element> has no setting called "<name>".yesruntimeRemove the setting, or use one the element takes (see the README's table).
prop_value_invalid<element> <setting> must be one of <values>. (Or: must be text of at most <n> characters; must be true or false; must be a whole number from <min> to <max>; a list or a record names the path, as in bry-table columns[0].align must be one of start, end.)yesruntimeUse an allowed value.
prop_required<element> needs a <setting>.yesruntimeAdd the setting.
event_unknown<element> raises <events>, not "<event>". Or: <element> raises no events, so it takes no <onEvent>.yesruntimeRemove the handler.
children_not_allowed<element> can’t hold other nodes.yesruntimeGive the element its words as a setting (text="…", label="…").
source_syntaxThis does not parse: <TypeScript's words>yesnoFix the syntax.
screen_source_missing (from brydio build)The "<screen>" screen is built from src/<entry>.tsx (or .ts, .jsx, .js), and there is no such file.buildnoAdd the source file, or fix the entry.
screen_build_failed (from brydio build)The "<screen>" screen did not build: <the bundler's words>buildnoFix what the bundler names.
handler_source_missing (from brydio build)The "<tool>" tool is built from src/<handler>.ts (or .js), and there is no such file.buildnoAdd the source file, or fix the handler's path.
handler_build_failed (from brydio build)The "<tool>" tool did not build: <the bundler's words>buildnoFix what the bundler names.

3. No element carries a style, a class or raw HTML

CodeRefused withvalidateserverHow to fix
style_forbiddenThe element's own refusal, followed by the reason. For style: Brydio draws every element in its own style; there is no style setting. For class and className: There are no classes in a Brydio app; choose a setting the element offers. For color and colour: Colours come from Brydio’s tokens through a setting like tone, never a value. For dangerouslySetInnerHTML and innerHTML: A Brydio app has no HTML to set.yesruntime, as the element's refusal without the reasonUse the element's own settings (tone, variant, gap).

A screen also runs in a worker with no page, no network, no storage and no other workers, and it may import only @brydio/*, Preact and its own files:

CodeRefused withvalidateserverHow to fix
dom_global<name>: A screen has no page: it runs in a worker and draws only with the catalogue.yesruntime (the worker has none)Draw with elements.
network_global<name>: A screen has no network; call the app’s tools instead.yesruntimeCall the app's tools.
storage_global<name>: A screen has no storage; keep records in the app’s collections.yesruntimeKeep records in a collection.
worker_global<name>: A screen is one module in one worker; there is nothing else to start or load.yesruntimeBuild everything into the screen.
eval_forbiddeneval is refused in a Brydio app’s worker; write the code out.yesruntimeWrite the code out.
import_not_allowedimport "<specifier>": a screen imports only its own files, @brydio packages and Preact. Anything else it needs belongs in the SDK.yesnoImport only @brydio/*, preact, preact/hooks, preact/jsx-runtime and your own files.

4. Every collection has a schema, and a version that changes one declares its migration

Every collection must have a schema; the manifest checks in section 1 refuse one without. A version whose collections differ from the version published before it must list migrations steps for its own version number (add, rename, drop, dropCollection, replace), in Brydio's A3-F07 shape.

validate compares against the previous version when it can see it:

When neither is there, only the publish route runs this check. It compares against the highest version already published below this one.

CodeRefused withvalidateserverHow to fix
migration_missing<why> (<version> against <previous>, the version before it.) <why> is one of the sentences in the next table. validate lists every one; the route stops at the first.with a previous version: --previous <app.json>, an older dist/, or, in brydio publish, the version Brydio last published (GET /api/v1/apps/publish/:appKey/latest), checked before uploadingpublishAdd the step to migrations under this version's number.
previous_unreadableThere is no manifest at <path> to compare this version with. Or: <path> is not valid JSON, so this version can't be compared with it.yesnoPoint --previous at the published version's app.json.
previous_not_older<path> is version <v>, not one before <version>, so it can't say what this version changes.yesnoPoint --previous at an earlier version.

The reasons behind migration_missing, from @brydio/manifest's copy of Brydio's migrations.ts:

CodeRefused withvalidateserverHow to fix
migration_unexplained<collection>.<field> is new; add it with a step (and a default, when the field is required). Or: <collection>.<field> is gone; drop it or rename it with a step. Or: <collection> is no longer declared; say dropCollection to remove its records.with a previous versionpublish{ "op": "add" }, "rename", "drop" or "dropCollection".
migration_type_changed<collection>.<field> changed type; drop ` <field> and add it again under a new name. Or: <collection>.<field>` became required, and records without it would not be readable; add a new field with a default instead.with a previous versionpublishDrop the field and add a new one.
migration_value_removed<collection>.<field> no longer allows "<value>"; say which value replaces each.with a previous versionpublish{ "op": "replace", "from": …, "to": … }.
migration_default_missingadd <collection>.<field>: a required field needs a default for the records already kept.with a previous versionpublishGive the add step a default.
migration_default_invalidadd <collection>.<field>: the default is not valid; <why>with a previous versionpublishFix the default.
migration_step_invalid<op> <collection>.<field>: there is no such field (or collection, or value).with a previous versionpublishFix the step so it names what the previous version had.
migration_step_pointless<op> <collection>.<field>: the new version still declares it (or does not declare it).with a previous versionpublishRemove the step.

5. Every tool the manifest exposes is generated or has a handler

An app's tools are the ones Brydio generates for its collections (create_, update_, get_, list_, search_, delete_). tools.custom must be empty until handler scripts arrive (Phase 3), so a declared tool with no handler is manifest_invalid ("Custom tools are not available yet."). Screens may call only tools that exist:

CodeRefused withvalidateserverHow to fix
tool_unknown"<tool>" is not one of this app's tools. Its tools are <list>.yesruntimeCall a generated tool, or declare the collection that generates it.
collection_unknown"<collection>" is not one of this app's collections. It keeps <list>.yesruntimeRead a declared collection.

6. The bundle is under the cap, and holds only scripts and the manifest

CodeRefused withvalidateserverHow to fix
bundle_not_builtThere is no dist/ yet. Run brydio build first.yesnoRun brydio build.
screen_not_builtThe "<screen>" screen names "<entry>", which is not a script in this bundle. Run brydio build.yespublishRun brydio build, or fix the entry.
handler_not_builtThe "<tool>" tool runs "<handler>", which is not a script in this bundle. Run brydio build.yespublish (handler_missing)Run brydio build, or fix the handler's path.
bundle_stale (warning)The built manifest is not the manifest as it is now. Run brydio build again.yesnoRun brydio build.
bundle_too_largeThat bundle is <size>, over the 1.00 MB cap. The largest file is "<file>" at <size>.yespublishMake the screens smaller; start with the largest file.
bundle_file_not_code"<file>" is not a script. A bundle holds only .js files and app.json. (From brydio build: The "<screen>" screen brings in "<file>", which a bundle cannot hold. A Brydio app has no CSS, HTML or images: Brydio draws every element itself.)yespublishRemove the import of CSS, HTML or images.
runtime_too_largeThe "<screen>" screen carries <n> KB of Brydio's runtime, over the 30 KB a screen may carry. Something in @brydio is being bundled that this screen does not use.no: brydio build (minified) refuses it, and validate reads a buildnoImport what the screen uses from @brydio/app, not a schema or checking library; say so on the channel if the SDK itself leaks.
bundle_path_invalid"<path>" is not a path a bundle can hold.yespublishUse plain ASCII names with no hidden segments.
bundle_manifest_missingThat bundle has no app.json at its root.yespublishRun brydio build.
bundle_emptyThat bundle has no screens in it, only a manifest.yespublishDeclare and build a screen.
archive_unreadable, archive_too_large, archive_uncompressed_too_large, archive_too_many_entries, archive_member_path_absolute, archive_member_path_has_parent_segment, archive_member_path_has_backslash, archive_member_path_invalid, archive_member_type_unsupported, skill_directory_hiddenThe archive reader's sentence, at bundle scale ("That upload is larger than 2 MB. A bundle is at most 1 MB of code.").no: brydio publish writes the zip itselfpublishPublish with brydio publish rather than a zip made by hand.

7. No secret-looking string in the bundle

CodeRefused withvalidateserverHow to fix
secret_in_bundleThat package contains a secret. Header values and client secrets are entered here, never shipped in a file — remove it and import again.yespublishRemove the value, and treat it as leaked.

This is Brydio's existing scan (extensions/apps/secret-scan.ts), copied exactly. It looks at the value keys (value, secret, token, apiKey, …) anywhere in the bundle's app.json, and in servers.json and integrations/*.json, and skips placeholders like ${user_config.key} or <your key>. app.json is served to every screen that opens the app, so it holds nothing private. Scripts aren't scanned, on purpose: a pattern scan of minified code misses real keys and refuses innocent strings.

8. The grants list everything the screens call, and nothing they don't

A tool counts as granted when its name, *, or its collection's name is in grants.tools, as long as its collection is also granted. That is how Brydio reads grants (apps/api/src/apps/manifest/grants.ts). validate checks the calls a screen writes out: tools.call('…'), tools.result('…'), data.get('…'), data.list('…'), useList('…'), the bridge's callTool, getDocument and listDocuments, and navigate(…). Asking for too little is an error. Asking for too much is a warning: a tool grant may be meant for the assistant rather than a screen.

CodeRefused withvalidateserverHow to fix
grant_collection_missingapp.json: "grants.collections": <collection> is kept but not asked for: add it to grants.collections.yespublishAdd the collection, or *, to grants.collections.
grant_tool_missing"<tool>" is called here but not asked for: add it, or its collection <collection>, to grants.tools.yesruntimeAdd the tool or its collection to grants.tools.
grant_host_missingnavigate is called here but not asked for: add "navigate" to grants.host.yesruntimeAdd navigate to grants.host.
grant_unknownapp.json asks for "<grant>", which Brydio does not grant. An app may ask for navigate, message, members, projects or connection:<name>.yespublishAsk only for navigate, message, members, projects or connection:<name>.
grant_tool_unknown (warning)grants.tools asks for "<name>", which is neither one of this app's tools nor one of its collections.yesnoRemove the name, or fix its spelling.
grant_collection_unknown (warning)grants.collections asks for "<name>", which this app does not keep.yesnoRemove the name.
grant_host_unused (warning)grants.host asks for "<grant>", which no screen uses. Ask only for what the app does.yesnoRemove the grant, or use it.

Only at publish

The publish route runs these; the command line can't, or checks them in its own step.

CodeRefused withvalidateserverHow to fix
sdk_unsupportedThis app was built with SDK <v>. Brydio runs apps built with SDK <oldest> or newer, before <before>. (Or: app.json does not say which SDK built it. Build it with brydio build. …)no: brydio publish checks it before uploading, against GET /api/v1/apps/sdkpublishBuild with an SDK in the range.
screenshot_failed<screen>, <narrow or wide>, <light or dark>: the screen stopped, had a node refused, threw, or drew nothing, when pictured in the fake host with .brydio/samples.json. (Or: pictures need @brydio/fake-host in the app's devDependencies.)no: brydio publish pictures every screen before uploadingpublish, as screenshot_invalid for a picture the server refusesFix the screen so it draws with the sample records, or fix .brydio/samples.json.
version_exists<app> <version> was already published on <date> <how it differs>. A version is never replaced; publish this as a new version number.nopublish (409)Raise the version number.
not_publisher<app> is published by other accounts. Ask one of them to add you as a publisher.nopublish (403)Ask a publisher to add you.
manifest_unreadableapp.json is not valid JSON. (Or: app.json is not a JSON object.)no: manifest_not_json covers itpublishFix the JSON.