BuildWithHQ Lego kit: choosing and arranging native features

Current boundary
These are repository source examples. They are not a new published Developer Kit,
an installed tenant template, or a deployed runtime. The release hold remains in
effect. Keep the published kit's version separate from the current source API.

Start with an existing canonical page
In a source-built Developer Kit, the reviewed examples and companion layout guides
are under examples/native-lego/. In the repository they are alongside this guide.
Read examples/native-lego/README.txt for registry prerequisites. In particular,
Account Administration uses the native tenant registry; these examples do not
register it in the portable kit schema or bypass that schema validation.
Use placeable-contacts.json, placeable-files.json or placeable-conversations.json
in this directory. Other examples cover Calendar, Chat Rooms, Company News,
Work Orders, Knowledge, Search, Mail, Notifications, Favorites, Record Graph and
Record Activity. Each example is a starting layout, not proof that every nested
control in that module is independently placeable.

The outer feature block has its module key and props.part = "workspace".
Place children of that same module key below it with props.part naming a catalog
entry. Use core.container blocks to organize them into rows, grids and sections.
Every block needs its own unique _id. Use the current modulePartCatalog in
apps/tenant-runtime/src/modulePartCatalog.ts as the exact available-part list.
Do not copy a part name from a different module or place the same field twice.

The workspace owns the selected record, reads, permissions, drafts and actions.
Moving a part changes where it renders. It does not create a second business
state owner. The nearest matching feature workspace supplies its parts, even
through a different feature's workspace. A nested workspace of the same feature
starts a separate scope for ModulePart controls. Contacts and Files reset their custom-row
context before the follower gate, including when no parent is selected.

Keep the normal style layers when building a preview
The tenant entry point imports @buildwithhq/ui/professional.css,
apps/tenant-runtime/src/tailadmin-foundation.css and its styles.css. The first
layer supplies native Core container grids, spacing and disclosure behavior.
Your own React design can supply its own presentation, but a preview using the
native components should include the same style layers as the tenant app.

Place native record panels
Current source supports action-panel and panel-section in these17 workspaces:
Contacts, Files, Work Orders, Calendar, Web Links, Company News, Chat Rooms,
Checklists, Knowledge Articles, Record Graph, Conversations, Record Types,
Reservations, Dynamic Records, Discussion Boards, Mail Reader and Active Workplace.
Set the panel's variant to side-panel, drawer, modal, full-page or inline.
Set panelLayout to sections, tabs or accordion, and optionally set panelTitle.
A panel-section child uses sectionTitle. Keep all blocks' _id values unique.

An empty action-panel supplies the native detail/editor. Use one empty default
panel per workspace; multiple empty defaults show a configuration error instead
of mounting competing native controls. Put empty default panels in the workspace
layout, outside repeated rows and other native parts. Independent nested
workspaces may each have their own default panel. An explicitly
placed detail may show guidance when the default panel owns that content.
For a custom panel, put the owning module's existing parts inside panel-section
children, with core containers for your layout. A panel does not create a new
record, a second draft or another form owner. Do not repeat the same writer.
See record-action-panels.page.json for the canonical shape and use the module's
current catalog for valid parts; different modules expose different actions.

Closing and reopening preserves the native draft. An uncertain write remains
locked until its native recovery completes; closing a panel never retries it.
Keep native notices, validation and confirmation available in the chosen layout.
Historical Workplace playback remains read-only, and Mail archive confirmation
retains its audit warning. Presentation controls do not grant write authority.

These adapters are source features, not an activated runtime or published kit.
Connected Work, Knowledge Finder, Account Administration, Favorites, Global
Search, Notifications and Record Activity still use their existing placeable
parts without this record-panel adapter. Administration exposes section blocks plus
independent header and overview values; its internal editing controls still need
finer placement. Read projections link to their
native record lifecycle; they do not duplicate destructive business actions.

Use expandable sections
Set core.container props.disclosure-label to a useful label and
props.disclosure-state to "open" or "closed". Omit the state for an ordinary
container. Closing the section keeps its children mounted, so drafts remain.
Native invalid controls open their containing disclosure so the browser can
focus them. Puck preserves the settings through the canonical page round trip
and keeps the editing slot available to the builder.

Place native fields and actions together or apart
Contact form-* parts, File form-* parts and Conversation edit-* parts keep their
native HTML form association even when placed in separate containers. Keep the
grouped form/detail/list fallback when some controls are not placed; it supplies
the remaining fields. The workspace preserves required-field validation,
permission checks and the current draft/revision. Do not wrap these native
controls in another form or replace their save handler with a second write.

