Foundation 6

Contributing to Foundation

This is the contract between Foundation and the people who work on it: how a method is written, named, documented and tested, and what a change must not break. Read it once before your first change. It is short because the code is the long version: when in doubt, copy the nearest Fnd_* method that does something similar.

Foundation is released under the MIT licence. By contributing you agree that your contribution is too.

What Foundation is

Foundation (Foundation Shell) is a 4D component. It runs inside a host database that supplies its own tables and forms, and it never knows what those are. Everything Foundation does for a host, it does through two kinds of method the host provides:

The demo host, Foundation Product Sales, carries a working copy of every one: https://github.com/open4dshell/Foundation-Product-Sales. HooksAndHosts/ holds an annotated example.

The floor is 4D 20 LTS

Foundation compiles on 4D 20 LTS, 20 R, 21 LTS and 21 R. The LTS line never receives R-release features, so anything that arrived in an R release is out, however old it looks:

Do not useArrivedUse instead
Try(expression), Try…Catch…End try20 R4 / 20 R5ON ERR CALL("Fnd_Gen_SilentError") around the one statement that can raise; restore the previous handler on the next line; test the result
Direct typing without Compiler_* entries20 R4keep the Compiler_Fnd_* typing methods current
Process info, Call chain, Application info20 R10Process activity, Get call chain, through Fnd_Shell_CommandWrapper
... in #DECLARE20 R3the two-form declaration block (see Variadic methods)

Never take a version claim from memory. Development/commands.json lists every command with the version it appeared in; Fnd_FCS_VersionCapabilities gates features at runtime.

ON ERR CALL is not Catch: it resumes at the next statement. Guard the single statement that can fail, set the result to Null first, and test it after. Fnd_Gen_SilentError is the component's only no-op method; do not add another.

Modules

Methods are grouped by prefix: Fnd_<Module>_<Subject><Verb>, for example Fnd_Dlg_SetIcon, Fnd_Pref_GetValue, Fnd_Wnd_OpenWindow. Every module has:

Fnd_Gen is required by everything; Fnd_Shell is the application shell; Fnd_FCS is build-time tooling for Foundation's own developers. It is never published: no Fnd_FCS_* method is shared, and a host that needs something it does gets an Fnd_XXX_* wrapper in the owning module instead. A Foundation method may call into it (ruled 2026-08-23). The Fnd_FCS folder is kept in step with other projects by DevSync, so Foundation-specific tooling goes in Fnd_FCS_Local; Guide/11-Developer-Tooling.md describes the set and AgentNotes/Reference/Fnd_FCS-Classification.md the split.

Method and variable names cannot exceed 31 characters. Class function names and object properties can.

Spelling. Anything resolved by name at runtime uses US spelling: method names, Storage keys, object properties, list names (Initialized, not Initialised), because Foundation was written by an American in 2003 and names are matched, not read. Comments and local variables are the author's choice.

State

Foundation is thread-safe by default and is removing its last interprocess variables. Add no new <> variables and no new process variables that outlive a method. Instead:

Fnd_Dlg is the worked example of a module converted this way.

Prefer ORDA (ds, entities, query) over classic record commands in new or changed code. Prefer the native 4D command over a hand-written equivalent, and let the method become a wrapper: prove the two agree first, with assertions written against the old implementation.

Writing a method

One method per .4dm file. Helpers get their own file, not a Function block. Every project method begins with a boxed header and a #DECLARE:

//%attributes = {"invisible":true,"shared":true,"folder":"Fnd_Text","lang":"en"}
// ----------------------------------------------------
// Project Method: Fnd_Text_Wrap ($text_t : Text {; $width_i : Integer}) --> $wrapped_t : Text

// Wraps the text into lines of at most the given width, joined by carriage
//   returns. The width is a character count, not a pixel width.

// Access: Shared

// Parameters:
//   $text_t  : Text    : The text to wrap
//   $width_i : Integer : Characters per line (optional, default 72)

// Returns:
//   $wrapped_t : Text : The wrapped text

