0004 — Forms Permissions Architecture¶
- Status: Accepted
- Date: 2026-05-09
- Decision owners: Gina Lyn Cox · Operations Lead
- Related: Form O ·
guildcommerce-core-plugin· ADR-005 (Community Platform)
Context¶
The Ganjier Guild ecosystem uses 22 operational forms (Forms A–V) spanning onboarding, registries, governance, communications, capital, and cross-entity service. The Operational Manual already defines a role taxonomy (Section 28A) and a role × form permissions matrix (Form O). Until now, that taxonomy and matrix existed only as prose. We needed:
- A machine-readable representation of the permissions that the
guildcommerce-core-plugincan consume. - A way to gate visibility, fill rights, and approval rights on each form by the participant's role inside the member hub (rendered by
guildcommerce-theme). - A platform-neutral schema so the model survives a future migration from BuddyPress to BuddyBoss Platform (see ADR-005).
- PDF and Markdown renderings of every form that visually communicate the audience tier (Public / Internal / Restricted) so reviewers, board members, and counsel can read the same artifact and know who sees it.
Decision¶
Adopt a frontmatter + manifest model for form permissions.
- YAML frontmatter on every
forms/*.mdcaptures:form_letter,title,category,audience(public|internal|restricted),status,visible_to,fillable_by,approver_roles,filer_role,retention_years,related_forms. .github/pdf/build-manifest.pyruns in the GitHub Actions PDF build and emitsdist/forms-manifest.json— a single, versioned, machine-readable manifest of every form and its permission rules.guildcommerce-core-plugin / formssub-module fetches the manifest from a release artifact (or a checked-in copy) and uses it to gate render in the member hub.- Visual audience tier is rendered both in the markdown/PDF (
<span class="form-audience {public|internal|restricted}">) and in the hub UI as a colored chip mapped to the same brand palette (Honey · Mint · Lavender). - Form O is the canonical role × form matrix. Frontmatter values must agree with Form O. Drift is detected in CI by a future check (
check-form-o-consistency.py, deferred).
Schema (v1.0)¶
---
form_letter: A
title: "Ecosystem Access & Participation Form"
category: Onboarding # Onboarding | Registries | Communications | Operations | Governance | Capital & Cross-Entity
audience: public # public | internal | restricted
status: final # final | superseded
visible_to: [guest, applicant, member, ...]
fillable_by: [guest, applicant]
approver_roles: [officer, sysadmin]
filer_role: sysadmin
retention_years: 7
related_forms: [E, O]
---
Audience tiers¶
| Tier | Visual | Who sees it | Examples |
|---|---|---|---|
| Public | Honey badge | Participants and external counterparties per role | Forms A, B, D, E, I, P, Q, R, S, T, V |
| Internal | Mint badge | Officers, SysAdmin, operational leads | Forms C, F, G, H, J, L, M, N, O, U |
| Restricted | Lavender badge | Officers and SysAdmin only | Form K (emergency credentials) |
Role taxonomy (frontmatter values)¶
Sourced from Section 28A and Form O. Stable slugs that map 1:1 to BuddyBoss member types:
guest, applicant, member, ganjier, navigator, operator, voyage-captain, voyage-crew, coop-participant, contractor, employee, sponsor, board, officer, sysadmin, counsel.
Plugin integration contract¶
The guildcommerce-core-plugin / forms sub-module:
- Reads
forms-manifest.jsonat activation and on a daily cron. - Registers a custom post type
gc_formand writes one post per manifest entry. - On every render of a
gc_formpost, callsbp_get_member_type( $user_id )and filters bypermissions.visible_to. - Exposes a shortcode
[guild-form letter="A"]and a member-profileMy Formstab. - Exposes a REST endpoint
GET /wp-json/guildcommerce/v1/formsthat returns the same manifest filtered to the current user's permissions. - PDFs are served via signed URL; the markdown is rendered inline by the
guildcommerce-themetemplate.
Consequences¶
- Single source of truth. Form O is canonical; frontmatter is the executable copy; manifest is the integration surface.
- No third-party plugin coupling. The plugin lives inside
guildcommerce-core-plugin, deployed via the existingphase3-deploy.shflow. - Migration cost to BuddyBoss is small. The manifest is platform-neutral; the plugin's
bp_get_member_type()call works on both BuddyPress and BuddyBoss Platform (BB ships a compatibility layer). - CI must keep frontmatter and Form O in sync. A future ADR-update or a
check-form-o-consistency.pyscript will catch drift. - GitHub repo posture is unchanged. The blueprint repo can stay public (transparency for trade-association mission) because the hub itself enforces gates. Restricted form (K) markdown is already structural — it does not contain credentials. Credentials live only in the live record stored in
Privileged-Confidential/.
Alternatives considered¶
- Members + Restrict Content Pro. Works, but adds third-party plugin licensing and admin surface area. Rejected.
- Two-repo split (public + private). Adds maintenance overhead for marginal security benefit, since restricted content (credentials) was never going to live in markdown anyway. Rejected.
- Native BuddyPress xprofile-only gating. Insufficient — BuddyPress does not have first-class member types. Drove ADR-005 (BuddyBoss migration).
Follow-ups¶
- ADR-005 — Community Platform Selection (BuddyBoss Platform).
- Implement
guildcommerce-core-plugin / formssub-module (separate sprint). - Add CI consistency check (
check-form-o-consistency.py). - Phase 2: e-signature + completion tracking on participant-facing forms.
- Phase 3: cross-entity workflow on Form V (dual-officer signature gate).