Updated
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
# Addon Skeleton Specification
|
||||
|
||||
**Project:** kane-diagnostics
|
||||
**Version:** 2.0
|
||||
**Prerequisite reading:** README.md, DSC-development-map.md, HANDOFF.md, CODING-GUIDELINES.md, INSTITUTIONAL-RELATIONSHIPS.md, FOR-PARTICIPANTS-AND-PROFESSIONALS.md
|
||||
**Version:** 3.0
|
||||
**Prerequisite reading:** README.md, DSC-development-map.md, CODING-GUIDELINES.md, INSTITUTIONAL-RELATIONSHIPS.md, FOR-PARTICIPANTS-AND-PROFESSIONALS.md, ASSOCIATION-CHANNEL-MODEL.md
|
||||
|
||||
---
|
||||
|
||||
## The Standard
|
||||
|
||||
This document defines the skeleton standard for all Kane Diagnostics Hubzilla addons — present and future. The three addons described below are the first implementations of the standard, not the definition of it. Any addon added to this project follows the same skeleton: same file layout, same Widget structure, same PDL pattern, same config template fields, same spool contract discipline, same coding constraints.
|
||||
This document defines the skeleton standard for all Kane Diagnostics Hubzilla addons — present and future. The three addons described below are the first implementations of the standard, not the definition of it. Any addon added to this project follows the same standard: same file layout, same Widget structure, same PDL pattern, same config template fields, same spool contract discipline, same coding constraints.
|
||||
|
||||
The standard changes only by deliberate revision of this document. A revised document increments the version number and is committed to the repo. The previous version is never deleted — it remains as the historical record of what was built under it.
|
||||
|
||||
@@ -16,6 +16,11 @@ If you are reading this document and the repo contains addon code, read the code
|
||||
|
||||
Nothing in this document should be built without the operator's input and confirmation at each step.
|
||||
|
||||
**Version history:**
|
||||
- v1.0 — initial skeleton standard, single `corpus_builder_group_id` config, single `{addon}.php` file
|
||||
- v2.0 — Widget directory structure, four-tab institutional directory, `listings.json.template` with Core slots
|
||||
- v3.0 — per-association config structure, split-file PHP pattern (`{addon}.php` + `{addon}_renderer.php` + `{addon}_spool.php`), `xchan` field in listings template
|
||||
|
||||
---
|
||||
|
||||
## The Three Addons
|
||||
@@ -24,15 +29,14 @@ Nothing in this document should be built without the operator's input and confir
|
||||
|
||||
**Purpose:** Present the ten structural preconditions of an HOA association. Public-facing — readable without authentication. This is the first thing a participant reads and the first thing an attorney reads.
|
||||
|
||||
**Route:** `/vs01`
|
||||
**Route:** `/vs01/[association-slug]/[VS-code]`
|
||||
|
||||
**Access:** Public read. No authentication required to view. HOA_MEMBER standing required to submit a Vital Signs record for a specific association.
|
||||
**Access:** Public read. HOA_MEMBER standing (SASE Participant or Corpus Builder group) required to submit a VS record for a specific association.
|
||||
|
||||
**What it does:**
|
||||
- Presents each of the ten Vital Signs in plain language
|
||||
- Explains why each one matters to the homeowner
|
||||
- Provides public record lookup guidance for each Vital Sign (Recorder of Deeds, Secretary of State, IDFPR, etc.)
|
||||
- Allows a verified participant to submit a Vital Signs record for their association
|
||||
- Presents each of the ten Vital Signs in plain language, one at a time per JSON schema
|
||||
- Provides verification source guidance for each Vital Sign
|
||||
- Allows a verified participant to submit a VS record for their association
|
||||
- Stores the record via the orchestrator spool
|
||||
|
||||
**What it does not do:**
|
||||
@@ -77,7 +81,7 @@ Nothing in this document should be built without the operator's input and confir
|
||||
- Presents the scenario carousel — browse only, no selection required
|
||||
- Accepts the participant's account in their own words
|
||||
- Writes the submission to the orchestrator spool as a TMP record
|
||||
- Provides an operator-only `/scn01/manage` interface for scenario management and TMP review
|
||||
- Provides an operator-only `/scn01/[association-slug]/manage` interface for TMP review
|
||||
|
||||
**What it does not do:**
|
||||
- It does not present Vital Signs
|
||||
@@ -91,11 +95,13 @@ The pilot `ds01` addon in `caselaw-document-access` is the functional prototype
|
||||
|
||||
## Skeleton File Structure
|
||||
|
||||
Each addon skeleton follows this structure. Files marked `[placeholder]` contain the correct structure but no functional logic.
|
||||
Each addon skeleton follows this structure. The `{addon}.php` entry point, `{addon}_renderer.php`, and `{addon}_spool.php` are the three mandatory PHP files for any addon that renders JSON-driven forms and POSTs to the orchestrator.
|
||||
|
||||
```
|
||||
hubzilla/addon/{addon}/
|
||||
{addon}.php — main addon file: hooks, load/unload, content routing
|
||||
{addon}.php — entry point: hooks, routing, access state, config/listings loaders, CSRF
|
||||
{addon}_renderer.php — form renderer: schema loader, all field type functions, flags, sources
|
||||
{addon}_spool.php — POST handler: validation, envelope builder, orchestrator POST
|
||||
{addon}.apd — app descriptor
|
||||
mod_{addon}.pdl — PDL layout: aside, content, right_aside
|
||||
config.json.template — all config fields documented, no secrets
|
||||
@@ -109,30 +115,59 @@ hubzilla/addon/{addon}/
|
||||
{addon}.js — all behavior, no inline scripts anywhere
|
||||
contracts/
|
||||
spool-v1.json — the spool envelope contract this addon produces
|
||||
vital-signs/ — vs01 only: ten VS JSON schema files
|
||||
VS-RENDERER-SPEC.md — vs01 only: renderer contract document
|
||||
README.md — one paragraph: what this addon does and does not do
|
||||
```
|
||||
|
||||
**File size constraint:** No PHP file may exceed 500 lines. If the entry point approaches that limit, extract further — for example, a `{addon}_manage.php` for the operator TMP interface. The split point is always a single `require_once` in the entry point.
|
||||
|
||||
---
|
||||
|
||||
## Skeleton Content Requirements
|
||||
|
||||
### {addon}.php
|
||||
### {addon}.php — Entry Point
|
||||
|
||||
Must contain, in order:
|
||||
|
||||
1. **File header** — addon name, description, version, min/max Hubzilla version
|
||||
2. **Hook registration** — `{addon}_load()` and `{addon}_unload()` with PDL and Widget hooks
|
||||
3. **PDL loader** — `{addon}_load_pdl()` following the established convention
|
||||
4. **Helper** — `{addon}_h()` — the HTML escape function
|
||||
5. **Access state** — `{addon}_access_state()` — returns `'public'`, `'participant'`, or `'operator'`
|
||||
6. **Access wall** — `{addon}_access_wall()` — plain language, SASE link
|
||||
7. **Content router** — `{addon}_content()` — loads CSS/JS, checks access, routes by path and method
|
||||
8. **Config loader** — `{addon}_load_config()`
|
||||
9. **Listings loader** — `{addon}_load_listings()` — reads `listings.json`, returns structured array; fails visibly if file is missing or malformed
|
||||
10. **CSRF token and verify** — standard pattern
|
||||
11. **Placeholder content functions** — one per route, each returning a `// TODO` string with a plain description of what goes here
|
||||
2. **require_once** — loads `{addon}_renderer.php` and `{addon}_spool.php`
|
||||
3. **Hook registration** — `{addon}_load()` and `{addon}_unload()` with PDL and Widget hooks
|
||||
4. **PDL loader** — `{addon}_load_pdl()` following the established convention
|
||||
5. **Helper** — `{addon}_h()` — the HTML escape function
|
||||
6. **Access state** — `{addon}_access_state($association_slug)` — returns `'public'`, `'participant'`, `'professional'`, or `'operator'`
|
||||
7. **Professional check** — `{addon}_is_professional()` — reads `listings.json` xchan field
|
||||
8. **Access wall** — `{addon}_access_wall($association_slug)` — plain language, SASE link
|
||||
9. **Content router** — `{addon}_content()` — loads CSS/JS, reads argv(1) for association slug, argv(2) for sub-route, routes by path and method
|
||||
10. **Render functions** — one per route: index, landing, form, manage (TMP review)
|
||||
11. **Config loader** — `{addon}_load_config()`
|
||||
12. **Listings loader** — `{addon}_load_listings()`
|
||||
13. **CSRF token and verify** — standard pattern
|
||||
|
||||
No functional logic in the skeleton. No database calls. No file I/O beyond config and listings loaders. No orchestrator calls. Those come after the skeleton is confirmed.
|
||||
### {addon}_renderer.php — Form Renderer
|
||||
|
||||
Contains all functions that read JSON schemas and produce HTML. Must contain:
|
||||
|
||||
- Schema loader — reads all schema files from the appropriate directory, sorts by filename, skips malformed files with visible logger call
|
||||
- Seven field type functions — `text`, `number`, `date`, `boolean`, `select`, `textarea`, `multiselect`
|
||||
- Field dispatcher — routes to the correct type function, appends diagnostic flag
|
||||
- Conditional display wrapper — `depends_on` rendered with `data-` attributes
|
||||
- Diagnostic flag renderer — evaluates `flag_condition` server-side, three severity levels
|
||||
- Compound condition banner renderer — placeholder returning empty string until orchestrator query implemented
|
||||
- Cross-VS reference renderer — linked callout
|
||||
- Verification sources panel — collapsible Bootstrap collapse
|
||||
- Perspective note renderer — information-record distinction for specific VS codes
|
||||
- Immutable public record check and read-only renderer
|
||||
|
||||
### {addon}_spool.php — POST Handler
|
||||
|
||||
Contains all functions that handle form submission. Must contain:
|
||||
|
||||
- POST handler — validates CSRF, determines perspective, collects fields
|
||||
- Required field validator — returns array of error labels
|
||||
- Spool envelope builder — assembles the contract-compliant JSON payload
|
||||
- Orchestrator POST — curl call with node token header, returns true on success or error string
|
||||
- Form re-render with submitted values — called on validation failure
|
||||
|
||||
### {addon}.apd
|
||||
|
||||
@@ -176,11 +211,27 @@ Every field the addon reads from `config.json` must appear here with a descripti
|
||||
"_note": "Copy to config.json. Do not commit config.json — it contains secrets and installation-specific values.",
|
||||
"receiver_url": "REPLACE — orchestrator receiver endpoint",
|
||||
"node_token": "REPLACE — shared secret for node-to-orchestrator auth",
|
||||
"corpus_builder_group_id": 0,
|
||||
"listings_file": "REPLACE — absolute path to listings.json on the host"
|
||||
"listings_file": "REPLACE — absolute path to listings.json on the host",
|
||||
"directory_default_tab": "core",
|
||||
"associations": {
|
||||
"REPLACE-SLUG": {
|
||||
"_note": "Key is the URL slug — lowercase, hyphens only, matches argv(1) in the route",
|
||||
"name": "REPLACE — association legal name",
|
||||
"address": "REPLACE — association street address",
|
||||
"channel_id": 0,
|
||||
"channel_address": "REPLACE — channel@node address",
|
||||
"groups": {
|
||||
"public": 0,
|
||||
"sase_participant": 0,
|
||||
"corpus_builder": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Multiple associations are registered as additional keyed entries in the `associations` object. No code change is required to add a new association. See ASSOCIATION-CHANNEL-MODEL.md for the full operator workflow.
|
||||
|
||||
### listings.json.template
|
||||
|
||||
The institutional directory schema. This file documents the structure. The live `listings.json` on the host is operator-managed and never committed to the repo.
|
||||
@@ -189,7 +240,7 @@ The Core tier always contains exactly three slots. They are permanent. They rend
|
||||
|
||||
```json
|
||||
{
|
||||
"_note": "Copy to listings.json on the host. Do not commit listings.json — it contains entity names and approval records that are operator-managed.",
|
||||
"_note": "Copy to listings.json on the host. Do not commit listings.json.",
|
||||
"_version": "1.0",
|
||||
"core": [
|
||||
{
|
||||
@@ -238,12 +289,16 @@ Entry schema for Tier-I, Tier-II, and Other:
|
||||
"name": "REPLACE — display name",
|
||||
"description": "REPLACE — one sentence describing this entity's practice",
|
||||
"url": "REPLACE — public URL",
|
||||
"xchan": "REPLACE — Hubzilla xchan hash of this professional's channel, used for access control",
|
||||
"qualifying_case": "REPLACE — Tier-II attorneys only: case citation from public record",
|
||||
"listed_date": "REPLACE — ISO date of listing",
|
||||
"approved_date": "REPLACE — ISO date of written approval"
|
||||
"approved_date": "REPLACE — ISO date of written approval",
|
||||
"status": "REPLACE — active or pending"
|
||||
}
|
||||
```
|
||||
|
||||
The `xchan` field is how the addon identifies a professional visitor and grants the `professional` access state. The operator records the xchan hash when approving the listing. Without it, the listing displays in the directory but does not grant professional access.
|
||||
|
||||
### Widget/{Addon}.php
|
||||
|
||||
The Widget renders the institutional directory in the left aside. This is its only job.
|
||||
@@ -257,11 +312,11 @@ Must contain:
|
||||
- `render_tier_tab($entries, $placeholder)` — renders entries if any exist; renders placeholder text if none
|
||||
- `h($value)` — local HTML escape method
|
||||
|
||||
The Widget does not render navigation. It does not render a steps indicator. It does not render resource panels. Those patterns belong to the pilot and do not transfer.
|
||||
The Widget does not render navigation. It does not render a steps indicator. It does not render resource panels.
|
||||
|
||||
#### Tab structure
|
||||
|
||||
Four horizontal Bootstrap nav-tabs across the top of the aside:
|
||||
Four horizontal Bootstrap nav-tabs:
|
||||
|
||||
| Tab | Label | Content |
|
||||
|-----|-------|---------|
|
||||
@@ -270,83 +325,53 @@ Four horizontal Bootstrap nav-tabs across the top of the aside:
|
||||
| Tier-II | Tier-II | Attorneys — qualification standard per INSTITUTIONAL-RELATIONSHIPS.md |
|
||||
| Other | Other | Civic organizations, consumer protection entities, oversight agencies |
|
||||
|
||||
The default active tab is Core. The operator may specify a different default per addon in `config.json` via a `directory_default_tab` field — this field is listed in `config.json.template` with value `"core"`.
|
||||
|
||||
A pending Core slot renders:
|
||||
|
||||
```html
|
||||
<div class="{addon}-directory-slot {addon}-slot-pending">
|
||||
<div class="{addon}-slot-role">{role}</div>
|
||||
<div class="{addon}-slot-description">{description}</div>
|
||||
<div class="{addon}-slot-status">Invitation pending</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
A populated Core slot renders the entity name, mark (if provided), and URL. It never renders until `status` is `"active"` in `listings.json`.
|
||||
Default active tab is Core. Operator may specify a different default per addon via `directory_default_tab` in `config.json`.
|
||||
|
||||
### contracts/spool-v1.json
|
||||
|
||||
The JSON contract this addon produces and POSTs to the orchestrator. Must contain:
|
||||
- `_header` block with all required fields documented
|
||||
- `_payload` block with the addon-specific fields
|
||||
- `_meta` block describing the contract version and purpose
|
||||
The JSON contract this addon produces and POSTs to the orchestrator. Must document:
|
||||
- `_header` block — all envelope fields with descriptions
|
||||
- `_payload` block — the addon-specific fields
|
||||
- `_meta` block — contract version and purpose
|
||||
|
||||
This file is the contract. When the payload structure changes, the version increments and a new file is created (`spool-v2.json`). The old contract is never deleted — it documents what the orchestrator must remain able to consume.
|
||||
|
||||
### {addon}.css
|
||||
|
||||
Skeleton contains:
|
||||
- File header comment with addon name and version
|
||||
- Section comments for each visual area: layout, header, content, aside, directory, directory-core, directory-tiers, manage
|
||||
- No rules yet — just the section comments as placeholders
|
||||
|
||||
### {addon}.js
|
||||
|
||||
Skeleton contains:
|
||||
- IIFE wrapper
|
||||
- `initDirectory()` function — placeholder, `// TODO: Bootstrap tab init if needed beyond data-bs-toggle`
|
||||
- One placeholder function per interactive area with a `// TODO` comment
|
||||
- `init()` calling all functions
|
||||
- DOMContentLoaded guard
|
||||
|
||||
The pilot's `initResourcePanels()` function does not transfer. The new addons have no resource panels.
|
||||
When the payload structure changes, a new `spool-v2.json` is created. The old contract is never deleted.
|
||||
|
||||
---
|
||||
|
||||
## Build Sequence
|
||||
|
||||
The operator will direct which addon to build first. The expected sequence based on the DSC development map is:
|
||||
The operator directs which addon to build first. The expected sequence is:
|
||||
|
||||
1. `vs01` — Vital Signs — because it is the entry point for both participants and attorneys
|
||||
2. `dsc01` — DSC Categories — because it is the primary reference surface
|
||||
3. `scn01` — Scenarios — because it depends on both Vital Signs and DSC context being established
|
||||
1. `vs01` — Vital Signs — entry point for both participants and attorneys
|
||||
2. `dsc01` — DSC Categories — primary reference surface
|
||||
3. `scn01` — Scenarios — depends on Vital Signs and DSC context being established
|
||||
|
||||
**Each skeleton must be confirmed working — loading without errors, displaying the access wall or placeholder content correctly, rendering all four directory tabs including the three pending Core slots — before the next skeleton begins.**
|
||||
Each addon must be confirmed working before the next begins.
|
||||
|
||||
---
|
||||
|
||||
## What the Operator Will Decide Before Each Skeleton Is Built
|
||||
## What the Operator Will Decide Before Each Addon Is Built
|
||||
|
||||
Before writing any skeleton file, discuss and confirm with the operator:
|
||||
Before writing any file, confirm with the operator:
|
||||
|
||||
1. The exact route — the operator may have a different preference than what is proposed here
|
||||
2. The access model — which privacy group gates participant access for this addon
|
||||
3. The default directory tab — which of the four tabs is active on first load for this addon's typical visitor
|
||||
4. The config fields — whether any addon-specific configuration is needed beyond the standard fields
|
||||
1. The exact route
|
||||
2. The access model — which privacy groups gate participant access for this addon
|
||||
3. The default directory tab
|
||||
4. Any addon-specific config fields beyond the standard fields
|
||||
5. The spool contract — what the payload block contains for this addon's submissions
|
||||
|
||||
Do not assume these decisions are made because they appear in this document. This document proposes. The operator decides.
|
||||
This document proposes. The operator decides.
|
||||
|
||||
---
|
||||
|
||||
## What Not To Do
|
||||
|
||||
- Do not port the pilot `ds01` code. Read it as reference for Hubzilla conventions. Write the skeleton fresh.
|
||||
- Do not add functional logic to the skeleton. Placeholders only.
|
||||
- Do not generate all three skeletons at once. One at a time, confirmed working before the next begins.
|
||||
- Do not port the pilot `ds01` code. Read it as reference. Write fresh.
|
||||
- Do not generate all three addons at once. One at a time, confirmed working before the next begins.
|
||||
- Do not create files that already exist in the repo. Read the repo first.
|
||||
- Do not hardcode any value that belongs in `config.json`.
|
||||
- Do not write inline styles or inline scripts.
|
||||
- Do not exceed 500 lines in any PHP file. The skeleton should be well under that — if it is approaching 300 lines before any logic is added, something is wrong.
|
||||
- Do not build the Widget without all four directory tabs present and the three Core slots rendering. The directory is not a feature added after the skeleton is confirmed. It is part of what makes the skeleton complete.
|
||||
- Do not populate Core slots with entity names. Pending state is the correct skeleton state. Names appear only after written approval is recorded by the operator.
|
||||
- Do not exceed 500 lines in any PHP file.
|
||||
- Do not build the Widget without all four directory tabs and the three Core slots rendering.
|
||||
- Do not populate Core slots with entity names. Pending state is the correct state until written approval is recorded.
|
||||
- Do not push from the host. Gitea is the SSOT.
|
||||
|
||||
Reference in New Issue
Block a user