Contacts: contact-builder-layout.txt describes form, detail, navigation and row
parts. Files: file-builder-layout.txt describes metadata editing, navigation and
rows. Conversations: conversation-builder-layout.txt describes detail/editor
parts and the remaining grouped create/navigation/thread surfaces.

Customize repeated rows
For Contacts and Files, children placed inside the list part become the layout
repeated for each secured result. Put row-* parts inside that layout. Their open
and favorite actions use the returned row's identity. A block-supplied recordId
does not replace it. A list without children keeps the native list presentation.

Share a selection across features
Give the source workspace an explicit contextKey when several selections would
otherwise use the same default key. A feature that supports recordFrom can
follow that key. Follow only the settings supported by that feature; a generic
context setting is not a promise that every module supports every relationship.
Current followers: Calendar, Checklists, Contacts, Files, Reservations and Work Orders.
Selection-only publishers: Chat Rooms, Company News, Conversations,
Discussion Boards, Dynamic Records, Favorites, Knowledge Articles and Web Links.
These selection-only features can drive a supported follower, but cannot filter
their own lists by recordFrom. The designer no longer adds unsupported following
settings. A persisted unsupported recordFrom on a workspace shows a useful error
and does not mount its unfiltered module or load child bindings/favorites.
Correct that saved page configuration before using the feature independently;
unknown saved properties are preserved through the Puck round trip.
Ambiguous publishers must be resolved, not treated as a first-record choice.
Showing Calendar beside Contacts does not itself create a persisted relation.

Create an event from a selected Contact or other supported source
Read related-calendar-builder.txt and RelatedCalendarComposer.tsx. The example
uses createRuntimeRelatedCalendarDraft from @buildwithhq/ui with an authenticated
transport, an explicit secured source selection and a session epoch. The event
gets its own explicit visibility/location selection. Creating the event and
linking it to the source are separate user actions, with separate permissions.
An uncertain create or link does not authorize another write. Keep the draft
instance and receipt, then use the read-only recovery operation. Changing the
selected Contact must never redirect an in-flight operation to another record.

Use individually placed custom fields in a custom React design
@buildwithhq/ui exports useRuntimeCustomFields and RuntimeCustomFieldControl.
The hook supplies the shared values/update/collect behavior; the control renders
one definition and supports an explicit formId. Builders may use their own markup
with the same metadata and value contract. Keep the hook mounted per draft and
change draftVersion for a new record/session even if its metadata is identical.
Preserve exact untouched numeric/date-time values and explicit Boolean false.
Visible-field metadata is not field-management access or protected-field access.

Current source API parity
The delegated visible-field read is
GET /v1/apps/{saasAppId}/runtime/fields/{moduleKey}, with runtime.bindings.read.
The typed source client is BuildWithHQRuntimeClient.getVisibleCustomFields.
The native read remains /api/runtime/fields/{moduleKey}.

Page removal is reversible availability, with explicit state and current version:
PUT /api/runtime/design/pages/{pageKey}/active and
PUT /v1/apps/{saasAppId}/design/pages/{pageKey}/active.
The public design operation requires the live builder credential and pages.publish;
BuildWithHQDesignClient.setPageActive validates the successor receipt and does not
blindly retry writes. An active homepage must be reassigned before deactivation.
Existing content/history remains available for restore. Source contracts are
updated; immutable published Support/Kit versions stay unchanged under the hold.

Keep record authority explicit
Layout, menus, UserTypes and context keys never grant record/action authority.
Reads and writes still use the verified actor and routed app, persisted page
identity and secured Personal/DataRole/location contract. A new top-level record
needs the user's scope selection. An absent location choice is not All. Child
records use their reviewed parent-scope contract. Do not infer a role, location,
record identity or write permission from the layout or metadata.

Assess progress honestly
The Files empty-follower custom-list defect recorded at the previous window's
close is fixed in current source. Empty/cleared/ambiguous followers preserve the
local prompt and cannot inherit outer rows/actions, including inside a row.
Focused Puck/renderer and desktop/mobile local checks cover that boundary; routed
tenant and release acceptance remain held. See td55-file-follower-isolation-2026-11-01.txt.
The same isolation is verified for Contact followers in
td55-contact-follower-isolation-2026-11-01.txt.

tools/check_lego_kit_ready.py is a static review aid. Registered parts can still
contain whole forms or panels; a static pass does not prove every element is
placeable, live API parity, or complete lifecycle/permission acceptance. Use the
current modulePartCatalog for exact part names and counts. Grouped components
still require an individual-control review before claiming every element is
placeable; the existence of a panel or form does not close that review.

Historical incremental notes
The following counts describe earlier slices, not the current catalog totals.
Later entries supersede earlier entries for the same module. Current source
and each module's builder-layout guide take precedence over these snapshots.

