216 lines
9.9 KiB
Markdown
216 lines
9.9 KiB
Markdown
---
|
|
name: adminive-development
|
|
description: 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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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:
|
|
|
|
```cpp
|
|
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.
|
|
|
|
### Work on the React Gallery
|
|
|
|
- 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:
|
|
|
|
```text
|
|
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/`:
|
|
|
|
```text
|
|
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.
|