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

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.