Mixed-module starting point: contact-reservations.json demonstrates selecting a Contact and booking through the native Reservations workspace. placeable-reservations.json demonstrates individual forms and row data. Core stack containers keep contents at the start of equal-height columns; empty module cells collapse. Valid empty followers keep forms inactive until a record is selected.

Custom Records now exposes23 parts, including module choice, search, creation, refresh and paging controls. See dynamic-records-builder-layout.txt. Its record-detail, row and dynamic-field internals remain follow-ups.

Custom Records now exposes38 parts including15 selected-record detail/action pieces. See dynamic-records-builder-layout.txt. Placement does not grant record authority.

Custom Records now exposes46 parts, including8 repeated row pieces. A list with children repeats that layout per secured record; row-field uses explicit fieldKey. See dynamic-records-builder-layout.txt.

Custom Records now exposes54 parts including8 create/edit form pieces. form-field selects an explicit schema fieldKey; form-fields renders the remaining fields. Custom form children and external controls keep one native form owner and current write recovery.

Custom Records now exposes55 parts. record-field selects one secured value by explicit fieldKey and removes its duplicate from grouped details/custom-fields. It stays on the workspace selected record and supports a compact value beside expandable native controls.

Discussion Boards exposes21 parts, including individual search, creation and paging controls. Grouped header/filters/pager omit separately placed controls; native form ownership and submitted-filter paging remain intact.

Discussion Boards now exposes37 parts. Board data/actions may be placed independently or inside expandable controls; they retain board identity while a topic is selected. Grouped details omit independently placed children and retain pending-write protection.

Discussion Boards now exposes53 parts. Selected-topic data, actions and native edit form may occupy custom containers/disclosures. Board and topic actions keep their respective secured identities; post history retains its native cursor and edit fences.

Discussion Boards exposes63 parts with ten per-board row parts. Custom list children repeat the secured response; row actions retain exact identity and pending-write admission.

Discussion Boards exposes72 parts, including separate board form controls. External controls use the native form owner, explicit fieldKey and existing scope choice; native pending/unknown write protections remain.

Discussion Boards exposes93 parts. Topic/reply form controls and history navigation can occupy separate custom containers while sharing native form owners, exact parent identities and write recovery.

Discussion Boards exposes103 parts, including ten independent topic-row parts for custom cards.

Discussion Boards exposes113 parts, including ten independent post-row parts for custom feeds and editors.

Checklists exposes23 parts including independent navigation/filter/paging controls; see checklists-builder-layout.txt.

Checklists follows a selected canonical record through its existing targetRecordId list/start contract. contact-checklists.json is a canonical mixed-page recipe, rendered after a Puck round trip in local proof. No checklist reads occur before selection. Start fixes the selected parent; changing or clearing selection discards the old workspace. Checklist run IDs are not Records IDs and are no longer published as shared records. The current run contract supplies targetModuleId but no authoritative targetModuleKey, so the target is not republished with a guessed module key.

Checklist details now expose 16 independent pieces (39 total): back-action, run-status, run-title, target-title, target-location, progress, request-signoff-action, approval-status, cancellation, items, target-favorite, template-name, run-created, run-updated, run-revision and heading. Grouped heading/detail omit separately placed pieces. A custom detail layout takes over the native detail fallback; retain items/cancellation/approval-status where the workflow needs them. With only list parts placed the native detail remains available. Actions reuse exact native run revisions and permissions; target-favorite uses the canonical parent record. Proof upload wraps inside narrow cards and item status stays compact on mobile. Local synthetic proof is not tenant deployment acceptance.

Checklist list accepts custom child layouts. Ten row parts (49 total) expose row-open, row-title, row-target, row-location, row-status, row-progress, row-template, row-updated, row-revision and row-target-favorite. Values come from the current secured run row; row-open uses checklistRunId, favorite uses targetRecordId. Nested workspaces clear row identity. Native empty/loading/error/form recovery remains; no children keeps the native list. No new API or permission bypass.

Checklist start now exposes nine independent pieces (58 total): start-heading, start-template-status, start-create-template-action, start-record-query, start-find-records-action, start-target, start-template, start-title and start-submit-action. They share one native form owner even across custom columns. The grouped form fills omitted pieces. A followed record stays in the owner as a fixed hidden target, with tampering rejected; no separate target choice is invented. Native empty-template recovery, denial draft retention and pending request protection remain.

Reusable Checklist template forms expose nine pieces (67 total): template-form-heading, template-item-count, template-remove-action, template-form-name, template-form-description, template-form-items, template-add-item-action, template-save-action and template-cancel-action. External controls belong to one native owner, including required item fields. Read-only/pending controls remain disabled and stale saves retain draft values. Clearing an item key requires an explicit replacement instead of silently inventing a new one. Existing template removal keeps existing runs.

