# 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 --- ## 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. 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. If you are reading this document and the repo contains addon code, read the code before reading this document any further. The code is the ground truth. This document describes what the code should look like. When they conflict, ask the operator before proceeding. Nothing in this document should be built without the operator's input and confirmation at each step. --- ## The Three Addons ### vs01 — Vital Signs **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` **Access:** Public read. No authentication required to view. HOA_MEMBER standing required to submit a Vital Signs 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 - Stores the record via the orchestrator spool **What it does not do:** - It does not present scenarios - It does not present case law - It does not manage the institutional directory --- ### dsc01 — Diagnostic Surface Categories **Purpose:** Present the Diagnostic Surface Categories — the legal surfaces where HOA governance disputes manifest — organized from the homeowner's perspective. Browsable publicly. Case law entries require HOA_MEMBER standing to submit. **Route:** `/dsc01` **Access:** Public read. HOA_MEMBER standing required to submit a diagnostic record or case law entry. **What it does:** - Presents the ten Active DSC categories with their diagnostic questions - Presents the Vital Sign dependencies for each category - Presents case law entries organized by DSC category - Connects each DSC category to the relevant Vital Signs - Allows verified participants to submit accounts under a specific DSC category - Stores records via the orchestrator spool **What it does not do:** - It does not present scenarios - It does not manage the Vital Signs record - It does not manage the institutional directory --- ### scn01 — Scenarios **Purpose:** The participant intake surface. A verified participant reads diagnostic scenarios, writes their account in their own words, and submits. The account is the record. **Route:** `/scn01` **Access:** HOA_MEMBER standing required. Public visitors see the access wall with SASE instructions. **What it does:** - 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 **What it does not do:** - It does not present Vital Signs - It does not present case law - It does not manage the institutional directory **Relationship to pilot:** The pilot `ds01` addon in `caselaw-document-access` is the functional prototype for this addon. The new `scn01` is a clean rebuild on the complete foundation — not a port of the pilot code. The pilot is reference, not source. --- ## Skeleton File Structure Each addon skeleton follows this structure. Files marked `[placeholder]` contain the correct structure but no functional logic. ``` hubzilla/addon/{addon}/ {addon}.php — main addon file: hooks, load/unload, content routing {addon}.apd — app descriptor mod_{addon}.pdl — PDL layout: aside, content, right_aside config.json.template — all config fields documented, no secrets listings.json.template — institutional directory schema, Core slots pre-populated Widget/ {Addon}.php — sidebar widget: the institutional directory view/ css/ {addon}.css — all presentation styles, no inline styles anywhere js/ {addon}.js — all behavior, no inline scripts anywhere contracts/ spool-v1.json — the spool envelope contract this addon produces README.md — one paragraph: what this addon does and does not do ``` --- ## Skeleton Content Requirements ### {addon}.php 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 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}.apd ``` version: 2 url: $baseurl/{addon} requires: local_channel name: {Descriptive name} photo: icon:{bootstrap-icon-name} categories: Civic Diagnostics desc: {One sentence — what this addon does.} ``` ### mod_{addon}.pdl ``` [template]default[/template] [region=aside] [widget={addon}][/widget] [/region] [region=content] $content [/region] [region=right_aside] [widget=notifications][/widget] [widget=newmember][/widget] [/region] ``` The right_aside is permanent and identical across all addons. It is never modified. ### config.json.template Every field the addon reads from `config.json` must appear here with a descriptive comment and a placeholder value. No field should ever be read from `config.json` that does not first appear in `config.json.template`. ```json { "_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.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. The Core tier always contains exactly three slots. They are permanent. They render whether populated or not. An unpopulated slot displays its role description and invitation text. No entity name or mark appears until the operator records written approval and populates the entry. ```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.", "_version": "1.0", "core": [ { "slot": "taxonomy-authority", "role": "Taxonomy Authority", "description": "The institutional source of the case law categories this diagnostic record is organized by.", "status": "pending", "name": null, "mark_url": null, "url": null, "approved_date": null }, { "slot": "methodology-certifier", "role": "Methodology Certifier", "description": "The law firm whose professional association with this project certifies the diagnostic record meets a standard the legal community can use.", "status": "pending", "name": null, "mark_url": null, "url": null, "approved_date": null }, { "slot": "peer-operator", "role": "Peer Operator", "description": "The cooperating Civic Infrastructure host who can independently verify, support, and if necessary continue this diagnostic record.", "status": "pending", "name": null, "mark_url": null, "url": null, "approved_date": null } ], "tier1": [], "tier2": [], "other": [] } ``` Entry schema for Tier-I, Tier-II, and Other: ```json { "id": "REPLACE — unique identifier, e.g. t1-001", "role": "REPLACE — e.g. Mediator, Attorney, Civic Organization", "name": "REPLACE — display name", "description": "REPLACE — one sentence describing this entity's practice", "url": "REPLACE — public URL", "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" } ``` ### Widget/{Addon}.php The Widget renders the institutional directory in the left aside. This is its only job. Must contain: - Correct namespace: `namespace Zotlabs\Widget;` - Class name matching filename exactly: `class {Addon}` - `widget($arr)` method returning sidebar HTML - `render_directory()` — loads listings via `{addon}_load_listings()`, renders the four-tab structure - `render_core_tab()` — always renders all three Core slots; pending slots show role description and invitation text - `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. #### Tab structure Four horizontal Bootstrap nav-tabs across the top of the aside: | Tab | Label | Content | |-----|-------|---------| | Core | Core | Three permanent slots — Taxonomy Authority, Methodology Certifier, Peer Operator | | Tier-I | Tier-I | Mediators, subject matter experts, advisors, authors | | 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