// Created by Jane Smith on 2026-12-03
// ----------------------------------------------------

#DECLARE($text_t : Text; $width_i : Integer)->$wrapped_t : Text

var $numberOfParameters_i : Integer

Rules the header carries, because a program reads it: Fnd_FCS_WriteDocumentation turns these headers into the documentation under Documentation/Methods/ and the IDE tooltip, and it keys on the literal tokens.

Then:

Variadic methods

A method with an open-ended parameter list carries both declaration forms between literal markers, and Fnd_FCS_ToggleDeclarationBlocks activates the right one at startup:

// ==== BEGIN COMPILER DECLARATION BLOCK
// ---- 4D 20 R3 and later
#DECLARE($module_t : Text; ... : Text)->$result_t : Text
// ---- 4D 20 R2 and earlier
// C_TEXT($0; $1; ${2})
// var $module_t; $result_t : Text
// $module_t:=$1
// ==== END COMPILER DECLARATION BLOCK

Write the body once with ${$i} and Count parameters, and end with return. Never narrow a variadic signature: hosts rely on the open end.

Signatures are contract

Every method with "shared":true in its attributes is callable by a host, at the signature it has, whatever its header says. Hosts call Foundation by name at runtime, so no static search finds every caller. Therefore:

Record every host-visible change in Release/UPGRADING.md as Breaking, Behavioural or Additive.

History

Foundation is more than thirty years of work by many hands, since 1993, and the names in it are kept. Never remove or reword a line naming Dave Batton or Walt Nelson, or any other earlier contributor. Add your own line under // Created by when you change a method, and one line to aaaMods, newest first, inside the current year's Case of. Development churn is squashed to one line per method at each release; the detail lives in the repository history.

Tests

Forms

Foundation has used one form style since 1993. Three dialogs rewritten in 2026 with all their logic in the form method had to be redone, so:

  1. Code lives on the objects. Every interactive object has its own file in ObjectMethods/, even if it is one line calling a project method. No central dispatch on object name in the form method.
  2. The form method is a thin shell: On Load setup, cross-object state (enabling buttons), and a tail call to Fnd_Gen_FormMethod, which handles On Activate (menu bar), On Close Box and the close-all and quit machinery. Fnd_SqNo_Editor shows the shape. Enable onActivate and onCloseBox on the form. An object method that handles one event does not test the event code.
  3. Dialog furniture, right to left along the bottom: Save (default button, answers Enter), Cancel (Cmd-period), then auxiliary buttons. Return and Enter are different keys on the Mac, so every dialog also carries two off-form traps just below the visible boundary, labelled Trap Return (accept) and Trap Escape (cancel), sizingY: move, not focusable. Entry fields disable the Return trap while they hold the focus. Fnd_Dlg_OSX_Dialog is the reference. The close box behaves as Cancel.
  4. Metrics: a single-line input at the form font is 17 pixels high, not the 21 the editor draws.
  5. Multi-page editors: shared furniture on page 0, one page per mode.

A host's data-entry forms are a different kind of form, table-bound and inheriting Fnd_IO_InputForm; Guide/06-Input-Forms.md is the guide, with the parent-field and child-listbox patterns and the helpers to copy.

Object names are prefix_name (btn_save, input_email); form names are Fnd_<Module>_<Purpose>. Event names in form.4DForm must match 4D's schema exactly (onAlternateClick), because a wrong name fails silently. Windows open through Fnd_Wnd_* with the menu bar installed and register with Fnd_Menu_Window_Add; Fnd_Art_About is the model. Menus are built at runtime with Fnd_List_AddItem, never in menus.json.

Commits and branches

Building

Open Project/Foundation 6.4DProject in the target 4D and run __BuildComponent. The version lives only in Resources/InfoPlist.json; the build derives the target from the running 4D and picks Release/BuildSettings/buildApp-<target>.4DSettings. Release/README.md is the release process and is the source of truth where a script disagrees.

Asking

Open an Issue with the 4D version, the target, and what you saw. A failing Test_Fnd_* section or a ten-line method that reproduces it is worth more than a description.