Files
2026-08-08 22:39:18 +08:00

9.9 KiB

name, description
name description
adminive-development Use when implementing, modifying, debugging, reviewing, or integrating Adminive descriptors, field presentation, Form/Collection views, Composition manifests, JSON/AMIS generation, managed values, HTTP resources, adapters, Gallery frontend, tests, CMake, install/export, or packaging. Do not use for work confined to Structive internals; use the nested structive-development skill there.

Adminive Development

Goal

Make changes through Adminive's existing semantic layers instead of bypassing them with framework-specific code. The backend remains the business-schema source of truth; adapters translate that truth into JSON, HTTP, AMIS, or frontend renderer contracts.

Start by reading the source of truth

For public behavior or architecture changes, inspect:

  1. AGENTS.md.
  2. backend/library/DESIGN.md.
  3. The closest public header and its focused tests.
  4. README.md for the user-facing contract.
  5. If the change reaches third_party/Structive, read third_party/Structive/AGENTS.md and its skill before editing that subtree.

Do not infer an API from the Gallery alone. The public headers and tests define the actual contract.

Pick the correct layer

Use this decision table before editing:

Need Owner
Native property identity, intrinsic read/write, constraint, synchronization topology Structive
Admin-facing field label/description/control/options/visible condition Adminive Field Presentation
Display/create/edit field selection and logical field layout Form View
Collection columns, query capability, CRUD layout, table/list/cards mode Collection View
Composition of complete Form/Collection/Status components Composition View
Slot source/kind/API contract for an independent frontend Composition Manifest
JSON encoding/decoding protocol Adminive Core adapter protocol + concrete JSON adapter
AMIS renderer JSON AMIS adapter
HTTP route/body/query bridging httplib/Drogon adapter
External prepare/commit/rollback Resource_Transaction
In-process mutable consistency Managed/Structive synchronization
Pixel styling/responsive layout for a frontend node frontend renderer

If a proposed feature crosses several rows, keep each concern in its owner instead of creating one all-purpose descriptor flag.

Common implementation recipes

Describe a business object

Prefer explicit field descriptors:

struct Config {
    std::string host;
    int port{};
};
template <>
struct adminive::Type_Descriptor<Config> {
    static auto get() {
        using T = Config;
        return adminive::object<T>(
            "config",
            "Config",
            ADMINIVE_FIELD(T, host).editable().creatable().label("Host").text_input(),
            ADMINIVE_FIELD(T, port).editable().creatable().label("Port").number_input()
        );
    }
};

Do not add the same field list to React.

Define Form semantics

Put view-specific order/grouping in Type_View_Descriptor<T>, not in the field descriptor:

template <>
struct adminive::Type_View_Descriptor<Config> {
    static auto edit() {
        using T = Config;
        return adminive::edit_form<T>(adminive::vertical(
            adminive::use<&T::host>(),
            adminive::use<&T::port>()
        ));
    }
};

Keep display/create/edit semantics distinct when they differ.

Define Collection semantics

Start from one collection contract and derive the rendering mode:

const auto base = adminive::collection_view<Row>(
    adminive::column<&Row::name>("Name").search(),
    adminive::column<&Row::score>("Score").sort()
).default_sort("score");
const auto item = adminive::collection_item<&Row::name>().body<&Row::score>();
const auto table = base.as_table();
const auto list = base.as_list(item);
const auto cards = base.as_cards(item, 3);

sort(), search(), and filter() are backend capabilities. Both httplib and Drogon must pass dynamic query fields to Collection_Service, which rejects fields not authorized by the View Schema.

Compose complete components

Use Composition instead of hand-building AMIS containers:

const auto page = adminive::composition_view("device_page", adminive::compose::vertical(
    adminive::compose::slot("config"),
    adminive::compose::slot("devices")
));

Use compose::frontend(...) only when the backend intentionally delegates final positioning. For an independent frontend, expose a Composition Manifest with a unique Slot Contract for every referenced slot.

Adapt an external value type

Use Value_Adapter<T, Json> when a wrapper has a stable model value. Use Control_Adapter<T, Json> only when the renderer itself needs custom behavior. Keep JSON-framework details out of backend/library.

Adapt enums or reflected aggregates

  • Enum semantics go through Enum_Adapter<Enum>; include adminive/adapters/magic_enum.hpp only when that bridge is desired.
  • Aggregate reflection goes through Reflection_Adapter<T>; use the Boost.PFR bridge only in the service layer.
  • Do not make Adminive Core include magic_enum, Boost.PFR, nlohmann JSON, cpp-httplib, or Drogon.

