Files
kane-diagnostics/ADDON-SKELETON-SPEC.md
2026-06-06 02:34:22 -04:00

16 KiB

Addon Skeleton Specification

Project: kane-diagnostics
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 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.

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.

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

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/[association-slug]/[VS-code]

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, 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:

  • 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/[association-slug]/manage interface for 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. 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               — 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
  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
  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 — Entry Point

Must contain, in order:

  1. File header — addon name, description, version, min/max Hubzilla version
  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

{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

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.

{
  "_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",
  "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.

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.

{
  "_note": "Copy to listings.json on the host. Do not commit listings.json.",
  "_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:

{
  "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",
  "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",
  "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.

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.

Tab structure

Four horizontal Bootstrap nav-tabs:

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

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 document:

  • _header block — all envelope fields with descriptions
  • _payload block — the addon-specific fields
  • _meta block — contract version and purpose

When the payload structure changes, a new spool-v2.json is created. The old contract is never deleted.


Build Sequence

The operator directs which addon to build first. The expected sequence is:

  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 addon must be confirmed working before the next begins.


What the Operator Will Decide Before Each Addon Is Built

Before writing any file, confirm with the operator:

  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

This document proposes. The operator decides.


What Not To Do

  • 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.
  • 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.