Checklist template-form-items accepts a custom repeated layout with eight item parts (75 total): template-item-position, key, title, instructions, response-type, evidence, required and remove-action (all with template-item- prefix). Each row retains the native controlled draft and form ownership. Removing a row preserves the other rows and renumbers sort order. Nested Checklist modules clear the outer item context. No children preserves the grouped editor.

Checklist templates accepts a custom repeated layout with six row parts plus templates-heading (82 total): template-row-open, name, description, item-count, updated and status. Actions use the secured returned template identity, not block-provided IDs. Empty and failed reads preserve the native recovery controls; nested modules clear the outer row context.

Checklist items accepts a custom repeated layout with fourteen parts (96 total): item-title, required, status, instructions, response, save-action, revision, response-type, proof-heading, proof-list, proof-file, proof-kind, proof-caption and proof-upload-action (all with item- prefix). Each item retains separate native save/upload form owners, exact item revision and canonical upload parent. Omitted controls stay in native forms. Pending/read-only controls are disabled; failed saves preserve drafts and successful refreshes advance revision.

Checklist item-proof-list accepts custom repeated proof cards with eight parts (104 total): proof-kind, file-name, caption, content-type, size, added-by, created and remove-action. Each action uses the returned evidence identity and the native confirmation lifecycle; it detaches evidence while retaining its underlying file. Unknown/read-only/busy actions are rejected, cancellation makes no request, failed removal retains the proof and nested workspaces clear its context.

Record Types exposes 17 parts. Place eyebrow, title, subtitle, selected-name, selected-key, selected-owner, selected-state and selected-created independently. Grouped header/detail omit individually placed content. Selection remains explicit; no first record type is chosen automatically. App-provided types remain read-only. Existing list, create, form, retire, restore and fields groups retain their existing behavior.

Record Types now exposes 24 parts: create-heading, create-name, create-key, create-key-hint, create-action, rename-name and rename-action join the existing seventeen. Inputs retain their native form owner when moved. Omitted controls remain in the grouped form; pending saves lock controls and duplicate submission. A failed save retains its message and draft until the user explicitly refreshes.

Record Types list accepts a custom repeated layout with row-open, row-name, row-key, row-owner, row-state and row-created (30 parts total). Each returned type owns its scope/key selection; designer props cannot replace that identity. Native empty/error handling remains, app types stay read-only, and nested workspaces clear row context.

Chat rooms: messages and member-list accept custom child layouts. Message rows expose author, timestamp, reply context, content (including the native edit form), attachment, reply/edit/remove actions, reactions, pinned and updated status. Member rows expose name, admin/member role, mute status, self marker, joined timestamp and removal action. Keep row pieces inside their owning list; nested workspaces clear inherited row context. Actions retain server-returned permissions and existing typed APIs. Removing a room member retains their earlier messages and author attribution for remaining authorized viewers; removing a message is a separate explicit action.

Record Types retirement asks for confirmation using the selected type name and states that existing records are retained. Cancel makes no write; the same pending lock covers confirmation and save. A closed workspace cannot execute its pending confirmation. Restore uses the existing expected-name/state update contract.

Account Administration overview parts
Use placeable-account-admin.json to arrange the native heading, account name,
summary counts, primary email, status, created time, available modules and
administrative capabilities inside custom Core containers or disclosures. The
12 fine parts share one native owner and its verified overview read. Unplaced
values stay in the default layout. An overview section block can own these parts;
an independent nested administration workspace starts a separate read scope.
Individual parts outside an administration owner show placement guidance.
These pieces never choose an actor, grant an administrative capability, or
create another business record. Existing People, Access and other section blocks
retain their native editing and recovery. Source example only; routed live
acceptance and approved release are still required.

Account Administration workspace controls
Use placeable-account-admin-workspace.json to place the native section navigator,
notices and current section-content in your own containers, including openable
controls. The 51 available parts include navigation, notices, loading, errors,
notice, write-review, section-content and a navigate-<section> button for each
existing section. Native capabilities decide which buttons appear. Grouped
navigation and notices omit children placed separately. A fixed section block
cannot switch sections; use a workspace owner for navigation.
Place exactly one section-content per owner, outside repeated rows, with a unique
block ID. Independent nested workspaces own their own content. The native editor
retains validation, pending/uncertain-write recovery and exact revisions. Dirty
section departure asks Stay or Discard; a pending answer cannot navigate another
verified actor. Closing a disclosure does not create a second form owner.
This groups the existing controls without certifying independent placement of
every editor field. Source example only; routed live acceptance remains pending.