Expose a mutable resource

Use Resource_Service through the transport wrapper. Build Request_Context only at the outer HTTP bridge. Put persistence/external side effects in Resource_Transaction, and preserve rollback behavior when runtime commit or external commit fails.

  • Read /descriptor, /view, /data, /amis, and Composition Manifest contracts from the backend.
  • Resolve frontend slots from the manifest/renderer registry; never hard-code business slot names or C++ field names into a parallel schema.
  • When TypeScript receives unknown, narrow it before rendering. Do not silence protocol problems with as any.
  • Keep index.html non-cacheable and content-hashed/versioned static resources immutable.

Invariants that must not regress

  • Descriptors validate legal, unique field names.
  • Readonly fields can be displayed but are not included in create/edit submit payloads unless the active View permits writing.
  • Sensitive fields do not appear in frontend data or default-bearing descriptor output.
  • Collection query fields and sort direction are rejected when the View does not authorize them.
  • Httplib and Drogon expose the same Collection query contract even though their execution models differ.
  • frontend composition delegates layout; vertical/horizontal/flow/grid/list/group/card/tabs retain backend semantic composition.
  • Slot names in a Manifest have exactly one contract.
  • AMIS generation translates semantic View/Composition data; it does not invent business layout or permissions.
  • Managed synchronization and persistence transaction remain separate mechanisms.

Testing workflow

Use the smallest relevant tests first:

  • Core descriptor/JSON/collection behavior: Adminive_Core_Adapter_Test or focused library tests.
  • View/Composition/Manifest behavior: Adminive_View_Schema_Test.
  • Managed behavior: Adminive_Managed_Test.
  • Value/Object/Reflection/Polymorphic adapters: Adminive_Advanced_Adapter_Test.
  • Httplib routes: Adminive_Httplib_Adapter_Test with a real localhost server/client.
  • Drogon routes: Adminive_Drogon_Adapter_Test.
  • Gallery contract: Adminive_Gallery_Test.
  • Install/export: Adminive_Install_Consumer_Test.
  • Source archive: Adminive_Package_Zip_Test.

After focused checks, run the repository gate appropriate to the change. For normal C++ changes:

cmake -S . -B verification/debug -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON
cmake --build verification/debug
ctest --test-dir verification/debug --output-on-failure

For release readiness, use python scripts/verify.py. If frontend code changed, run from frontend/:

npm run test:unit
npm run build

Run Playwright when the change affects browser behavior and dependencies are available.

Adding a public API

Before adding a public API:

  1. Confirm an existing semantic layer cannot express the requirement.
  2. Prefer additive APIs over changing old signature meaning.
  3. Do not create a compatibility implementation for a replaced design unless explicitly required.
  4. Add focused tests that prove the old semantics still hold.
  5. Add/update independent public-header compile coverage if a public header changes.
  6. Update backend/library/DESIGN.md and README.md when the contract changes.
  7. Update the Gallery Capability Case when the feature is user-visible and should be discoverable.

Review checklist

Before handoff, verify:

  • The change is in the correct layer.
  • No frontend business schema was duplicated.
  • No sensitive/write/query boundary became weaker.
  • Httplib/Drogon behavior remains contract-equivalent where they share Collection_Service/Resource_Service.
  • Old implementations were removed rather than wrapped when the design was replaced.
  • Tests use ADMINIVE_CHECK, not assert().
  • CMake paths are local to the owning CMake file.
  • Install/export/package contents still match public headers and repo-local Codex guidance.
  • Only verification steps actually executed are reported as passing.

Dependency and release workflow

Repository builds use pinned bundled nlohmann/json, magic_enum, and cpp-httplib headers, but installed adapter components use external CMake package targets. Never restore installation of bundled dependency headers under ${CMAKE_INSTALL_INCLUDEDIR}. When changing a dependency version, update the bundled source, AdminiveConfig.cmake.in, THIRD_PARTY_NOTICES.md, the matching license text, install consumer, and package test together.

The fast Drogon test uses a fake transport contract. Before release on a machine with real Drogon installed, run python scripts/verify.py --real-drogon; it must compile the real adapter and an installed-package consumer. Do not report that gate as passed when it was skipped.

Adminive/Structive project-owned code has no outbound license declared in this tree. Do not generate a project license unless the owner explicitly chooses one.